//----------------------------------------------------------------------------
// ObjectComponents
// Copyright (c) 1994, 1996 by Borland International, All Rights Reserved
//
/// \file
/// Implementation of class TOleFrame.
//----------------------------------------------------------------------------
#include <ocf/pch.h>
 
#include <owl/decmdifr.h>
#include <owl/statusba.h>
#include <ocf/ocfevent.h>
#include <ocf/oleframe.h>
#include <ocf/olewindo.h>
#include <owl/commctrl.h>
 
namespace ocf {
 
using namespace owl;
 
using namespace std;
 
OWL_DIAGINFO;
//DIAG_DECLARE_GROUP(OwlCmd);
DIAG_DEFINE_GROUP_INIT(OWL_INI, OcfOleMenu, 1, 0);
 
//#define DEBUG_PADS
// define to make pads paint red for debugging time in MS for idle action polling
const int DllIdleTime = 200;
 
// Four edge locations for inplace-server space negotiation
//
const TDecoratedFrame::TLocation SpaceLoc[] = {
  TDecoratedFrame::Left,
  TDecoratedFrame::Top,
  TDecoratedFrame::Right,
  TDecoratedFrame::Bottom
};
 
//----------------------------------------------------------------------------
/// An empty no-erase window that inserts itself into its decorated frame
/// parent
//
 
class TPadWindow : public TWindow {
  public:
    TPadWindow(TWindow* parent, int edge, TModule* module = 0)
      : TWindow(parent, 0, module)
    {
      Attr.Id = IDW_PADDECORATION+edge;
      Attr.Style = WS_CHILD | WS_VISIBLE;
      Attr.W = Attr.H = 1;             // takeup no space until needed
                                       // need 1 since layout overlapps borders
#if defined(DEBUG_PADS)
      SetBkgndColor(TColor(255,0,0));  // to help Debug toolbar negotiations
      strstream s;
      s << _T("Pad") << (_T('0') + edge);
      Title = strnewdup(s.str());
#else
      SetBkgndColor(NoErase);          // Don't erase--to avoid flicker
#endif
    }
};
 
//
/// \class TRemViewBucket
// ~~~~~ ~~~~~~~~~~~~~~
/// A small window class to act as a holder for the unattached remote server
/// view windows
//
class  TRemViewBucket : public TFrameWindow {
  public:
    TRemViewBucket(TWindow* parent);
 
    // Overriden virtuals of base class
    //
    bool      SetDocTitle(LPCTSTR, int);
 
    // Override handlers to act as pass-thru on behalf of remove server view
    //
    TResult   EvCommand(uint id, THandle hWndCtl, uint notifyCode);
    TResult   EvNotify(uint id, TNotify & notifyInfo);
    void      EvCommandEnable(TCommandEnabler& ce);
    TWindow*  FindRemoteWindow();
};
 
//
//
//
TRemViewBucket::TRemViewBucket(TWindow* parent)
               :TFrameWindow(parent, _T("RemViewBucket"))
{
  Attr.Style = WS_CHILD | WS_DISABLED;
  Attr.Id = IDW_REMVIEWBUCKET;
}
 
//
//
//
bool
TRemViewBucket::SetDocTitle(LPCTSTR, int)
{
  return true;
}
 
//
//
//
TWindow*
TRemViewBucket::FindRemoteWindow()
{
  // Check if we can find the object representing the remote window object by
  // traversing the focus window tree
  //
  TWindow* remote = 0;
  HWND focusHwnd  = GetFocus();
  HWND hwnd       = focusHwnd;
  while (hwnd) {
    remote = GetWindowPtr(hwnd);
    if (remote) {
      // The remote window has the remViewBucket as its parent
      //
      if (remote->GetParentO() == this)
        break;
      else
        remote = 0;
    }
    hwnd = ::GetParent(hwnd);
  }
  return remote;
}
 
//
//
//
TResult
TRemViewBucket::EvCommand(uint id, THandle hWndCtl, uint notifyCode)
{
  // Find remote view
  //
  TWindow* handler = FindRemoteWindow();
 
  // Default to parent if none was found
  //
  if (!handler)
    handler = GetParentO();
 
  // Forward event
  //
  CHECK(handler);
  return handler->EvCommand(id, hWndCtl, notifyCode);
}
 
//
//
//
TResult
TRemViewBucket::EvNotify(uint id, TNotify & notifyInfo)
{
  // Find remote view
  //
  TWindow* handler = FindRemoteWindow();
 
  // Default to parent if none was found
  //
  if (!handler)
    handler = GetParentO();
 
  // Forward event
  //
  CHECK(handler);
  return handler->EvNotify(id, notifyInfo);
}
 
//
//
//
void
TRemViewBucket::EvCommandEnable(TCommandEnabler& ce)
{
  // Find remote view
  //
  TWindow* handler = FindRemoteWindow();
 
  // Default to parent if none was found
  //
  if (!handler)
    handler = GetParentO();
 
  // Forward event
  //
  CHECK(handler);
  handler->EvCommandEnable(ce);
}
 
DEFINE_RESPONSE_TABLE1(TOleFrame, TDecoratedFrame)
  EV_WM_SIZE,
  EV_WM_TIMER,
  EV_WM_ACTIVATEAPP,
 
