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

/* --------------------------------------------------------
  WINDOW.CPP
  Defines type TWindow.  This defines the basic behavior
  of all windows.
  -------------------------------------------------------- */

#include <string.h>
#include <alloc.h>
#include "applicat.h"
#include "window.h"

__link(RegScroller)

extern PTWindowsObject CreationWindow;
extern PTWindowsObject DlgCreationWindow;

extern PTWindowsObject GetObjectPtr(HWND);

#ifdef WIN31
extern long FAR PASCAL _export
     InitWndProc(HWND, UINT, WPARAM, LPARAM);
#endif

#ifdef WIN30
extern LONG FAR PASCAL _export
     InitWndProc(HWND_30, WORD, WORD, LONG);
#endif

/* Constructor for a TWindow.  Initializes its data fields using passed
  parameters and default values. */
TWindow::TWindow(PTWindowsObject AParent, LPSTR ATitle, PTModule AModule)
        : TWindowsObject(AParent, AModule)
{
  Title = _fstrdup(ATitle ? ATitle : "");
  DefaultProc = (WNDPROC)DefWindowProc;
  if ( !AParent )
    Attr.Style = WS_OVERLAPPEDWINDOW;
  else
    if ( AParent->IsFlagSet(WB_MDIFRAME) )
    {
      /* Set WB_MDICHILD by default, change this if you want a
         non-mdi child as a child of your TMDIFrame window. */
      SetFlags(WB_MDICHILD, TRUE);
      Attr.Style = WS_CLIPSIBLINGS;
    }
    else
      Attr.Style = WS_VISIBLE;
  Attr.ExStyle = 0;
  Attr.X = CW_USEDEFAULT;
  Attr.Y = 0;
  Attr.W = CW_USEDEFAULT;
  Attr.H = 0;
  Attr.Param = NULL;
  Attr.Menu = NULL;
  Attr.Id = 0;
  Scroller = NULL;
  FocusChildHandle = 0;
}

/* Constructor for a TWindow which is being used in a DLL as an alias
   for a non-OWL window */
TWindow::TWindow(HWND AnHWindow, PTModule AModule) :
         TWindowsObject((PTWindowsObject)NULL, AModule)
{
  HWND HParent;
  RECT WndRect;

  HWindow = AnHWindow;

  int TitleLength = GetWindowTextLength(AnHWindow);
  if (TitleLength > -1)
  {
      Title = (LPSTR)farmalloc((unsigned long)TitleLength + 1);
      Title[0] = '\0'; Title[TitleLength] = '\0';
      GetWindowText(AnHWindow, Title, TitleLength+1);
  }
  else
      Title = _fstrdup("");

  DefaultProc = (WNDPROC) SetWindowLong(AnHWindow, GWL_WNDPROC,
                                          (DWORD)GetInstance());

  // Note that the following two lines are not guaranteed to exactly
  // reconstruct the original Style and ExStyle values passed to
  // CreateWindow or CreateWindowEx.  This is because some controls
  // (e.g. EDIT) modify their style bits (e.g. WS_BORDER) when they
  // are being created.

  Attr.Style = GetWindowLong(AnHWindow, GWL_STYLE);
  Attr.ExStyle = GetWindowLong(AnHWindow, GWL_EXSTYLE);

  HParent = GetParent(AnHWindow);
  GetWindowRect(AnHWindow, &WndRect);
  Attr.H = WndRect.bottom - WndRect.top;
  Attr.W = WndRect.right - WndRect.left;
  if ( HParent && ((Attr.Style & WS_CHILD) == WS_CHILD) )
    ScreenToClient(HParent, (LPPOINT)&WndRect);
  Attr.X = WndRect.left;
  Attr.Y = WndRect.top;
  Attr.Param = NULL;
  Attr.Menu = NULL;
  Attr.Id = GetWindowWord(AnHWindow, GWW_ID);

  Scroller = NULL;
  FocusChildHandle = 0;
  SetFlags(WB_ALIAS, TRUE);
}

/* Destructor for a TWindow.  Disposes of its Scroller if the TScroller
  object was constructed. */
TWindow::~TWindow()
{
  if ( Scroller )
  {
    delete Scroller;
    Scroller = NULL;
  }
  if ( HIWORD(Attr.Menu) )
    farfree(Attr.Menu);
}

