// ObjectWindows - (C) Copyright 1992 by Borland International

/* --------------------------------------------------------
  WINDOBJ.CPP
  Defines type TWindowsObject.  This defines the basic behavior
  of all Windows objects.
  -------------------------------------------------------- */

#include <string.h>
#include <alloc.h>
#include "applicat.h"
#include "windobj.h"
#include "appdict.h"

extern WNDPROC MakeObjectInstance(PTWindowsObject);
extern void FreeObjectInstance(WNDPROC);
extern PTWindowsObject GetObjectPtr(HWND);

/* Constructor for a TWindowsObject.  If a parent window is passed, adds
  the TWindowsObject to its parent's list of children.  Makes an
  instance thunk to be used in associating an MS-Windows interface
  element to the TWindowsObject. */
TWindowsObject::TWindowsObject(PTWindowsObject AParent, PTModule AModule)
{
  Status = 0;
// If this is a "DLL alias", a derived class's constructor must set HWindow
  HWindow = 0;
  Title = NULL;
  CreateOrder = 0;
  ChildList = NULL;
  Flags = 0x0;
  TransferBuffer = NULL;
  EnableAutoCreate();
  Instance = MakeObjectInstance(this);
  DefaultProc = NULL;

  Parent = AParent;
  if ( Parent )
    Parent->AddChild(this);
  else
    SiblingList = NULL;

  Application = GetApplicationObject();
  if ( AModule )
    Module = AModule;
  else
  /* by default Module = Application for all applications and for DLLs
     dynamically linked with the OWL DLL. */
    if ( Application )
      Module = (PTModule)Application;
    else  // In a DLL statically linked with OWL and AModule == 0
      Status = EM_INVALIDMODULE;
}

static void FreeChild(void *P, void *)
{
  ((PTWindowsObject)P)->ShutDownWindow();
}

/* Destructor for a TWindowsObject.  Disposes of each window in its
  ChildList and removes itself from a non-NULL parent's list of
  children. Destroys a still-associated MS-Windows interface element and
  frees the instance thunk used for association of an MS-Windows element
  to the TWindowsObject. */
TWindowsObject::~TWindowsObject()
{
  if ( !IsFlagSet(WB_ALIAS) ) //if alias only delete, don't destroy.
    Destroy();
  ForEach(FreeChild, 0);
  if (Parent)
    Parent->RemoveChild(this);
  if ( GetApplication() && this == GetApplication()->MainWindow )
    GetApplication()->MainWindow = NULL;
  if ( HIWORD(Title) )
    farfree(Title);
  FreeObjectInstance(Instance);
}

void TWindowsObject::AssignCreateOrder()
{
  HWND CurWindow;
  PTWindowsObject Wnd;
  int I;

  Wnd = (PTWindowsObject)GetClient();
  if ( !Wnd )
    CurWindow = HWindow;
  else
    CurWindow = Wnd->HWindow;
  if ( CurWindow )
  {
    CurWindow = GetWindow(CurWindow, GW_CHILD);
    if ( CurWindow )
    {
      CurWindow = GetWindow(CurWindow, GW_HWNDLAST);
      I = 1;
      while ( CurWindow )
      {
        Wnd = (PTWindowsObject)GetObjectPtr(CurWindow);
        if ( Wnd )
          Wnd->CreateOrder = I++;
        CurWindow = GetWindow(CurWindow, GW_HWNDPREV);
      }
    }
  }
}

/* The private field CreateOrder is used to ensure the create order is
  consistent through read and write of the object.  If the object is
  written, write will fill in this value.  CreateOrder ranges in value
  from 1 to N where N is the number of objects with values.  All other
  objects will have a CreateOrder of Zero, which implies the object
  will be created after the last object with a create order
*/
BOOL TWindowsObject::OrderIsI(void *P, void *I)
{
  return ((PTWindowsObject)P)->CreateOrder == *(WORD *)I;
}

/*  Returns TRUE if the child was supposed to be created but couldn't be. */
static BOOL CantCreateChild(PTWindowsObject P)
{
  BOOL Success;

  Success = (!P->IsFlagSet(WB_AUTOCREATE)) || P->Create();
  if ( P->HWindow && IsIconic(P->HWindow) )
  {
    int TextLen = GetWindowTextLength(P->HWindow);
    Pchar Text = new char[TextLen + 1];
    GetWindowText(P->HWindow, Text, TextLen + 1);
    SetWindowText(P->HWindow, Text);
    delete Text;
  }
  return !Success;
}

