- BORLAND/: Borland C++ 4.52 (chosen over 4.5 by byte-match: CODE/RP/CW32.LIB
is identical to 4.52's install lib). BCC32/TLINK32/TLIB/MAKE run natively on
Win11; CODE/BT/OPT.MAK is the shipped BTL4OPT.EXE's exact flag recipe
(extender = Borland PowerPack DPMI32, not Phar Lap TNT).
- restoration/source410/: the literal 1995-form reconstruction of the missing
BT game source (never mixed into CODE/). Round 1-3 state:
* 6 of 10 surviving original TUs COMPILE CLEAN under the period toolchain
(BTMSSN, BTCNSL, BTSCNRL, BTTEAM, BTL4MODE, BTL4ARND) - first builds
since 1996.
* BT_L4/BTL4APP.CPP pilot reconstruction: 12/12 functions, Fail() lands on
its binary-recorded line 400 exactly.
* BT/BTCNSL.HPP: console wire IDs recovered from the binary's ctors
(Killed=9, Damaged=10, ScoreUpdate=13, DeathWithoutHonor=15 [T1];
TeamScore=12 flagged [T4]).
* MUNGA/: 8 engine-header backfills back-dated from the BT412 WinTesla tree
(VDATA numbering decomp-verified; AUDREND's OpenAL-era virtual removed -
the period compiler is the drift detector).
* Tooling: backdate.py (WinTesla->1995 header transform), compile410.sh
(per-TU verification sweep under authentic OPT.MAK flags).
* README: corrected roadmap - MECH.HPP is the capstone grown with the mech
TU reconstructions; BTREG.CPP green = the header-family milestone.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
592 lines
15 KiB
C++
592 lines
15 KiB
C++
//----------------------------------------------------------------------------
|
|
// ObjectWindows
|
|
// (C) Copyright 1994 by Borland International, All Rights Reserved
|
|
//
|
|
// Implementation of class TOleFrame.
|
|
//----------------------------------------------------------------------------
|
|
#define INC_OLE2
|
|
#include <owl/owlpch.h>
|
|
#include <owl/decmdifr.h>
|
|
#include <owl/statusba.h>
|
|
#include <owl/ocfevent.h>
|
|
#include <owl/uihandle.h>
|
|
#include <owl/oleframe.h>
|
|
#include <owl/olewindo.h>
|
|
|
|
DIAG_DEFINE_GROUP_INIT(OWL_INI, OwlOleMenu, 1, 0);
|
|
|
|
//#define DEBUG_PADS // define to make pads paint red for debugging
|
|
const int DllIdleTime = 200; // time in MS for idle action polling
|
|
|
|
// 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 << "Pad" << ('0' + edge);
|
|
Title = strnewdup(s.str());
|
|
#else
|
|
SetBkgndColor(NoErase); // Don't erase--to avoid flicker
|
|
#endif
|
|
}
|
|
};
|
|
|
|
//----------------------------------------------------------------------------
|
|
// A small window class to act as a holder for the unattached remote server
|
|
// view windows
|
|
//
|
|
|
|
class TRemViewBucket : public TFrameWindow {
|
|
public:
|
|
TRemViewBucket(TWindow* parent)
|
|
: TFrameWindow(parent, "RemViewBucket")
|
|
{
|
|
Attr.Style = WS_CHILD | WS_DISABLED;
|
|
Attr.Id = IDW_REMVIEWBUCKET;
|
|
}
|
|
|
|
bool SetDocTitle(const char far*, int) {return true;}
|
|
|
|
protected:
|
|
private:
|
|
};
|
|
|
|
|
|
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;
|
|
|
|
TOleFrame::TOleFrame(const char far* 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 TOcModule
|
|
//
|
|
TOcModule* ocm = TYPESAFE_DOWNCAST(GetApplication(), TOcModule);
|
|
if (ocm)
|
|
SetOcApp(ocm->OcApp);
|
|
}
|
|
|
|
//
|
|
// Let the OC app go. It will delete itself when it can
|
|
//
|
|
TOleFrame::~TOleFrame()
|
|
{
|
|
}
|
|
|
|
//
|
|
// Initial set of OcApp being passed to us to use.
|
|
//
|
|
void
|
|
TOleFrame::SetOcApp(TOcApp* ocApp)
|
|
{
|
|
PRECONDITION(ocApp);
|
|
OcApp = ocApp;
|
|
|
|
// Initialize OLE 2 clipboard format names
|
|
//
|
|
char f[] = "%s";
|
|
AddUserFormatName(f, f, ocrEmbedSource);
|
|
AddUserFormatName(f, f, ocrEmbeddedObject);
|
|
AddUserFormatName(f, f, ocrLinkSource);
|
|
}
|
|
|
|
//
|
|
// Let the OcApp know our HWND so that it can talk to us
|
|
//
|
|
void
|
|
TOleFrame::SetupWindow()
|
|
{
|
|
PRECONDITION(OcApp);
|
|
TDecoratedFrame::SetupWindow();
|
|
OcApp->SetupWindow(*this);
|
|
|
|
// 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), ::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);
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
void
|
|
TOleFrame::CleanupWindow()
|
|
{
|
|
if (!OcApp->IsOptionSet(amExeMode))
|
|
KillTimer(IDT_DLLIDLE);
|
|
}
|
|
|
|
//
|
|
// Add user defined format and result names to the list
|
|
//
|
|
void
|
|
TOleFrame::AddUserFormatName(const char far* name, const char far* resultName,
|
|
const char far* id)
|
|
{
|
|
PRECONDITION(OcApp);
|
|
OcApp->AddUserFormatName(name, resultName, id);
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
void
|
|
TOleFrame::OleViewClosing(bool close)
|
|
{
|
|
if (close && OcShutDown == DontCare) {
|
|
OcShutDown = ViewInitiated;
|
|
}
|
|
else if (!close && OcShutDown == ViewInitiated) {
|
|
OcShutDown = DontCare;
|
|
}
|
|
}
|
|
|
|
// Disconnect document servers with their clients.
|
|
// Document servers can be documents with objects copied on the clipboard or
|
|
// documents brought up though linking.
|
|
//
|
|
void
|
|
sDisconnectDocServer(TWindow* win, void* /*retVal*/)
|
|
{
|
|
if (win) {
|
|
win->ForEach(sDisconnectDocServer);
|
|
|
|
TOleWindow* oleWin = TYPESAFE_DOWNCAST(win, TOleWindow);
|
|
if (oleWin)
|
|
oleWin->OleShutDown();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make sure that any embeded servers also get a chance to OK the close
|
|
// sequence. If the user closes the app, but there are still embeddings, hide
|
|
// the frame instead of destroying it
|
|
//
|
|
void
|
|
TOleFrame::Destroy(int retVal)
|
|
{
|
|
if (!HWindow || OcShutDown == UserInitiated)
|
|
return;
|
|
|
|
// Disconnect document servers with their clients if user shuts down the app
|
|
//
|
|
if (OcShutDown == DontCare) {
|
|
OcShutDown = UserInitiated;
|
|
ForEach(sDisconnectDocServer, 0);
|
|
}
|
|
|
|
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()->MainWindow = 0;
|
|
delete GetApplication();
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Forward size message to OcApp to allow it to notify any embedded servers
|
|
// about size changes
|
|
//
|
|
void
|
|
TOleFrame::EvSize(uint sizeType, TSize& size)
|
|
{
|
|
TDecoratedFrame::EvSize(sizeType, size);
|
|
if (OcApp)
|
|
OcApp->EvResize();
|
|
}
|
|
|
|
//
|
|
// Forward Activate messages to OcApp to allow it to notify any embedded servers
|
|
// about being activated
|
|
//
|
|
void
|
|
TOleFrame::EvActivateApp(bool active, HTASK hTask)
|
|
{
|
|
OcApp->EvActivate(active);
|
|
TDecoratedFrame::EvActivateApp(active, hTask);
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
void
|
|
TOleFrame::EvTimer(uint timerId)
|
|
{
|
|
if (timerId == IDT_DLLIDLE)
|
|
GetApplication()->PostDispatchAction();
|
|
TWindow::EvTimer(timerId);
|
|
}
|
|
|
|
//
|
|
// Handle & sub-dispatch the OC event message.
|
|
//
|
|
LRESULT
|
|
TOleFrame::EvOcEvent(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
TEventHandler::TEventInfo eventInfo(WM_OCEVENT, wParam);
|
|
|
|
// Give the client window the first chance at it
|
|
//
|
|
TWindow* receiver = GetClientWindow();
|
|
if (receiver->Find(eventInfo))
|
|
return receiver->Dispatch(eventInfo, wParam, lParam);
|
|
|
|
// Then try this frame
|
|
//
|
|
if (Find(eventInfo))
|
|
return Dispatch(eventInfo, wParam, lParam);
|
|
|
|
// Last, try the application in case it wants to override events
|
|
//
|
|
if (GetApplication()->Find(eventInfo))
|
|
return GetApplication()->Dispatch(eventInfo, wParam, lParam);
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Insert our menus into a provided menu bar, possibly merging them with a
|
|
// servers.
|
|
//
|
|
bool
|
|
TOleFrame::EvOcAppInsMenus(TOcMenuDescr far& sharedMenu)
|
|
{
|
|
if (HOldMenu) {
|
|
TRACEX(OwlOleMenu, 0, "EvOcAppInsMenus called while HOldMenu is " << hex <<
|
|
(uint)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));
|
|
}
|
|
|
|
// 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(OwlOleMenu, 0, "Merged menu " << hex << (uint)sharedMenu.HMenu);
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Now, actually set the merged menubar into our frame
|
|
//
|
|
bool
|
|
TOleFrame::EvOcAppMenus(TOcMenuDescr far& appMenus)
|
|
{
|
|
if (!appMenus.HMenu) {
|
|
if (HOldMenu) {
|
|
TRACEX(OwlOleMenu, 0, "EvOcAppMenus(0) resetting Old " << hex <<
|
|
(uint)HOldMenu);
|
|
TMenu oleMenu(*this);
|
|
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(OwlOleMenu, 0, "EvOcAppMenus called while HOldMenu is " << hex <<
|
|
(uint)HOldMenu);
|
|
return true;
|
|
}
|
|
|
|
HOldMenu = GetMenu();
|
|
TRACEX(OwlOleMenu, 0, "Saved Old " << hex << (uint)HOldMenu);
|
|
|
|
SetMenu(appMenus.HMenu);
|
|
TRACEX(OwlOleMenu, 0, "Set merged " << hex << (uint)appMenus.HMenu);
|
|
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Process accelerators and other queued messages from the server's queue in
|
|
// our app
|
|
//
|
|
bool
|
|
TOleFrame::EvOcAppProcessMsg(MSG far* msg)
|
|
{
|
|
#if defined(BI_DATA_NEAR)
|
|
MSG nearMsg = *msg;
|
|
bool ret = GetApplication()->ProcessAppMsg(nearMsg);
|
|
*msg = nearMsg;
|
|
return ret;
|
|
#else
|
|
return GetApplication()->ProcessAppMsg(*msg);
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Let the server know our frame's client rect, minus the status bar height,
|
|
// if there is one
|
|
//
|
|
bool
|
|
TOleFrame::EvOcAppFrameRect(TRect far* rect)
|
|
{
|
|
PRECONDITION(rect);
|
|
*rect = GetClientRect();
|
|
TWindow* sb = ChildWithId(IDW_STATUSBAR);
|
|
if (sb) {
|
|
TRect sbr = sb->GetClientRect();
|
|
rect->bottom -= sbr.bottom+1;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Let the server know that we can handle border adornments on all four edges
|
|
//
|
|
bool
|
|
TOleFrame::EvOcAppBorderSpaceReq(TRect far* /*space*/)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Actually create space on the requested edges for the server's adornments
|
|
//
|
|
bool
|
|
TOleFrame::EvOcAppBorderSpaceSet(TRect far* 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%1 == 0 && pad->Attr.W != edge || // left & right edge
|
|
i%1 == 1 && pad->Attr.H != edge) { // top & bottom edge
|
|
TLayoutMetrics m;
|
|
GetChildLayoutMetrics(*pad, m);
|
|
pad->Attr.H = pad->Attr.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;
|
|
}
|
|
|
|
//
|
|
// Display the servers status messages on our status bar
|
|
//
|
|
void
|
|
TOleFrame::EvOcAppStatusText(const char far* text)
|
|
{
|
|
TMessageBar* mb = TYPESAFE_DOWNCAST(ChildWithId(IDW_STATUSBAR), TMessageBar);
|
|
if (mb) {
|
|
//mb->SetHintText(text); // is the text a hint, or general status??
|
|
#if defined(BI_DATA_NEAR)
|
|
mb->SetText(string(text).c_str());
|
|
#else
|
|
mb->SetText(text);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
//
|
|
// Save off the popup menu handles that are copies so that we can destroy
|
|
// them later. Keep track of active object menus, to allow cleanup when
|
|
// the last one goes away.
|
|
//
|
|
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(OwlOleMenu, 1, "Stashing " << hex << (uint)shMenuDescr.GetSubMenu(m+j));
|
|
StashedContainerPopups.AppendMenu(state, TMenu(shMenuDescr.GetSubMenu(m+j)), "");
|
|
}
|
|
m += shMenuDescr.GetGroupCount(i);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Throw away our popup copies that are no longer needed, when the last active
|
|
// object is deactivated.
|
|
//
|
|
void TOleFrame::DestroyStashedPopups()
|
|
{
|
|
if (--StashCount)
|
|
return;
|
|
|
|
while (StashedContainerPopups.GetMenuItemCount()) {
|
|
TRACEX(OwlOleMenu, 1, "Destroying " << hex << (uint)StashedContainerPopups.GetSubMenu(0));
|
|
StashedContainerPopups.DeleteMenu(0, MF_BYPOSITION);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Restore the UI back to pre-inplace server state. Remove the border space pads
|
|
// and restore the menu.
|
|
//
|
|
void
|
|
TOleFrame::EvOcAppRestoreUI()
|
|
{
|
|
// Only restore the old menu if we are holding a merged one
|
|
//
|
|
if (HOldMenu) {
|
|
TRACEX(OwlOleMenu, 0, "EvOcAppRestoreUI resetting Old " << hex <<
|
|
(uint)HOldMenu);
|
|
TMenu oleMenu(*this);
|
|
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);
|
|
}
|
|
|
|
//
|
|
// Called by TOcRemView::Close when last embedding is closed
|
|
// If that's the only reason the app is up we need to shut ourselves down
|
|
//
|
|
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
|
|
}
|
|
}
|