/* If Attr.Menu currently points to a string then free it. Set Attr.Menu to
   the passed parameter. If the window has been created then load and
   set the new menu and destroy the old one. The return value is FALSE
   if SetMenu is called and fails, TRUE otherwise.  Note that this function
   is declared virtual, but may be called from constructors of classes
   derived from TWindow.  In C++, virtual functions called from constructors
   are called as though they were non-virtual.  Care must be taken when
   redefining AssignMenu in derived classes. */
BOOL TWindow::AssignMenu(LPSTR MenuName)
{
  BOOL ReturnVal = TRUE;
  HMENU NewHMenu;
  HMENU OldHMenu;
  LPSTR TempMenuName = NULL;    // handle case where MenuName==Attr.Menu

  if ( HIWORD(Attr.Menu) )
      TempMenuName = Attr.Menu;

  if ( HIWORD(MenuName) )   // Not NULL and not an int in disguise
    Attr.Menu = _fstrdup(MenuName);
  else
    Attr.Menu = MenuName;

  if (TempMenuName)
      farfree(TempMenuName);

  if ( HWindow )
  {
    OldHMenu = GetMenu(HWindow);
    NewHMenu = LoadMenu(GetModule()->hInstance, Attr.Menu);
    ReturnVal = SetMenu(HWindow, NewHMenu);
    if ( OldHMenu )
      DestroyMenu(OldHMenu);
  }
  return ReturnVal;
}

BOOL TWindow::AssignMenu(int MenuId)
{
  return AssignMenu((LPSTR)MAKEINTRESOURCE(MenuId));
}


/* Specifies registration attributes for the MS-Windows window
   class of the TWindow, allowing instances of TWindow to be
   registered.  Sets the fields of the passed WNDCLASS parameter
   to the default attributes appropriate for a TWindow. */
void TWindow::GetWindowClass(WNDCLASS& AWndClass)
{
  AWndClass.cbClsExtra		= 0;
  AWndClass.cbWndExtra		= 0;
  AWndClass.hInstance		= GetModule()->hInstance;
  AWndClass.hIcon		= LoadIcon(0, IDI_APPLICATION);
  AWndClass.hCursor		= LoadCursor(0, IDC_ARROW);
  AWndClass.hbrBackground	= (HBRUSH)(COLOR_WINDOW + 1);
  AWndClass.lpszMenuName	= NULL;
  AWndClass.lpszClassName	= GetClassName();
  AWndClass.style		= CS_HREDRAW | CS_VREDRAW;
  AWndClass.lpfnWndProc 	= InitWndProc;
}

/* Associates an MS-Windows interface element with the TWindow object,
  after creating the interface element if not already created.  When
  creating an element, uses the creation attributes previously set
  in the Attr data field.  (Simply associates the TWindow with an
  already-created interface element if the "FromResource" flag is set.)
  If the TWindow could be successfully associated, calls SetupWindow and
  returns True.  Association is not attempted if the TWindow's Status
  data field is non-zero. */