  EV_MESSAGE(WM_OCEVENT, EvOcEvent),
  EV_OC_APPINSMENUS,
  EV_OC_APPMENUS,
  EV_OC_APPPROCESSMSG,
  EV_OC_APPFRAMERECT,
  EV_OC_APPBORDERSPACEREQ,
  EV_OC_APPBORDERSPACESET,
  EV_OC_APPSTATUSTEXT,
  EV_OC_APPRESTOREUI,
  EV_OC_APPSHUTDOWN,
END_RESPONSE_TABLE;
 
//
/// Constructs a TOleFrame object with the specified caption for the frame window
/// (title), the client window (clientWnd), and module instance. The
/// trackMenuSelection parameter indicates whether or not the frame window should
/// track menu selections (that is, display hint text in the status bar of the
/// window when a menu item is highlighted).
//
TOleFrame::TOleFrame(LPCTSTR   title,
                     TWindow* clientWnd,
                     bool     trackMenuSelection,
                     TModule* module)
:
  TDecoratedFrame(0, title, clientWnd, trackMenuSelection, module),
  TFrameWindow(0, title, clientWnd, false, module),
  TWindow(0, title, module),
  HOldMenu(0),
  OcApp(0),
  StashCount(0),
  OcShutDown(DontCare)
{
  new TRemViewBucket(this);  // Construct bucket to hold hidden server windows
 
  // Retrieve the OcApp ptr from our owning application if it is a TOcAppHost
  //
  TOcAppHost* ocm = TYPESAFE_DOWNCAST(GetApplication(), TOcAppHost);
  if (ocm)
    SetOcApp(ocm->OcApp);
}
 
//
/// Destroys a TOleFrame object.
//
/// Let the OC app go. It will delete itself when it can
//
TOleFrame::~TOleFrame()
{
}
 
//
/// Initial set of OcApp being passed to us to use.
//
/// Sets the ObjectComponents application associated with this frame window to the
/// applications specified in the app parameter.
//
void
TOleFrame::SetOcApp(TOcApp* ocApp)
{
  PRECONDITION(ocApp);
  OcApp = ocApp;
 
  // Initialize OLE 2 clipboard format names
  //
  TCHAR f[] = _T("%s");
  AddUserFormatName(f, f, ocrEmbedSource);
  AddUserFormatName(f, f, ocrEmbeddedObject);
  AddUserFormatName(f, f, ocrLinkSource);
}
 
//
/// Associates the ObjectComponents application with the window's HWND so that the
/// TOcApp and the window can communicate. Prepares a place to insert the server's
/// toolbars when in-place editing of the embedded object occurs.
//
void
TOleFrame::SetupWindow()
{
  PRECONDITION(OcApp);
  TDecoratedFrame::SetupWindow();
  OcApp->SetupWindow(GetHandle());
 
  // Insert the four pad windows for in-place server toolbars. Inserting last
  // will place them as the inner-most decorations, which is needed with the
  // status bar
  //
  for (int edge = 0; edge < 4; edge++)
    Insert(*new TPadWindow(this, edge), ::ocf:: SpaceLoc[edge]);
 
  // Create a timer to allow us to poll for idle time when we are a dll server
  //
  if (!OcApp->IsOptionSet(amExeMode))
    SetTimer(IDT_DLLIDLE, DllIdleTime);
}
 
//
/// Performs normal window cleanup of any HWND-related resources. For DLL servers,
/// CleanupWindow destroys the idle timer.
//
void
TOleFrame::CleanupWindow()
{
  if (!OcApp->IsOptionSet(amExeMode))
    KillTimer(IDT_DLLIDLE);
}
 
//
/// Adds user defined formats and the corresponding names to the list of Clipboard
/// formats. Use this function if you want to associate a Clipboard data format
/// (name) with the description of the data format as it appears to users in the
/// Help text of the Paste Special dialog box (resultName). To use a standard
/// Clipboard format, set the id parameter to an appropriate constant (for example,
/// CF_TEXT). Otherwise, if the format is identified by a string, pass the string as
/// the name and omit the ID.
//
void
TOleFrame::AddUserFormatName(LPCTSTR name, LPCTSTR resultName,
                             LPCTSTR id)
{
  PRECONDITION(OcApp);
  OcApp->AddUserFormatName(name, resultName, id);
}
 
//
/// Sets the OcShutDown data member to ViewInitiated (if close is true and
/// OcShutDown = DontCare) or DontCare (if close if false and OcShutDown =
/// ViewInitiated.
//
void
TOleFrame::OleViewClosing(bool close)
{
  if (close && OcShutDown == DontCare) {
    OcShutDown = ViewInitiated;
  }
  else if (!close && OcShutDown == ViewInitiated) {
    OcShutDown = DontCare;
  }
}
 
namespace {
 
/// Disconnect document servers with their clients.
/// Document servers can be documents with objects copied on the clipboard or
/// documents brought up though linking.
//
void DisconnectDocServer_(TWindow& win)
{
  for (auto& child : win.GetChildren())
    DisconnectDocServer_(child);
 
  TOleWindow* oleWin = TYPESAFE_DOWNCAST(&win, TOleWindow);
  if (oleWin)
    oleWin->OleShutDown();
}
 
} // namespace
 
//
/// Checks with all the connected servers to ensure that they can close before
/// destroying the frame window. If the user closes the application with objects
/// still embedded, Destroy hides the frame window instead of destroying it.
//
void
TOleFrame::Destroy(int retVal)
{
  if (!GetHandle() || OcShutDown == UserInitiated)
    return;
 
  // Disconnect document servers with their clients if user shuts down the app
  //
  if (OcShutDown == DontCare) {
    OcShutDown = UserInitiated;
    for (auto& child : GetChildren())
      DisconnectDocServer_(child);
  }
 
  if (!OcApp->CanClose()) {
    OcApp->SetOption(amEmbedding, true);
    OcShutDown = DontCare; // reset the shutdown flag
    ShowWindow(SW_HIDE);
  }
  else {
    bool dllServer = !OcApp->IsOptionSet(amExeMode);
 
    TDecoratedFrame::Destroy(retVal);
 
    // If user shuts down the DLL server (as in the case of open-editing,
    // we need to set the mainwindow flag to 0 so that only the application
    // will be destroyed.  All windows are destroyed in previous calls.
    //
    if (dllServer && OcShutDown != ServerInitiated) {
//      GetApplication()->SetMainWindow(0);
      GetApplication()->ClearMainWindow();
      delete GetApplication();
    }
  }
}
 
//
/// Passes a WM_SIZE message to TLayoutWindow.
//
/// Responds to an EV_WM_SIZE message indicating a change in the frame window's size
/// and forwards this information to the TOcApp, which then notifies any in-place
/// server. The server uses this information to change the size of its toolbar, if
/// necessary.
//
void
TOleFrame::EvSize(uint sizeType, const TSize& size)
{
  TDecoratedFrame::EvSize(sizeType, size);
  if (OcApp)
    OcApp->EvResize();
}
 
//
/// Responds to a EV_WM_ACTIVATEAPP message sent when a window is activated or
/// deactivated. If active is true, the window is being activated.
/// This message is sent to the top-level window being deactivated before it is sent
/// to the top-level window being activated. hTask is a handle to the current
/// process.
/// This event is forwarded to the TOcApp object, which activates an in-place server
/// if one exists.
//
void
TOleFrame::EvActivateApp(bool active, DWORD threadId)
{
  OcApp->EvActivate(active);
  TDecoratedFrame::EvActivateApp(active, threadId);
}
 
//
/// If this is a DLL server, EvTimer responds to a timer message by running an idle
/// loop if the message queue is empty.
//
void
TOleFrame::EvTimer(uint timerId)
{
  if (timerId == IDT_DLLIDLE)
    GetApplication()->PostDispatchAction();
  TWindow::EvTimer(timerId);
}
 
//
/// Responds to a WM_OCEVENT message and subdispatches it to one of the EvOcXxxx
/// event-handling functions based on the value of wParam. WM_OCEVENT messages are
/// sent by ObjectComponents when it needs to communicate with an OLE-generated
/// event; for example, if a server wants to display toolbars.
//
TResult
TOleFrame::EvOcEvent(TParam1 param1, TParam2 param2)
{
  TEventHandler::TEventInfo eventInfo(WM_OCEVENT, static_cast<uint>(param1));
 
  // Give the client window the first chance at it
  //
  TWindow* receiver = GetClientWindow();
  if (receiver->Find(eventInfo))
    return receiver->Dispatch(eventInfo, param1, param2);
 
  // Then try this frame
  //
  if (Find(eventInfo))
    return Dispatch(eventInfo, param1, param2);
 
  // Last, try the application in case it wants to override events
  //
  if (GetApplication()->Find(eventInfo))
    return GetApplication()->Dispatch(eventInfo, param1, param2);
  return 0;
}
 
//
/// Responds to an EV_OC_APPINSMENUS message by merging the container's menu into
/// the shared menu. The sharedMenu parameter refers to this merged menu.
//
bool
TOleFrame::EvOcAppInsMenus(TOcMenuDescr & sharedMenu)
{
  if (HOldMenu) {
    TRACEX(OcfOleMenu, 0, _T("EvOcAppInsMenus called while HOldMenu is ") << hex <<
      static_cast<void*>(HOldMenu));
    return true;
  }
 
  // Recreate a temporary composite menu for frame
  //
  TMenuDescr compMenuDesc; // empty menudescr
  if (GetMenuDescr()) {
    compMenuDesc.Merge(*GetMenuDescr());
 
    // Mask off the server menus
    //
    compMenuDesc.Merge(TMenuDescr(0,  0, -1, 0, -1, 0, -1));
  }
  else
    return false; // stop menu negotiation if we don't have menu
 
  // Merge into the OLE shared menubar
  //
  TMenuDescr shMenuDescr(sharedMenu.HMenu,
                         sharedMenu.Width[0],
                         sharedMenu.Width[1],
                         sharedMenu.Width[2],
                         sharedMenu.Width[3],
                         sharedMenu.Width[4],
                         sharedMenu.Width[5]);
  shMenuDescr.Merge(compMenuDesc);
 
  // Copy the shared menu widths back to the OC struct
  //
  for (int i = 0; i < 6; i++)
    sharedMenu.Width[i] = shMenuDescr.GetGroupCount(i);
 
  // Save the container popups so they can be destroyed later
  //
  StashContainerPopups(shMenuDescr);
 
  TRACEX(OcfOleMenu, 0, _T("Merged menu ") << hex << static_cast<void*>(sharedMenu.HMenu));
  return true;
}
 
//
/// Responds to an OC_OCAPPMENUS sent to the container. The response is to install a
/// merged menu bar.
//
bool
TOleFrame::EvOcAppMenus(TOcMenuDescr & appMenus)
{
  if (!appMenus.HMenu) {
    if (HOldMenu) {
      TRACEX(OcfOleMenu, 0, _T("EvOcAppMenus(0) resetting Old ") << hex <<
        static_cast<void*>(HOldMenu));
      TMenu oleMenu(GetHandle());
      SetMenu(HOldMenu);     // assumes we are just restoring the old owl menu
      HOldMenu = 0;
    }
    DestroyStashedPopups();  // destroy the popup copies we made
    return true;
  }
 
  // Don't set the menu again if we are holding a merged one already
  //
  if (HOldMenu) {
    TRACEX(OcfOleMenu, 0, _T("EvOcAppMenus called while HOldMenu is ") << hex <<
      static_cast<void*>(HOldMenu));
    return true;
  }
 
  HOldMenu = GetMenu();
  TRACEX(OcfOleMenu, 0, _T("Saved Old ") << hex << static_cast<void*>(HOldMenu));
 
  SetMenu(appMenus.HMenu);
  TRACEX(OcfOleMenu, 0, _T("Set merged ") << hex << static_cast<void*>(appMenus.HMenu));
 
  return true;
}
 
//
/// Responds to an EV_OC_APPROCESSMSG message sent to the container asking the server
/// to process accelerators and other messages from the container's message queue.
//
bool
TOleFrame::EvOcAppProcessMsg(MSG * msg)
{
  return GetApplication()->ProcessAppMsg(*msg);
}
 
//
/// Responds to an OC_APPFRAMERECT message sent to a container. The response is to
/// get the coordinates of the client area rectangle of the application's main window.
//
bool
TOleFrame::EvOcAppFrameRect(TRect * rect)
{
  PRECONDITION(rect);
  *rect = GetClientRect();
  TWindow*  sb = ChildWithId(IDW_STATUSBAR);
  if (sb) {
    TRect sbr = sb->GetClientRect();
    rect->bottom -= sbr.bottom+1;
  }
  return true;
}
 
//
/// Responds to an OC_APPBORDERSPACEREQ message sent to a container. The response is
/// to ask the container if it can give border space in its frame window to the
/// server.
//
bool
TOleFrame::EvOcAppBorderSpaceReq(TRect * /*space*/)
{
  return true;
}
 
//
/// Responds to an OC_APPBORDERSPACESET message by making room in the container's
/// frame window for the border space that the server has requested.
//
bool
TOleFrame::EvOcAppBorderSpaceSet(TRect * space)
{
  // Resize pad decorations based on edges requested
  //
  int* edges = (int*)space;   // treat space as array of 4 int edges
  bool needLayout = false;
  for (int i = 0; i < 4; i++) {
    TWindow*  pad = ChildWithId(IDW_PADDECORATION+i);
    if (pad) {
      int edge = edges && edges[i] ? edges[i]+1 : 1;
      if ((i%2 == 0 && pad->GetWindowAttr().W != edge) ||   // left & right edge
          (i%2 == 1 && pad->GetWindowAttr().H != edge)) {   // top & bottom edge
        TLayoutMetrics m;
        GetChildLayoutMetrics(*pad, m);
        pad->GetWindowAttr().H = pad->GetWindowAttr().W = edge;  // set both axis, one will be stretched
        SetChildLayoutMetrics(*pad, m);
        needLayout = true;
      }
    }
  }
 
  // Turn on/off control bar as needed
  //
  TWindow*  tb = ChildWithId(IDW_TOOLBAR);
  if (tb)
    if ((space && tb->IsWindowVisible()) || (!space && !tb->IsWindowVisible())) {
      SendMessage(WM_COMMAND, IDW_TOOLBAR);  // toggle tool bar on/off
      needLayout = false;  // layout already done now by decorated frame.
    }
 
  // Now do layout once at the end to reduce repaint
  //
  if (needLayout)
    Layout();
 
  return true;
}
 
//
/// Responds to an OC_APPSTATUSTEXT message by displaying text from the server on
/// this container's status bar.
//
void
TOleFrame::EvOcAppStatusText(LPCTSTR text)
{
  PRECONDITION(text);
  TMessageBar* mb = TYPESAFE_DOWNCAST(ChildWithId(IDW_STATUSBAR), TMessageBar);
  if (mb) {
    //mb->SetHintText(text);   // is the text a hint, or general status??
    mb->SetText(text);
  }
}
 
//
/// Stores a local copy of the pop-up menus so they can be used for menu merging and
/// then destroyed later by DestroyStashedPopups. shMenuDescr is the shared menu
/// descriptor to be stored. Increments StashCount each time the pop-up menus are saved.
//
void TOleFrame::StashContainerPopups(const TMenuDescr& shMenuDescr)
{
  StashCount++;
 
  int m = 0;
  for (int i = 0; i < 6; i++) {
    if (i%2 == 0)
      for (int j = 0; j < shMenuDescr.GetGroupCount(i); j++) {
        uint  state = shMenuDescr.GetMenuState(m+j, MF_BYPOSITION);
        if (state == uint(-1))
          continue;
        TRACEX(OcfOleMenu, 1, _T("Stashing ") << hex << static_cast<void*>(shMenuDescr.GetSubMenu(m+j)));
        StashedContainerPopups.AppendMenu(state, TMenu(shMenuDescr.GetSubMenu(m+j)), _T(""));
      }
    m += shMenuDescr.GetGroupCount(i);
  }
}
 
//
/// Destroys the previously stored shared pop-up menus. Checks to see if StashCount
/// is 0 before destroying the menus.
//
void TOleFrame::DestroyStashedPopups()
{
  if (--StashCount)
    return;
 
  while (StashedContainerPopups.GetMenuItemCount()) {
    TRACEX(OcfOleMenu, 1, _T("Destroying ") << hex << static_cast<void*>(StashedContainerPopups.GetSubMenu(0)));
    StashedContainerPopups.DeleteMenu(0, MF_BYPOSITION);
  }
}
 
//
/// Responds to an OC_APPRESTOREUI message by restoring the container's normal menu
/// and borders because in-place editing has finished.
//
void
TOleFrame::EvOcAppRestoreUI()
{
  // Only restore the old menu if we are holding a merged one
  //
  if (HOldMenu) {
    TRACEX(OcfOleMenu, 0, _T("EvOcAppRestoreUI resetting Old ") << hex <<
      static_cast<void*>(HOldMenu));
    TMenu oleMenu(GetHandle());
    SetMenu(HOldMenu);    // assumes we are just restoring the old owl menu
    HOldMenu = 0;
  }
 
  // Remove pad decorations & restore our toobar if we have one
  //
  EvOcAppBorderSpaceSet(0);
}
 
//
/// Responds to an OC_APPSHUTDOWN message indicating that the last embedded object
/// has been closed. The response is to shut down the server.
//
bool
TOleFrame::EvOcAppShutdown()
{
  // If TOleFrame was created purely for embedded server, then
  // we want to shut down the app when nobody is using the server.
  // The amEmbedding flag will be set to false if user created normal
  // document in this frame.
  //
  if (OcShutDown == DontCare && OcApp->IsOptionSet(amEmbedding)) {
    // The shut down is initiated by OCF
    //
    OcShutDown = ServerInitiated;
 
    // Post frame close to kill the app later
    //
    PostMessage(WM_CLOSE);
 
    return true;
  }
  else {
 
    // If the last view closing caused the ocapp to shutdown, then close this
    // frame.
    //
    if (OcApp->IsOptionSet(amEmbedding) && OcShutDown == ViewInitiated)
      PostMessage(WM_CLOSE);
 
    return false; // Shut down initiated by user
  }
}
 
} // OCF namespace
 
//==============================================================================
 

V1004 The 'handler' pointer was used unsafely after it was verified against nullptr. Check lines: 150, 151.

V1004 The 'handler' pointer was used unsafely after it was verified against nullptr. Check lines: 171, 172.

V1004 The 'handler' pointer was used unsafely after it was verified against nullptr. Check lines: 192, 193.

V1004 The 'rect' pointer was used unsafely after it was verified against nullptr. Check lines: 575, 576.

V728 An excessive check can be simplified. The '(A && B) || (!A && !B)' expression is equivalent to the 'bool(A) == bool(B)' expression.

V1004 The 'text' pointer was used unsafely after it was verified against nullptr. Check lines: 646, 650.

V807 Decreased performance. Consider creating a reference to avoid using the 'pad->GetWindowAttr()' expression repeatedly.