BOOL TWindowsObject::CreateZeroChild(void *P, void *)
{
  return ((PTWindowsObject)P)->CreateOrder == 0 &&
         CantCreateChild((PTWindowsObject)P);
}

/* Create the children of the object.  Returns true if all the windows
   were successfully created. */
BOOL TWindowsObject::CreateChildren()
{
  int I = 1;
  PTWindowsObject P;
  BOOL Failure = FALSE;

  do {
    P = FirstThat(&TWindowsObject::OrderIsI, &I);
    if ( P )
      Failure = CantCreateChild(P);
    ++I;
  } while( !Failure && P != (PTWindowsObject)NULL );
  return !Failure && (FirstThat(&TWindowsObject::CreateZeroChild, NULL) == NULL);
}

/* Transfer window 'data' to/from the passed data buffer.  Used to
  initialize dialogs and get data out of them.  The TransferFlag passed
  specifies whether data is to be read from or written to the passed
  buffer, or whether the data element size is simply to be returned. The
  return value is the size (in bytes) of the transfer data.  This method
  simply returns zero and is redefined in TControl descendant classes. */
WORD TWindowsObject::Transfer(Pvoid, WORD)
{
    return 0;
}

/* Sets flag which indicates that the TWindowsObject has requested
  "keyboard handling" (translation of keyboard input into control
  selections). */
void TWindowsObject::EnableKBHandler()
{
  SetFlags(WB_KBHANDLER, TRUE);
}

/* Sets flag which indicates that the TWindowsObject should be
  created if a create is sent while in the parent's child list. */
void TWindowsObject::EnableAutoCreate()
{
  SetFlags(WB_AUTOCREATE, TRUE);
}

/* Sets flag which indicates that the TWindowsObject can/will
  tranfer data via the transfer mechanism. */
void TWindowsObject::EnableTransfer()
{
  SetFlags(WB_TRANSFER, TRUE);
}

/* Sets flag which indicates that the TWindowsObject should not be
  created if a create is sent while in the parent's child list. */
void TWindowsObject::DisableAutoCreate()
{
  SetFlags(WB_AUTOCREATE, FALSE);
}

/* Sets flag which indicates that the TWindowsObject cannot/
  will not tranfer data via the transfer mechanism. */
void TWindowsObject::DisableTransfer()
{
  SetFlags(WB_TRANSFER, FALSE);
}

/* Sets flag(s) for the TWindowsObject, which are stored in its Flags
   data field.  The mask of the flag(s) to be set
  (WB_KBHANDLER, etc.), and an OnOff "flag" are passed --
  On = True, Off = False. */
void TWindowsObject::SetFlags(WORD Mask, BOOL OnOff)
{
  if ( OnOff )
    Flags |= Mask;
  else
    Flags &= ~Mask;
}

/* Adds the passed pointer to a child window to the linked list
  of sibling windows which ChildList points to. */
void TWindowsObject::AddChild(PTWindowsObject AChild)
{
  if ( AChild )
    if ( !ChildList )
    {
      ChildList = AChild;
      AChild->SiblingList = AChild;
    }
    else
    {
      AChild->SiblingList = ChildList->SiblingList;
      ChildList->SiblingList = AChild;
      ChildList = AChild;
    }
}

/* Returns a pointer to the TWindowsObject's previous sibling (the
   window previous to the TWindowsObject in its parent's child window
   list). Returns the sibling which points to the TWindowsObject. If
   the TWindowsObject was the first child added to the list, returns
   a pointer to the last child added.*/
PTWindowsObject TWindowsObject::Previous()
{
  PTWindowsObject CurrentIndex;

  if ( !SiblingList )
   return NULL;
  else
  {
    CurrentIndex = this;
    while ( CurrentIndex->Next() != this)
      CurrentIndex = CurrentIndex->Next();
    return CurrentIndex;
  }
}

/* Removes the passed pointer to a child window from the linked list of
  sibling windows which the TWindowsObject's ChildList points to. */