BOOL TWindow::Create()
{
    HWND HParent;
    PTWindow TheMDIClient; // compiler doesn't know about TMDIClients yet
    MDICREATESTRUCT CreateStruct;
    WORD MenuOrId = 0;

  if (Status == 0)
  {
    DisableAutoCreate();
    if ( Parent == NULL )
      HParent = 0;
    else
      HParent = Parent->HWindow;
    if ( !IsFlagSet(WB_FROMRESOURCE) )
    {
      if ( Register() )
      {
        CreationWindow = this;
        TWindowsObject *TmpDlgCreationWindow = DlgCreationWindow;
        /*  Set DlgCreationWindow to NULL so in InitWndProc we'll know
            we're not being created from a resource */
        DlgCreationWindow = NULL;
        /* Create as MDI child if the WB_MDICHILD flag is set and Parent
           has a Client */
        if (IsFlagSet(WB_MDICHILD))
        {
	    if ((TheMDIClient = (PTWindow)(Parent->GetClient())) != NULL)
	    {   // MDI child window
                DefaultProc = (WNDPROC)DefMDIChildProc;
                CreateStruct.szClass = GetClassName();
                CreateStruct.szTitle = Title;
                CreateStruct.hOwner = GetModule()->hInstance;
                CreateStruct.x = Attr.X; CreateStruct.y = Attr.Y;
                CreateStruct.cx = Attr.W; CreateStruct.cy = Attr.H;
                CreateStruct.style = Attr.Style;
		CreateStruct.lParam = (LONG)Attr.Param;
		
		HWindow = (HWND)LOWORD(SendMessage(TheMDIClient->HWindow,
		    WM_MDICREATE, 0, (LPARAM)(&CreateStruct)));
            }
            else    // Parent doesn't have a client, error
                Status = EM_INVALIDWINDOW;
        }
        else    // Not an MDI child window
        {
          if ( Attr.Menu)
	    MenuOrId = (WORD)LoadMenu(GetModule()->hInstance,
                                   Attr.Menu);
          else
            MenuOrId = Attr.Id;
          HWindow = CreateWindowEx(Attr.ExStyle, GetClassName(),
            Title, Attr.Style, Attr.X, Attr.Y, Attr.W,
	    Attr.H, HParent, (HMENU)MenuOrId , GetModule()->hInstance,
	    Attr.Param);
        }
        DlgCreationWindow = TmpDlgCreationWindow;
      }
    }
    else /* Windows already created window */
      HWindow = GetDlgItem(HParent, Attr.Id);
    if ( HWindow )
    {
      if ( !GetObjectPtr(HWindow) ) // predefined Windows class
      {
        // Retrieve the Title
        int TitleLength = GetWindowTextLength(HWindow);
        if (TitleLength > -1)
        {
            Title = (char far*)farmalloc((unsigned long)TitleLength + 1);
            Title[0] = '\0'; Title[TitleLength] = '\0';
            GetWindowText(HWindow, Title, TitleLength+1);
        }
        else
            Title = _fstrdup("");

        // Retrieve Attr.Style and Attr.ExStyle.

        // NOTE:  Some windows controls (e.g. EDIT) change the style bits
        // (e.g. WS_BORDER) from their original values.  If we reset
        // Attr.Style and Attr.ExStyle by extracting their values from
        // Windows, we will lose some of the style bits we supplied
        // in the CreateWindowEx call.  In the case of the ResourceId
        // constructors, of course, we must retrieve these values.

        if ( IsFlagSet(WB_FROMRESOURCE) )
        {
        Attr.Style = GetWindowLong(HWindow, GWL_STYLE);
        Attr.ExStyle = GetWindowLong(HWindow, GWL_EXSTYLE);
        }

        // Retrieve Attr.X, Attr.Y, Attr.W and Attr.H
        RECT WndRect;
        GetWindowRect(HWindow, &WndRect);
        Attr.H = WndRect.bottom - WndRect.top;
        Attr.W = WndRect.right - WndRect.left;
        if ( HParent && ((Attr.Style & WS_CHILD) == WS_CHILD) )
          ScreenToClient(HParent, (LPPOINT)&WndRect);
        Attr.X = WndRect.left;
        Attr.Y = WndRect.top;

	  SetFlags(WB_PREDEFINEDCLASS, TRUE);
	DefaultProc =  (WNDPROC)SetWindowLong(HWindow, GWL_WNDPROC,
                                          (DWORD)GetInstance());
        SetupWindow();
      }
    }
    else                              // HWindow == 0
    {
      if ( Attr.Menu )
	DestroyMenu( HMENU( MenuOrId ));
      Status = EM_INVALIDWINDOW;
    }
  }
  return (Status == 0);
}

/* Response method for an incoming WM_MDIACTIVATE message. Calls
   ActivationResponse to provide a keyboard interface for the controls
   of a window. */
void TWindow::WMMDIActivate(TMessage& Msg)
{
  DefWndProc(Msg);
  if ( Parent )
    ((PTMDIFrame)Parent)->ActiveChild = (Msg.WParam ? this : NULL);
  if ( !IsFlagSet(WB_ALIAS) )
  {
    ActivationResponse(Msg.WParam, IsIconic(HWindow));
  }
}

/* Calls TWindowsObject::ActivationResponse to enable or disable the
   "keyboard handler". If the TWindow has requested keyboard handling
   for its messages, saves the child with the focus if it is being
   deactivated and restores the focus to this child when the TWindow
   is reactivated. */
void TWindow::ActivationResponse(WORD Activated, BOOL IsIconified)
{
  HWND CurrentFocus;

  TWindowsObject::ActivationResponse(Activated, IsIconified);
  if ( IsFlagSet(WB_KBHANDLER) )
  {
    if ( Activated )
    {
      if ( FocusChildHandle && IsWindow(FocusChildHandle) &&
             !IsIconified )
      {
        SetFocus(FocusChildHandle);
        // Set to 0 so we won't reset focus if another activate comes
        FocusChildHandle = 0;
      }
    }
    else
    {
      CurrentFocus = GetFocus();
      if ( !FocusChildHandle && CurrentFocus &&
           IsChild(HWindow, CurrentFocus) )
        FocusChildHandle = CurrentFocus;
    }
  }
}