void TWindowsObject::RemoveChild(PTWindowsObject AChild)
{
  PTWindowsObject LastChild , NextChild;

  if ( ChildList )
  {
    LastChild = ChildList;
    NextChild = LastChild;
    while ( (NextChild->SiblingList != LastChild) &&
            (NextChild->SiblingList != AChild) )
      NextChild = NextChild->SiblingList;
    if ( NextChild->SiblingList == AChild )
      if ( NextChild->SiblingList == NextChild )
        ChildList = NULL;
      else
      {
        if ( NextChild->SiblingList == ChildList )
          ChildList = NextChild;
        NextChild->SiblingList = NextChild->SiblingList->SiblingList;
      }
  }
}

/* Reparents "this" to a new parent */
void TWindowsObject::SetParent(PTWindowsObject NewParent)
{
  if ( Parent )
    Parent->RemoveChild(this);
  SiblingList = (PTWindowsObject)NULL;
  ::SetParent(HWindow, NewParent->HWindow);  // tell windows about it
  Parent = NewParent;
  if ( Parent )
    Parent->AddChild(this);
}

/* Returns a pointer to the first TWindowsObject in the
   ChildList that meets some specified criteria.If no child in the
   list meets the criteria, NULL is returned. The Test
   parameter which defines the criteria, is a pointer to a function
   that takes two pointers to void. The first void* will be used as
   the pointer to the child window and the second as a pointer to
   the Test function's additional parameters.  The Test function must
   return a Boolean value indicating whether the child passed meets
   the criteria.  */
PTWindowsObject TWindowsObject::FirstThat(TCondFunc Test,
                                          void *PParamList)
{
  PTWindowsObject NextChild;
  PTWindowsObject CurChild;
  BOOL Found = FALSE;

  if ( ChildList )
  {
    NextChild = ChildList->Next();
    do {
      CurChild = NextChild;
      NextChild = NextChild->Next();
      /* Test CurChild for ok */
      if ( (*Test)( CurChild, PParamList) )
        Found = TRUE;
    }
    while ( !Found && (CurChild != ChildList) && ChildList);

    if ( Found )
      return CurChild;
  }
  return NULL;

}

/* Iterates over each child window in the TWindowsObject's ChildList,
   calling the procedure whose pointer is passed as the Action to be
   performed for each child.  A pointer to a child is passed as the
   first parameter to the iteration procedure. */
void TWindowsObject::ForEach(TActionFunc Action, void *PParamList)
{
  PTWindowsObject CurChild;
  PTWindowsObject NextChild; // allows ForEach to delete children

  if ( ChildList )
  {
    NextChild = ChildList->Next();
    do
    {
      CurChild = NextChild;
      NextChild = NextChild->Next();
      ( *Action )( CurChild, PParamList );
    }
    while ( (CurChild != ChildList) && (ChildList) );
  }
}

/* Returns a pointer to the first TWindowsObject in the
   ChildList that meets some specified criteria.If no child in the
   list meets the criteria, NULL is returned. The Test
   parameter which defines the criteria, is a pointer to a member
   function (this is how it's different from FirstThat above).
   that takes two pointers to void. The first void* will be used as
   the pointer to the child window and the second as a pointer to
   the Test function's additional parameters.  The Test function must
   return a Boolean value indicating whether the child passed meets
   the criteria.  */
PTWindowsObject TWindowsObject::FirstThat(TCondMemFunc Test,
                                          void *PParamList)
{
  PTWindowsObject NextChild;
  PTWindowsObject CurChild;
  BOOL Found = FALSE;

  if ( ChildList )
  {
    NextChild = ChildList->Next();
    do {
      CurChild = NextChild;
      NextChild = NextChild->Next();
      /* Test CurChild for ok */
      if ( (this->*Test)( CurChild, PParamList) )
        Found = TRUE;
    }
    while ( !Found && (CurChild != ChildList) && ChildList);

    if ( Found )
      return CurChild;
  }
  return NULL;

}

/* Iterates over each child window in the TWindowsObject's ChildList,
   calling the member function (unlike ForEach above which takes pointer
   to non-member function) whose pointer is passed as the Action to
   be performed for each child.  A pointer to a child is passed as the
   first parameter to the iteration procedure. */
void TWindowsObject::ForEach(TActionMemFunc Action, void *PParamList)
{
  PTWindowsObject CurChild;
  PTWindowsObject NextChild; // allows ForEach to delete children

  if ( ChildList )
  {
    NextChild = ChildList->Next();
    do
    {
      CurChild = NextChild;
      NextChild = NextChild->Next();
      ( this->*Action )( CurChild, PParamList );
    }
    while ( (CurChild != ChildList) && (ChildList) );
  }
}

static int Position;

static BOOL IsItThisChild1(void *P, void *AChild)
{
  ++Position;
  return ((PTWindowsObject)P == (PTWindowsObject)AChild);
}

/* Returns the position at which the passed child window appears
   in the TWindowsObject's ChildList. If the child does not appear in
   the list, -1 is returned. Zero'th position is ChildList->Next */
int TWindowsObject::IndexOf(PTWindowsObject AChild)
{
  Position = -1;
  if ( FirstThat(IsItThisChild1, AChild) )
    return Position;
  else
    return -1;
}

/* Returns the child at the passed position in the TWindowsObject's
   ChildList.  The ChildList is circularly-referent so that passing a
   position larger than the number of children will cause the traversal
   of the list to wrap. Zero'th position is ChildList->Next */
PTWindowsObject TWindowsObject::At(int APosition)
{
  PTWindowsObject CurrentChild;

  if ( APosition == -1 )
    return NULL;
  CurrentChild = ChildList;
  if ( CurrentChild )
  {
    CurrentChild = ChildList->Next();
    while (APosition > 0)
    {
      CurrentChild = CurrentChild->Next();
      --APosition;
    }
  }
  return CurrentChild;
}

static BOOL IsItThisChild2(void *P, void *Id)
{
  return ( ((PTWindowsObject)P)->GetId() == *(int *)Id);
}

/* Returns a pointer to the window in the ChildList with the passed Id.
   If no child in the list has the passed Id, NULL is returned.*/
PTWindowsObject TWindowsObject::ChildWithId(int Id)
{
  return FirstThat(IsItThisChild2, &Id);
}

/* Specifies default processing for an incoming message. Invokes default
   processing, defined by MS-Windows. Calls the default window procedure
   if this is a window; sets Msg.Result to 0 if it's a dialog. Both cases
   must be handled here instead of in TWindow or TDialog because DefWndProc
   can be called while an object is being destructed, making it effectively
   non-virtual */
void TWindowsObject::DefWndProc(TMessage& Msg)
{
  if ( IsFlagSet(WB_MDIFRAME) )
  {
    HWND ClientHWnd;

    if ( GetClient() )
      ClientHWnd = GetClient()->HWindow;
    else
      ClientHWnd = 0;
    Msg.Result = DefFrameProc(HWindow, ClientHWnd, Msg.Message ,
                              Msg.WParam, Msg.LParam);
  }
  else
    if ( DefaultProc ) // this is a Window
      Msg.Result =
#ifdef STRICT
      CallWindowProc(DefaultProc, HWindow, Msg.Message,
				  Msg.WParam, Msg.LParam);
#else
      CallWindowProc((FARPROC)DefaultProc, HWindow, Msg.Message,
				  Msg.WParam, Msg.LParam);
#endif
    else // this is a dialog
      Msg.Result = 0;
}

// --------------------------------------------------------
// DispatchAMessage gets called by StdWndProc and
// Dispatches the Windows messages to the proper WM routine
// Calls _DDVTdispatch to return a pointer to the virtual function
// to call. If a virtual function is not defined calls the
// DefProc argument instead.
// ---------------------------------------------------------

typedef void (TWindowsObject::* MRMFP)(TMessage&);
typedef void (* MRFP)(TMessage&);

#if defined(_CLASSDLL) || defined(__DLL__)
#define getVptr(thisPtr)	(*(void far **)(thisPtr))
#else
#define getVptr(thisPtr)	(*(void near **)(thisPtr))
#endif

MRFP far * _DDVTdispatchNULL(void near *, int);
MRFP far * _DDVTdispatchNULL(void far  *, int);

typedef void FAR PASCAL (* DISPATCHHOOK)(HANDLE hWnd, WORD wMessage,
                                             WORD wParam, LONG lParam,
                                             void far *Handler,
                                             void far *ObjectPtr);
DISPATCHHOOK __OWL_DISPATCH_HOOK__ = NULL;