static BOOL HasStyle(void *P, void *Style)
{
  return ((PTWindowsObject)P)->HWindow &&
    (GetWindowLong(((PTWindowsObject)P)->HWindow, GWL_STYLE) &
       *(long *)Style) == *(long *)Style;
}

/* Initializes ("sets up") the TWindow.  Called following a
   successful association between an MS-Windows interface element
   and a TWindow.  Calls TWindowsObject::SetupWindow to create windows
   in child list. If keyboard handling has been requested and
   FocusChildHandle has not been set, sets it to the first child.
   Sets the focus to TWindows created as MDI children.  If the
   TWindow has a TScroller object, calls the TScroller's
   SetSBarRange to set the range of the TWindow's window scrollbars.
   Can be redefined in descendant classes to perform additional
   initialization. */
void TWindow::SetupWindow()
{
  TWindowsObject::SetupWindow();

  if ( IsFlagSet(WB_KBHANDLER) && !FocusChildHandle )
  {
    long Style = WS_TABSTOP;
    PTWindowsObject TmpWO;
    TmpWO = FirstThat(HasStyle, &Style);
    if ( !TmpWO ) // no child has WS_TABSTOP
    {
      Style = WS_CHILD;
      TmpWO = FirstThat(HasStyle, &Style);
    }
    if ( TmpWO )
      FocusChildHandle = TmpWO->HWindow;
  }

  if ( IsFlagSet(WB_MDICHILD) )
    SetFocus(HWindow);
  if ( Scroller )
    Scroller->SetSBarRange();
}

void TWindow::WMCreate(TMessage& Msg)
{
  SetupWindow();
  DefWndProc(Msg);
}

/* Response method for an incoming WM_HSCROLL message.  If the
   message is from a scrollbar control, calls DispatchScroll directly to
   avoid calling TWindowsObject::WMHScroll so that GetWindowLong is
   called only once. Else passes the message to the TWindow's Scroller
   if it has been constructed, and calls DefWndProc. Assumes because of
   a Windows bug that if the window has the scrollbar style, it will not
   have scrollbar controls. */
void TWindow::WMHScroll(TMessage& Msg)
{
  if ( !(GetWindowLong(HWindow, GWL_STYLE) & WS_HSCROLL) )
    DispatchScroll(Msg);
  else
    if ( Scroller )
      Scroller->HScroll(Msg.WParam, LOWORD(Msg.LParam));
    else
      DefWndProc(Msg);
}

/* Response method for an incoming WM_VSCROLL message.  If the
   message is from a scrollbar control, calls DispatchScroll directly to
   avoid calling TWindowsObject::WMVScroll so that GetWindowLong is
   called only once. Else passes the message to the TWindow's Scroller
   if it has been constructed, and calls DefWndProc. Assumes because of
   a Windows bug that if the window has the scrollbar style, it will not
   have scrollbar controls. */
void TWindow::WMVScroll(TMessage& Msg)
{
  if ( !(GetWindowLong(HWindow, GWL_STYLE) & WS_VSCROLL) )
    DispatchScroll(Msg);
  else
    if ( Scroller )
      Scroller->VScroll(Msg.WParam, LOWORD(Msg.LParam));
    else
      DefWndProc(Msg);
}

/* Response method for an incoming WM_PAINT message. Calls Paint,
  performing Windows-required paint setup and cleanup before and after.
  (If the TWindow has a TScroller, also calls its BeginView and EndView
  methods before and after call to Paint. */
void TWindow::WMPaint(TMessage& Msg)
{
  PAINTSTRUCT PaintInfo;

  if ( IsFlagSet(WB_ALIAS) ) // use application-defined wndproc
    DefWndProc(Msg);
  else
  {
    BeginPaint(HWindow, &PaintInfo);
    if ( Scroller )
      Scroller->BeginView(PaintInfo.hdc, PaintInfo);
    Paint(PaintInfo.hdc, PaintInfo);
    if ( Scroller )
      Scroller->EndView();
    EndPaint(HWindow, &PaintInfo);
  }
}

/* Redraws the contents of the TWindow after a WMPaint message
   is received. Placeholder for descendant object types to redefine. */

#ifdef WIN31
void TWindow::Paint(HDC, PAINTSTRUCT _FAR &)
{ }
#endif

#ifdef WIN30
void TWindow::Paint(HDC_30, PAINTSTRUCT _FAR &)
{ }
#endif

/* Response method for an incoming WM_SIZE message.  Calls the
   SetPageSize and SetSBarRange methods of the TWindow's Scroller,
   if it has been constructed. Also saves the normal size of the
   window in Attr. */