void TWindowsObject::DispatchAMessage(WORD AMsg, TMessage& Msg,
                                      MRMFP DefProc)
{
    MRFP FFP;
    MRMFP MFP;

    union
    {
        MRMFP mfp;
        MRFP fp;
    }   Temp;

    Temp.mfp = NULL;

    HWND TheHWindow = HWindow;

    BeforeDispatchHandler();

    FFP = *_DDVTdispatchNULL(getVptr(this), (int)AMsg);

    if (FFP)                        // found a DDVT function
    {
        Temp.fp = FFP;              // convert DDVT function pointer
        MFP = Temp.mfp;             //   to member function pointer
    }
    else                            // no DDVT function, use DefProc
        MFP = DefProc;

    if (__OWL_DISPATCH_HOOK__)
    {
        if (!FFP)                   // if don't have normal function ptr
        {                           //   convert from member function ptr
            Temp.mfp = MFP;         //   to normal function pointer
            FFP = Temp.fp;
        }

        (*__OWL_DISPATCH_HOOK__)(TheHWindow, Msg.Message, Msg.WParam,
                    Msg.LParam, (void far *)FFP, this);
    }

    (this->*MFP)(Msg);           // invoke handler

    if ( this == GetObjectPtr(TheHWindow) ) // this hasn't been deleted
      AfterDispatchHandler();
}

/* Responds to an incoming WM_COMMAND message.  If a child window had
  the focus when the message was sent, the message is sent to the child
  window.  If the message cannot be given to a child window, it is
  given to "this"  */
void TWindowsObject::WMCommand(TMessage& Msg)
{
  HWND CurrentWindow, Control;
  PTWindowsObject Child;

  if ( IsFlagSet(WB_KBHANDLER) && (Msg.LParam == 0L) )
  {
    Control = GetDlgItem(HWindow, Msg.WParam);
    if ( Control && ( SendMessage(Control, WM_GETDLGCODE, 0, 0) &
         (DLGC_DEFPUSHBUTTON | DLGC_UNDEFPUSHBUTTON) ) )
    {
      Msg.LP.Lo = (WORD)Control;
      Msg.LP.Hi = BN_CLICKED;
    }
  }
  if ( !IsFlagSet(WB_ALIAS) &&
     (LOWORD(Msg.LParam) == 0) )        // it's a command message and...
  {
    if ( Msg.WParam < CM_COUNT )        // ... we can route it
    {
      // Find the object closest to the focus window
      CurrentWindow = GetFocus(); // window with focus when command was sent
      Child = (PTWindowsObject)GetObjectPtr(CurrentWindow);
      while ( !Child && CurrentWindow && CurrentWindow != HWindow )
      {
        CurrentWindow = GetParent(CurrentWindow);
        Child = (PTWindowsObject)GetObjectPtr(CurrentWindow);
      }

 // If the object is found, route to the object, else handle it yourself
      if ( !Child )
        Child = this;
      Child->DispatchAMessage(CM_FIRST + Msg.WParam, Msg, &TWindowsObject::DefCommandProc);
    }
    else
      DefWndProc(Msg);
  }
  else
  {
    // Find the child that generated the notification
    Child = (PTWindowsObject)GetObjectPtr(GetDlgItem(HWindow, Msg.WParam));

    /* If the child is found, give the notification to the child,
      else give it to "this" as an "id" notification. */
    if ( Child && HIWORD(Msg.LParam) < NF_COUNT )
      Child->DispatchAMessage(NF_FIRST + HIWORD(Msg.LParam),
               Msg, &TWindowsObject::DefNotificationProc);
    else
      if ( !IsFlagSet(WB_ALIAS) )
      {
        if ( Msg.WParam < ID_COUNT )
          DispatchAMessage(ID_FIRST + Msg.WParam,
                 Msg, &TWindowsObject::DefChildProc);
        else
          DefChildProc(Msg);
      }
      else  // if I'm an alias don't route ID messages to me
        DefWndProc(Msg);
  }
}

/* Function called when a window must redraw one of its "owner-draw"
   controls. Having the control redraw itself is usually preferable.
   Also called when the window's "owner draw" menu must be redrawn */
void TWindowsObject::DrawItem(DRAWITEMSTRUCT far &)
{
}

/* Dispatches WM_DRAWITEM messages (for owner draw controls) to the
   appropriate control's appropriate member function */
void TWindowsObject::WMDrawItem(TMessage& Msg)
{
  PTWindowsObject Child = (PTWindowsObject)GetObjectPtr(
                          ((LPDRAWITEMSTRUCT)(Msg.LParam))->hwndItem);
  if ( Child )
    Child->DispatchAMessage(WM_FIRST + WM_DRAWITEM, Msg,
                                    &TWindowsObject::DefWndProc);
  else  // no child object (includes case where item is a menu)
  {
    if ( IsFlagSet(WB_ALIAS) )
      DefWndProc(Msg);
    else
      DrawItem(*((LPDRAWITEMSTRUCT)(Msg.LParam)));
  }
}

/* Dispatches scroll messages as if they where WMCommand messages,
   that is by routing them to the scroll bar control as a notification
   and to "this" as an "id" notification.  */
void TWindowsObject::DispatchScroll(TMessage& Msg)
{
    PTWindowsObject Child;
    WORD ChildId;

  if ( HIWORD(Msg.LParam) )
  {
    Child = (PTWindowsObject)GetObjectPtr((HWND)HIWORD(Msg.LParam));
    if ( Child )
      Child->DispatchAMessage(NF_FIRST + Msg.WParam, Msg, &TWindowsObject::DefNotificationProc);
    else
    {
      ChildId = GetWindowWord((HWND)HIWORD(Msg.LParam), GWW_ID);
      if ( ChildId < ID_COUNT )
        DispatchAMessage(ID_FIRST + ChildId, Msg, &TWindowsObject::DefChildProc);
      else
        DefChildProc(Msg);
    }
  }
  else
    DefWndProc(Msg);
}

/* Responds to an incoming WM_VSCROLL message by calling DispatchScroll. If
   message is not handled, calls DefWndProc. */
void TWindowsObject::WMVScroll(TMessage& Msg)
{
  if ( !(GetWindowLong(HWindow, GWL_STYLE) & WS_VSCROLL) )
    DispatchScroll(Msg);
  else
    DefWndProc(Msg);
}

/* Responds to an incoming WM_HSCROLL message by calling DispatchScroll.
   If message is not handled, calls DefWndProc. */
void TWindowsObject::WMHScroll(TMessage& Msg)
{
  if ( !(GetWindowLong(HWindow, GWL_STYLE) & WS_HSCROLL) )
    DispatchScroll(Msg);
  else
    DefWndProc(Msg);
}

/* Performs default processing for a command message (menu selection or
  accelerator). If the original message receiver was this object, give
  the message to DefWndProc, else if the object has a parent, give the
  message to the parent, else give the message to the original receiver. */
void TWindowsObject::DefCommandProc(TMessage& Msg)
{
  PTWindowsObject Target;

  if ( Msg.Receiver == HWindow )
    Target = NULL;
  else
    if ( Parent )
      Target = Parent;
    else
      Target = (PTWindowsObject)GetObjectPtr(Msg.Receiver);
  if ( Target == NULL )
    DefWndProc(Msg);
  else
    Target->DispatchAMessage(CM_FIRST + Msg.WParam, Msg, &TWindowsObject::DefCommandProc);
}

/* Performs default processing for an incoming notification message from
  a child of the TWindowsObject. Nothing can be done by default of a
  child notification (or "id" message). The user can override this
  function if it is more convenient to handle "id" messages in a case
  statement. */
void TWindowsObject::DefChildProc(TMessage& Msg)
{
  DefWndProc(Msg);
}

/* Performs default processing for a notification message generated by
   the TWindowsObject. (The TWindowsObject has the option to perform
   processing in response to its own notification messages. )  It passes
   the message to the parent as an "id" message.  It is assumed that the
   object giving this message to this object is the parent of this
   object.  This is done in WMCommand, WMHScroll, or WMVScroll of the
   parent. Notifications are translated into "id" messages so that the
   parent does not confuse child notification with its own
   notifications. If its an WMHScroll or WMVScroll the ID is looked up
   explicitly. */