void TWindow::WMSize(TMessage& Msg)
{
  RECT WndRect;

  if ( Scroller && (Msg.WParam != SIZEICONIC) )
  {
    Scroller->SetPageSize();
	Scroller->SetSBarRange();
  }
  if ( Msg.WParam == SIZENORMAL )
  {
    GetWindowRect(HWindow, &WndRect);
    Attr.H = WndRect.bottom - WndRect.top;
    Attr.W = WndRect.right - WndRect.left;
  }
  DefWndProc(Msg);
}

/* Save the normal position of the window.  If IsIconic and IsZoomed
   ignore the position since it does not reflect the normal state. */
void TWindow::WMMove(TMessage& Msg)
{
  RECT WndRect;

  if ( !(IsIconic(HWindow) || IsZoomed(HWindow)) )
  {
    GetWindowRect(HWindow, &WndRect);
    if ( ((GetWindowLong(HWindow, GWL_STYLE) & WS_CHILD) == WS_CHILD)
            && Parent && Parent->HWindow )
      ScreenToClient(Parent->HWindow, (LPPOINT)&WndRect);
    Attr.X = WndRect.left;
    Attr.Y = WndRect.top;
  }
  DefWndProc(Msg);
}

/* Response method for an incoming WM_LBUTTONDOWN message.  If
   the TWindow's Scroller has been constructed and if auto-scrolling
   has been requested, captures mouse input, loops until a
   WM_LBUTTONUP message comes in calling the Scroller's AutoScroll
   method, and then releases capture on mouse input. */
void TWindow::WMLButtonDown(TMessage& Msg)
{
  MSG LoopMsg;

  LoopMsg.message = 0;
  if ( Scroller && Scroller->AutoMode )
  {
    SetCapture(HWindow);
    while ( LoopMsg.message != WM_LBUTTONUP )
    {
      if ( PeekMessage(&LoopMsg, 0, 0, 0, PM_REMOVE) )
      {
        TranslateMessage(&LoopMsg);
        DispatchMessage(&LoopMsg);
      }
      Scroller->AutoScroll();
    }
    ReleaseCapture();
  }
  DefWndProc(Msg);
}

void *TWindow::read(ipstream& is)
{
  BOOL NameIsNumeric;
  TWindowsObject::read(is);

  if ( IsFlagSet(WB_FROMRESOURCE) )
  {
    DefaultProc = (WNDPROC)DefWindowProc;
    memset(&Attr, 0x0, sizeof (Attr));
  }
  else
  {
    is >> Attr.Style >> Attr.ExStyle
       >> Attr.X >> Attr.Y >> Attr.W
       >> Attr.H >> (long)(Attr.Param);

    if ( IsFlagSet(WB_MDICHILD) )
      DefaultProc = (WNDPROC)DefMDIChildProc;
    else
      DefaultProc = (WNDPROC)DefWindowProc;
  }
  is >> Attr.Id;

  is >> NameIsNumeric;
  if ( NameIsNumeric )
    is >> (long)(Attr.Menu);
  else
    Attr.Menu= is.freadString();

  is >> Scroller;
  if ( Scroller )
    Scroller->Window = this;
  FocusChildHandle = 0;
  return this;
}

/* Writes data of the TWindow to the passed opstream. Writes its
   Scroller if constructed. */
void TWindow::write(Ropstream os)
{
  long SaveStyle;
  BOOL NameIsNumeric;
  TWindowsObject::write(os);
  if ( !IsFlagSet(WB_FROMRESOURCE) )
  {
    SaveStyle = Attr.Style & ~(WS_MINIMIZE | WS_MAXIMIZE);
    if ( HWindow )
      if ( IsIconic(HWindow) )
        SaveStyle |= WS_MINIMIZE;
      else
        if ( IsZoomed(HWindow) )
          SaveStyle |= WS_MAXIMIZE;
    os << SaveStyle << Attr.ExStyle << Attr.X
       << Attr.Y << Attr.W << Attr.H
       << (long)(Attr.Param);
  }
  os << Attr.Id;

  NameIsNumeric = HIWORD(Attr.Menu) == NULL;
  os << NameIsNumeric;
  if ( NameIsNumeric )
    os << (long)(Attr.Menu);
  else
    os.fwriteString(Attr.Menu);

  os << Scroller;
}

PTStreamable TWindow::build()
{
  return new TWindow(streamableInit);
}

TStreamableClass RegWindow("TWindow", TWindow::build, __DELTA(TWindow));