void TWindowsObject::DefNotificationProc(TMessage& Msg)
{
  if ( Parent )
  {
    if ( Parent->IsFlagSet(WB_ALIAS) )
      DefWndProc(Msg);
    else
    {
      if (Msg.Message == WM_COMMAND )
        Parent->DispatchAMessage(ID_FIRST + Msg.WParam, Msg, &TWindowsObject::DefChildProc);
      else
        Parent->DispatchAMessage(
             ID_FIRST + GetWindowWord(HWindow, GWW_ID), Msg, &TWindowsObject::DefChildProc);
    }
  }
}


static void DoEnableAutoCreate(void *P, void *)
{
  if ( ((PTWindowsObject)P)->HWindow )
    ((PTWindowsObject)P)->EnableAutoCreate();
}

/* Destroys an MS-Windows element associated with the TWindowsObject
   after setting the WB_AUTOCREATE flag to ON for each of the windows
   in the TWindowsObject's ChildList. */
void TWindowsObject::Destroy()
{
  if ( HWindow )
  {
    ForEach(DoEnableAutoCreate, 0);
    if ( IsFlagSet(WB_MDICHILD) && (Parent->GetClient() != NULL) )
      SendMessage(((PTWindowsObject)(Parent->GetClient()))->HWindow,
		WM_MDIDESTROY, (WORD)HWindow, 0);
    else
      DestroyWindow(HWindow);
  }
}

/* Initializes the passed parameter with the registration attributes for
  the TWindowsObject. This function serves as a placeholder for
  descendant classes to redefine to specify registration
  attributes for the MS-Windows class of a window object. */
void TWindowsObject::GetWindowClass(WNDCLASS&)
{
}

/* Performs setup following creation of an associated MS-Windows window.
  Iterates through the TWindowsObject's ChildList, attempting to create
  an associated MS-Windows interface element for each child window
  object in the list. (A child's Create method is not called if its
  WB_AUTOCREATE flag is not set). Calls TransferData to transfer data
  for its children for whom data transfer is enabled.  Can be redefined
  in derived classes to perform additional special initialization.
*/
void TWindowsObject::SetupWindow()
{
  if ( ! CreateChildren() )
    Status = EM_INVALIDCHILD;
  else
    TransferData(TF_SETDATA);
}

static void _FAR * DataPtr;

static void TransferDataChild(void *AChild, void *Direction)
{
    if ( ((PTWindowsObject)AChild)->IsFlagSet(WB_TRANSFER) )
    {
      *(LPSTR _FAR *)&DataPtr += ((PTWindowsObject)AChild)->Transfer(
                     DataPtr, *(WORD *)Direction);
    }
}

/* Transfers data between the TWindowsObject's data buffer and the child
   windows in its ChildList. (Data is not transfered between any child
   windows whose WB_TRANSFER flag is not set). */
void TWindowsObject::TransferData(WORD Direction)
{
  if ( TransferBuffer )
  {
    DataPtr = TransferBuffer;
    ForEach(TransferDataChild, &Direction);
  }
}

/* Registers the TWindowsObject's MS-Windows, if not already registered. */
BOOL TWindowsObject::Register()
{
  WNDCLASS WindowClass;

  if ( !GetClassInfo( 0, GetClassName(), &WindowClass) &&
       !GetClassInfo(GetModule()->hInstance, GetClassName(),
        &WindowClass) )
  {
    GetWindowClass(WindowClass);
    return RegisterClass(&WindowClass);
  }
    return TRUE;
}

/* Displays the TWindowsObject, after checking that it has a valid
   (non-NULL) handle. */
void TWindowsObject::Show(int ShowCmd)
{
  if ( HWindow )
    ShowWindow(HWindow, ShowCmd);
}

/* Sets the Title and caption of the TWindowsObject. */
void TWindowsObject::SetCaption(LPSTR ATitle)
{
    if (Title != ATitle)
    {
        if ( HIWORD(Title) )
            farfree(Title);

        Title = _fstrdup(ATitle ? ATitle : "");
    }
    if ( HWindow )
        SetWindowText(HWindow, Title);
}

static BOOL CannotCloseChild(void *P, void *)
{
  return ( ((PTWindowsObject)P)->HWindow && !( ((PTWindowsObject)P)->CanClose()) );
}


/* Returns a BOOL indicating whether or not it is Ok to close
  the TWindowsObject.  Iterates through the TWindowsObject's
  ChildList, calling the CanClose method of each.  Returns False if
  any of the child windows return False. */
BOOL TWindowsObject::CanClose()
{
  return (FirstThat(CannotCloseChild, 0) == NULL);
}

/* Destroys the associated MS-Windows interface element and frees "this"
  after determining that it is Ok to do so. If the TWindowsObject is the
  main window of the application, calls the CanClose method of the
  application, else calls this->CanClose, before calling ShutDownWindow. */
void TWindowsObject::CloseWindow()
{
  BOOL WillClose;

  if ( GetApplication() && this == GetApplication()->MainWindow )
    WillClose = GetApplication()->CanClose();
  else
    WillClose = CanClose();
  if ( WillClose )
    ShutDownWindow();
}

/* Destroys the associated MS-Windows interface element and frees "this"
   without calling CanClose. */
void TWindowsObject::ShutDownWindow()
{
  Destroy();
  delete this;
}

/* The default response to a WMClose message is to send a CloseWindow
  message.  CloseWindow sends a CanClose to determine if the window
  can be closed. */
void TWindowsObject::WMClose(TMessage& Msg)
{
  if ( IsFlagSet(WB_ALIAS) )
    DefWndProc(Msg);
  else
    CloseWindow();
}

/* Responds to an incoming WM_DESTROY message.  If the TWindowsObject
   is the application's main window posts a 'quit' message to end
   the application. */
void TWindowsObject::WMDestroy(TMessage& Msg)
{
  if ( !IsFlagSet(WB_ALIAS) )
  {
      PTApplication app = GetApplication();
      if (app && (this == app->MainWindow))
          PostQuitMessage(app->Status);
  }
  DefWndProc(Msg);
}

/* Responds to an incoming WM_NCDESTROY message, the last message
   sent to an MS-Windows interface element.  Sets the HWindow data
   member of the TWindowsObject to zero to indicate that an interface
   element is no longer associated with the object.  */
void TWindowsObject::WMNCDestroy(TMessage& Msg)
{
  DefWndProc(Msg);
  HWindow = 0;
  if ( GetApplication() && this == GetApplication()->KBHandlerWnd )
    GetApplication()->SetKBHandler(NULL);
  if ( IsFlagSet(WB_ALIAS) )
    delete this;
}

/* Responds to an incoming WM_ACTIVATE message by calling
   DefWndProc followed by ActivationResponse. DefWndProc must be
   called first since TWindow::ActivationResponse does a SetFocus
   which would otherwise be overridden when DefWindowProc does its
   SetFocus. */
void TWindowsObject::WMActivate(TMessage& Msg)
{
  DefWndProc(Msg);
  if ( !IsFlagSet(WB_ALIAS) )
    ActivationResponse(Msg.WParam, Msg.LP.Hi);
}

/* Responds to an incoming WM_ACTIVATE or WM_MDIACTIVATE message. If the
   TWindowsObject is being activated and if it has requested
   keyboard handling for its messages, enables the "keyboard handler"
   by calling the SetKBHandler function of the application. Otherwise
   disables the "keyboard handler". */
void TWindowsObject::ActivationResponse(WORD Activated, BOOL /*IsIconified*/ )
{
  if ( GetApplication() )
  {
    if ( Activated && IsFlagSet(WB_KBHANDLER) )
      GetApplication()->SetKBHandler(this);
    else
      GetApplication()->SetKBHandler(NULL);
  }
}

/* Respond to Windows attempt to close close down. */
void TWindowsObject::WMQueryEndSession(TMessage& Msg)
{
  if ( IsFlagSet(WB_ALIAS) )
    DefWndProc(Msg);
  else
  {
    if ( GetApplication() && this == GetApplication()->MainWindow )
      Msg.Result = (int)GetApplication()->CanClose();
    else
      Msg.Result = (int)CanClose();
  }
}

/* If the window receives an Exit menu choice, it will attempt
  to close down the window. */
void TWindowsObject::CMExit(TMessage& Msg)
{
  if ( GetApplication() && this == GetApplication()->MainWindow )
    CloseWindow();
  else
    DefCommandProc(Msg);
}

/* Returns a NULL pointer to indicate that the TWindowsObject is not
   a TMDIFrame.  Is redefined for TMDIFrame to return a pointer to
   their TMDIClient window. */
TMDIClient *TWindowsObject::GetClient()
{
  return NULL;
}
