- 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>
900 lines
23 KiB
C++
900 lines
23 KiB
C++
//----------------------------------------------------------------------------
|
|
// ObjectWindows
|
|
// (C) Copyright 1991, 1994 by Borland International, All Rights Reserved
|
|
//
|
|
// Implementation of class TApplication. This defines the basic behavior
|
|
// for ObjectWindows applications.
|
|
//----------------------------------------------------------------------------
|
|
#pragma hdrignore SECTION
|
|
#include <owl/owlpch.h>
|
|
#include <owl/applicat.h>
|
|
#include <owl/framewin.h>
|
|
#include <owl/docmanag.h>
|
|
#include <owl/appdict.h>
|
|
|
|
#if defined(SECTION) && SECTION != 1
|
|
DIAG_DECLARE_GROUP(OwlApp); // General Application diagnostic group
|
|
#endif
|
|
|
|
#if !defined(SECTION) || SECTION == 1
|
|
|
|
DIAG_DEFINE_GROUP_INIT(OWL_INI, OwlApp, 1, 0);
|
|
|
|
//
|
|
// Static members for initialization
|
|
//
|
|
HINSTANCE TApplication::InitHInstance;
|
|
HINSTANCE TApplication::InitHPrevInstance;
|
|
string TApplication::InitCmdLine;
|
|
int TApplication::InitCmdShow;
|
|
|
|
//
|
|
// Global GetWindowPtr() message id used for registered message
|
|
//
|
|
uint _OWLDATA GetWindowPtrMsgId = 0;
|
|
|
|
//
|
|
// Constructor for use in OwlMain(). Gets members from statics set earlier by
|
|
// a call to InitWinMainParams() in Owl's WinMain.
|
|
//
|
|
TApplication::TApplication(const char far* name, TModule*& gModule,
|
|
TAppDictionary* appDict)
|
|
:
|
|
TModule(name, InitHInstance, InitCmdLine.c_str()),
|
|
XBase(0), XState(0), DocManager(0),
|
|
Dictionary(appDict ? appDict : &::OwlAppDictionary)
|
|
{
|
|
// Copy over values that were stashed in static members before this instance
|
|
// was constructed.
|
|
//
|
|
hPrevInstance = InitHPrevInstance;
|
|
nCmdShow = InitCmdShow;
|
|
|
|
MainWindow = 0;
|
|
HAccTable = 0;
|
|
BreakMessageLoop = false;
|
|
BWCCOn = Ctl3dOn = false;
|
|
BWCCModule = Ctl3dModule = 0;
|
|
Running = false;
|
|
CondemnedWindows = 0;
|
|
CmdLine = InitCmdLine;
|
|
|
|
Dictionary->Add(this);
|
|
gModule = this;
|
|
}
|
|
|
|
//
|
|
// Constructor for use in user defined WinMain() when all the args are
|
|
// available
|
|
//
|
|
TApplication::TApplication(const char far* name,
|
|
HINSTANCE instance,
|
|
HINSTANCE prevInstance,
|
|
const char far* cmdLine,
|
|
int cmdShow,
|
|
TModule*& gModule,
|
|
TAppDictionary* appDict)
|
|
:
|
|
TModule(name, instance, cmdLine),
|
|
XBase(0), XState(0), DocManager(0),
|
|
Dictionary(appDict ? appDict : &::OwlAppDictionary)
|
|
{
|
|
hPrevInstance = prevInstance;
|
|
nCmdShow = cmdShow;
|
|
MainWindow = 0;
|
|
HAccTable = 0;
|
|
BreakMessageLoop = false;
|
|
BWCCOn = Ctl3dOn = false;
|
|
BWCCModule = Ctl3dModule = 0;
|
|
Running = false;
|
|
CondemnedWindows = 0;
|
|
CmdLine = cmdLine;
|
|
|
|
Dictionary->Add(this);
|
|
gModule = this;
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
TApplication::~TApplication()
|
|
{
|
|
DeleteCondemned();
|
|
|
|
// Unregister ourselves from the Ctl3d DLL and/or the BWCC DLL if they are
|
|
// loaded.
|
|
//
|
|
if (Ctl3dModule) {
|
|
bool FAR PASCAL(*Ctl3dUnregister)(HANDLE);
|
|
(FARPROC)Ctl3dUnregister = Ctl3dModule->GetProcAddress("Ctl3dUnregister");
|
|
if (Ctl3dUnregister)
|
|
Ctl3dUnregister(*this);
|
|
delete Ctl3dModule;
|
|
}
|
|
if (BWCCModule) {
|
|
bool FAR PASCAL(*bwccIntlTerm)(void);
|
|
(FARPROC)bwccIntlTerm = BWCCModule->GetProcAddress("BWCCIntlTerm");
|
|
if (bwccIntlTerm)
|
|
bwccIntlTerm();
|
|
delete BWCCModule;
|
|
}
|
|
|
|
// Delete the main window if still present, may be destroyed but not deleted
|
|
// Set MainWindow to 0 to prevent calling ::PostQuitMessage
|
|
//
|
|
TWindow* mainWindow = SetMainWindow(0);
|
|
if (mainWindow) {
|
|
mainWindow->Destroy();
|
|
delete mainWindow;
|
|
}
|
|
|
|
delete (TStreamableBase*)DocManager;// cast to avoid ref to docmgr if not used
|
|
|
|
// Remove ourselves from the application dictionary that we are in
|
|
//
|
|
Dictionary->Remove(this);
|
|
delete XBase; // remove any exception copy
|
|
}
|
|
|
|
//
|
|
// Handle initialization for the first executing instance of the OWL
|
|
// application
|
|
//
|
|
void
|
|
TApplication::InitApplication()
|
|
{
|
|
}
|
|
|
|
//
|
|
// Handle initialization for each executing instance of the OWL
|
|
// application
|
|
//
|
|
// This implementation creates and displays the main window
|
|
//
|
|
void
|
|
TApplication::InitInstance()
|
|
{
|
|
InitMainWindow();
|
|
|
|
if (MainWindow) {
|
|
MainWindow->SetFlag(wfMainWindow);
|
|
MainWindow->Create();
|
|
MainWindow->Show(nCmdShow);
|
|
}
|
|
else
|
|
THROW( TXInvalidMainWindow() );
|
|
}
|
|
|
|
//
|
|
// Initialize the application's main window
|
|
//
|
|
// Default main window title is the same as the applications
|
|
//
|
|
void
|
|
TApplication::InitMainWindow()
|
|
{
|
|
SetMainWindow(new TFrameWindow(0, GetName()));
|
|
}
|
|
|
|
//
|
|
// Handle termination for each executing instance of the application
|
|
//
|
|
int
|
|
TApplication::TermInstance(int status)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Set (or reset) the main window. Returns, but does not destroy the
|
|
// previous main window.
|
|
//
|
|
TFrameWindow*
|
|
TApplication::SetMainWindow(TFrameWindow* window)
|
|
{
|
|
if (MainWindow)
|
|
MainWindow->ClearFlag(wfMainWindow);
|
|
TFrameWindow* oldMainWindow = MainWindow;
|
|
MainWindow = window;
|
|
if (MainWindow)
|
|
MainWindow->SetFlag(wfMainWindow);
|
|
return oldMainWindow;
|
|
}
|
|
|
|
//
|
|
// Sets (or resets) the document manager, returns the previous one if present
|
|
//
|
|
TDocManager*
|
|
TApplication::SetDocManager(TDocManager* docManager)
|
|
{
|
|
TDocManager* oldDocManager = DocManager;
|
|
DocManager = docManager;
|
|
return oldDocManager;
|
|
}
|
|
|
|
#if defined(BI_PLAT_WIN32)
|
|
//
|
|
// overrides TEventHandler::Dispatch() to handle multi-thread synchonization
|
|
//
|
|
LRESULT TApplication::Dispatch(TEventInfo& info, WPARAM wp, LPARAM lp)
|
|
{
|
|
TApplication::TAppLock Lock(*this);
|
|
return TModule::Dispatch(info, wp, lp);
|
|
}
|
|
|
|
int TApplication::TAppMutex::NotWIN32s =
|
|
!( (::GetVersion()&0x80000000) && (::GetVersion()&0xFF) < 4);
|
|
#endif
|
|
|
|
//
|
|
// default idle action is to forward the action to the main window.
|
|
//
|
|
bool
|
|
TApplication::IdleAction(long idleCount)
|
|
{
|
|
if (MainWindow)
|
|
return MainWindow->IdleAction(idleCount);
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Run this application, return when finished
|
|
//
|
|
// Initializes instances, creating and displaying their main window (calls
|
|
// InitApplication for the first executing instance and calls InitInstance for
|
|
// all instances). Runs the application's message loop. Each of the virtual
|
|
// functions called are expected to throw an exception if there is an error.
|
|
//
|
|
int
|
|
TApplication::Run()
|
|
{
|
|
int status;
|
|
TRY {
|
|
if (!MainWindow) {
|
|
if (!hPrevInstance)
|
|
InitApplication();
|
|
InitInstance();
|
|
}
|
|
Running = true;
|
|
status = MessageLoop();
|
|
}
|
|
CATCH( (TXOwl& x) {status = x.Unhandled(this, 0);})
|
|
CATCH( (xmsg& x) {status = Error(x, 0);})
|
|
CATCH( (Bad_cast& x) {status = Error(xmsg(string(typeid(x).name())), 0);} )
|
|
CATCH( (Bad_typeid& x) {status = Error(xmsg(string(typeid(x).name())), 0);} )
|
|
|
|
MSG msg;
|
|
while (::PeekMessage(&msg,0,0,0,PM_NOYIELD|PM_REMOVE)
|
|
&& msg.message != WM_PAINT) ; // flush queue
|
|
|
|
Running = false;
|
|
return TermInstance(status);
|
|
}
|
|
|
|
//
|
|
// Start this application and return immediately
|
|
//
|
|
// Initializes instances, creating and displaying their main window (calls
|
|
// InitApplication for the first executing instance and calls InitInstance for
|
|
// all instances). Each of the virtual functions called are expected to throw an
|
|
// exception if there is an error. Does not run message loop.
|
|
//
|
|
int
|
|
TApplication::Start()
|
|
{
|
|
int status = 0;
|
|
TRY {
|
|
if (!hPrevInstance)
|
|
InitApplication();
|
|
InitInstance();
|
|
}
|
|
CATCH( (TXOwl& x) {status = x.Unhandled(this, 0);})
|
|
CATCH( (xmsg& x) {status = Error(x, 0);})
|
|
CATCH( (Bad_cast& x) {status = Error(xmsg(string(typeid(x).name())), 0);} )
|
|
CATCH( (Bad_typeid& x) {status = Error(xmsg(string(typeid(x).name())), 0);} )
|
|
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Called after each message is pulled from the queue, and before it is
|
|
// dispatched. Return true if the message was handled completely here.
|
|
//
|
|
bool
|
|
TApplication::ProcessAppMsg(MSG& msg)
|
|
{
|
|
// start with the window that the event was destined for and allow it
|
|
// and its parent (and its parent...) an opportunity to preprocess the
|
|
// event
|
|
//
|
|
for (HWND hWnd = msg.hwnd; hWnd; hWnd = ::GetParent(hWnd)) {
|
|
TWindow* win = GetWindowPtr(hWnd);
|
|
|
|
if (win && win->PreProcessMsg(msg))
|
|
return true;
|
|
}
|
|
|
|
// for compatability with OWL 1.0, check the application's accelerator
|
|
// table if it is being used
|
|
//
|
|
// NOTE: we should only support this for one rev...
|
|
//
|
|
if (HAccTable && MainWindow)
|
|
return ::TranslateAccelerator(MainWindow->HWindow, HAccTable, &msg);
|
|
|
|
return false;
|
|
}
|
|
|
|
//
|
|
// Called after each message dispatch when TApplication's message loop is not
|
|
// being used.
|
|
//
|
|
void
|
|
TApplication::PostDispatchAction()
|
|
{
|
|
DeleteCondemned();
|
|
ResumeThrow();
|
|
|
|
MSG msg;
|
|
if (!::PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE))
|
|
IdleAction(0);
|
|
}
|
|
|
|
//
|
|
// Lowlevel message loop: retrieves and processes messages from the OWL
|
|
// application's message queue until it is empty
|
|
//
|
|
// calls ProcessAppMsg() to allow special pre-handling of the message
|
|
//
|
|
bool
|
|
TApplication::PumpWaitingMessages()
|
|
{
|
|
MSG msg;
|
|
bool foundOne = false;
|
|
|
|
while (::PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
|
|
foundOne = true;
|
|
if (msg.message == WM_QUIT) {
|
|
BreakMessageLoop = true;
|
|
MessageLoopResult = msg.wParam;
|
|
::PostQuitMessage(msg.wParam); // make sure all loops exit
|
|
break;
|
|
}
|
|
|
|
// Let the app preprocess the message. If it is not eaten, then translate
|
|
// it, & dispatch it. If no hwnd, then first find/dispatch it directly to
|
|
// the app (for PostAppMessage() functionality)
|
|
//
|
|
if (!ProcessAppMsg(msg)) {
|
|
::TranslateMessage(&msg);
|
|
if (!msg.hwnd) {
|
|
TEventInfo cmdEventInfo(msg.message, msg.wParam);
|
|
if (Find(cmdEventInfo)) {
|
|
Dispatch(cmdEventInfo, msg.wParam, msg.lParam);
|
|
return true;
|
|
}
|
|
else {
|
|
TEventInfo eventInfo(msg.message);
|
|
if (Find(eventInfo)) {
|
|
Dispatch(eventInfo, msg.wParam, msg.lParam);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
::DispatchMessage(&msg);
|
|
DeleteCondemned();
|
|
ResumeThrow();
|
|
}
|
|
}
|
|
return foundOne;
|
|
}
|
|
|
|
//
|
|
// Suspend throw functions store away a caught exception to allow foreign code
|
|
// to be re-entered. Call ResumeThrow() to rethrow the exception, if any, upon
|
|
// return from the foreign code.
|
|
//
|
|
void
|
|
TApplication::SuspendThrow(xalloc& x)
|
|
{
|
|
XString = x.why();
|
|
XSize = x.requested();
|
|
XState |= xsAlloc;
|
|
}
|
|
|
|
void
|
|
TApplication::SuspendThrow(xmsg& x)
|
|
{
|
|
XString = x.why();
|
|
XState |= xsMsg;
|
|
}
|
|
|
|
void
|
|
TApplication::SuspendThrow(TXBase& x)
|
|
{
|
|
delete XBase; // remove any previous exception
|
|
XBase = x.Clone();
|
|
XState |= xsBase;
|
|
}
|
|
|
|
void
|
|
TApplication::SuspendThrow(int flag)
|
|
{
|
|
XState |= (flag & (xsBadCast | xsBadTypeid | xsUnknown));
|
|
}
|
|
|
|
void
|
|
TApplication::ResumeThrow()
|
|
{
|
|
if (XState) {
|
|
if (XState & xsBase) {
|
|
XState &= ~xsBase;
|
|
XBase->Throw(); // must be deleted later
|
|
}
|
|
if (XState & xsAlloc) {
|
|
XState &= ~xsAlloc;
|
|
THROW( xalloc(XString,XSize) );
|
|
}
|
|
if (XState & xsMsg) {
|
|
XState &= ~xsMsg;
|
|
THROW( xmsg(XString) );
|
|
}
|
|
if (XState & xsUnknown) {
|
|
XState &= ~xsUnknown;
|
|
THROW( xmsg(string()) );
|
|
}
|
|
if (XState & xsBadCast) {
|
|
XState &= ~xsBadCast;
|
|
THROW( Bad_cast() );
|
|
}
|
|
if (XState & xsBadTypeid) {
|
|
XState &= ~xsBadTypeid;
|
|
THROW( Bad_typeid() );
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// General message loop: retrieves and processes messages from the OWL
|
|
// application's message queue until BreakMessageLoop becomes true
|
|
//
|
|
// Calls IdleAction() when there are no messages
|
|
//
|
|
int
|
|
TApplication::MessageLoop()
|
|
{
|
|
long idleCount = 0;
|
|
|
|
MessageLoopResult = 0;
|
|
while (!BreakMessageLoop) {
|
|
TRY {
|
|
if (!IdleAction(idleCount++))
|
|
::WaitMessage(); // allow system to go idle
|
|
if (PumpWaitingMessages()) // pumps any waiting messages
|
|
idleCount = 0;
|
|
}
|
|
CATCH( (TXOwl& x) {
|
|
MessageLoopResult = x.Unhandled(this, IDS_OKTORESUME);
|
|
if (MessageLoopResult != 0) {
|
|
::PostQuitMessage(MessageLoopResult);
|
|
break;
|
|
}
|
|
})
|
|
CATCH( (xmsg& x) {
|
|
MessageLoopResult = Error(x, 0, IDS_OKTORESUME);
|
|
if (MessageLoopResult != 0) {
|
|
::PostQuitMessage(MessageLoopResult);
|
|
break;
|
|
}
|
|
})
|
|
CATCH( (Bad_cast& x) {
|
|
MessageLoopResult = Error(xmsg(string(typeid(x).name())), 0, IDS_OKTORESUME);
|
|
if (MessageLoopResult != 0) {
|
|
::PostQuitMessage(MessageLoopResult);
|
|
break;
|
|
}
|
|
})
|
|
CATCH( (Bad_typeid& x) {
|
|
MessageLoopResult = Error(xmsg(string(typeid(x).name())), 0, IDS_OKTORESUME);
|
|
if (MessageLoopResult != 0) {
|
|
::PostQuitMessage(MessageLoopResult);
|
|
break;
|
|
}
|
|
})
|
|
}
|
|
BreakMessageLoop = false;
|
|
return MessageLoopResult;
|
|
}
|
|
|
|
//
|
|
// Default event handling simply defers to the DocManager if it has been
|
|
// installed
|
|
//
|
|
bool
|
|
TApplication::Find(TEventInfo &eventInfo, TEqualOperator equal)
|
|
{
|
|
return DocManager ? DocManager->Find(eventInfo, equal) : false;
|
|
}
|
|
|
|
//
|
|
// Determines whether the application can be closed.
|
|
// Makes sure the MainWindow can close & doc manager can close.
|
|
//
|
|
bool
|
|
TApplication::CanClose()
|
|
{
|
|
TEventInfo eventInfo(WM_OWLCANCLOSE);
|
|
return (!MainWindow || MainWindow->CanClose())
|
|
&& (!DocManager ||!DocManager->Find(eventInfo)
|
|
|| DocManager->Dispatch(eventInfo, 0, 0));
|
|
}
|
|
|
|
//
|
|
// Oportunity to preprocess the main window's menubar before it is installed.
|
|
// Normally delegated to the doc manager to install a file menu as needed
|
|
//
|
|
void
|
|
TApplication::PreProcessMenu(HMENU fmenu)
|
|
{
|
|
TEventInfo eventInfo(WM_OWLPREPROCMENU);
|
|
if (DocManager && DocManager->Find(eventInfo)) {
|
|
DocManager->Dispatch(eventInfo, (WPARAM)fmenu, 0);
|
|
MainWindow->DrawMenuBar();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Condemn a window to be deleted the at the next available safe time.
|
|
// Adds the window to a normal single linked list
|
|
//
|
|
// Condemned windows should be removed if they are destructed in the mean time
|
|
// thru some other mechanism (i.e. stack, aggregate, etc)
|
|
//
|
|
void
|
|
TApplication::Condemn(TWindow* win)
|
|
{
|
|
TRACEX(OwlApp, 1, "Condemning window: " << *win);
|
|
win->SetParent(0);
|
|
win->SetNext(CondemnedWindows);
|
|
CondemnedWindows = win;
|
|
}
|
|
|
|
//
|
|
// Remove a condemned window from the list.
|
|
//
|
|
void
|
|
TApplication::Uncondemn(TWindow* win)
|
|
{
|
|
if (win && CondemnedWindows) {
|
|
TWindow* w = 0;
|
|
if (CondemnedWindows != win)
|
|
for (w = CondemnedWindows; w->Next() != win; w = w->Next())
|
|
if (!w->Next())
|
|
return;
|
|
|
|
TRACEX(OwlApp, 1, "Uncondemning window: " << *win);
|
|
if (w)
|
|
w->SetNext(win->Next());
|
|
else
|
|
CondemnedWindows = win->Next();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Walk the condemned window list & delete each window. Assumes that the
|
|
// windows were constructed using 'new'
|
|
//
|
|
void
|
|
TApplication::DeleteCondemned()
|
|
{
|
|
while (CondemnedWindows) {
|
|
TRACEX(OwlApp, 1, "Deleting condemned window: " << *CondemnedWindows);
|
|
TWindow* next = CondemnedWindows->Next();
|
|
delete CondemnedWindows;
|
|
CondemnedWindows = next;
|
|
}
|
|
}
|
|
|
|
TApplication::TXInvalidMainWindow::TXInvalidMainWindow()
|
|
:
|
|
TXOwl(IDS_INVALIDMAINWINDOW)
|
|
{
|
|
}
|
|
|
|
TApplication::TXInvalidMainWindow::~TXInvalidMainWindow()
|
|
{
|
|
}
|
|
|
|
TApplication::TXInvalidMainWindow*
|
|
TApplication::TXInvalidMainWindow::Clone()
|
|
{
|
|
return new TXInvalidMainWindow(*this);
|
|
}
|
|
|
|
void
|
|
TApplication::TXInvalidMainWindow::Throw()
|
|
{
|
|
THROW( *this );
|
|
}
|
|
|
|
#endif
|
|
//----------------------------------------------------------------------------
|
|
#if !defined(SECTION) || SECTION == 2
|
|
|
|
struct TEnumInfo {
|
|
HWND ModalWnd; // The window being made modal if needed
|
|
short Count; // Count of windows in Wnds below
|
|
HWND* Wnds; // list of windows that were disabled
|
|
};
|
|
static TEnumInfo* stackTop = 0; // points to topmost enuminfo in BeginModal calls
|
|
|
|
//
|
|
// Enum[Thread/Task]Windows callback. Counts or disables given window based on
|
|
// whether or not window list has been allocated yet.
|
|
//
|
|
bool CALLBACK __export
|
|
DisableWnds(HWND wnd, TEnumInfo far* ei)
|
|
{
|
|
if (!(::GetWindowLong(wnd, GWL_STYLE) & WS_CHILD)) {
|
|
if (wnd != ei->ModalWnd && ::IsWindowEnabled(wnd)) {
|
|
if (!ei->Wnds) {
|
|
ei->Count++; // no buffer yet, we are just counting
|
|
}
|
|
else {
|
|
*(ei->Wnds++) = wnd;
|
|
::EnableWindow(wnd, false);
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Terminate the modal state initiated by BeginModal. Needs topmost ei block,
|
|
// and cleans the block up as needed inorder to be safe to be called twice.
|
|
//
|
|
static void termModal(TEnumInfo& ei)
|
|
{
|
|
//
|
|
// Re-enable window(s) that are disabled in BeginModal()
|
|
//
|
|
if (ei.Wnds) {
|
|
for (HWND* wnd = ei.Wnds; *wnd; wnd++)
|
|
::EnableWindow(*wnd, true);
|
|
delete[] ei.Wnds;
|
|
ei.Wnds = 0;
|
|
}
|
|
else {
|
|
if (ei.ModalWnd && IsWindow(ei.ModalWnd)) {
|
|
::EnableWindow(ei.ModalWnd, true);
|
|
ei.ModalWnd = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Go modal and enter a message loop until a quit message goes by.
|
|
// the flag values determine how window is used:
|
|
// MB_APPLMODAL - window is the owner to disable (if 0, none are disabled)
|
|
// MB_TASKMODAL - window is the window to make modal,
|
|
// all other top level windows are disabled
|
|
// MB_SYSTEMMODAL - window is the window to make system modal
|
|
// returns -1 on errors
|
|
//
|
|
int
|
|
TApplication::BeginModal(TWindow* window, int flags)
|
|
{
|
|
TEnumInfo ei = { 0, 0, 0 };
|
|
TEnumInfo* lastStackTop = stackTop; // keep last stackTop to restore later
|
|
stackTop = &ei; // point stackTop to topmost ei
|
|
|
|
// Set modal state
|
|
//
|
|
if (flags & MB_TASKMODAL) {
|
|
|
|
if (window)
|
|
ei.ModalWnd = window->HWindow; // don't disable the modal window if exists
|
|
|
|
// count windows to disable
|
|
//
|
|
#if defined(BI_PLAT_WIN32)
|
|
if (!EnumThreadWindows(GetCurrentThreadId(), (WNDENUMPROC)DisableWnds,
|
|
(LPARAM)(TEnumInfo far*)&ei))
|
|
return -1;
|
|
#else
|
|
if (!EnumTaskWindows(GetCurrentTask(), (WNDENUMPROC)DisableWnds,
|
|
(LPARAM)(TEnumInfo far*)&ei))
|
|
return -1;
|
|
#endif
|
|
|
|
// Allocate list of windows to disable, disable windows that are
|
|
// enabled and then stuff them into the list.
|
|
//
|
|
HWND* wnds = ei.Wnds = new HWND[ei.Count + 1];
|
|
memset(ei.Wnds, 0, sizeof(HWND)*(ei.Count + 1));
|
|
|
|
#if defined(BI_PLAT_WIN32)
|
|
EnumThreadWindows(GetCurrentThreadId(), (WNDENUMPROC)DisableWnds,
|
|
(LPARAM)(TEnumInfo far*)&ei);
|
|
#else
|
|
EnumTaskWindows(GetCurrentTask(), (WNDENUMPROC)DisableWnds,
|
|
(LPARAM)(TEnumInfo far*)&ei);
|
|
#endif
|
|
|
|
ei.Wnds = wnds; // restore alloc'd pointer since enumerator bumps it
|
|
}
|
|
#if defined(BI_PLAT_WIN16)
|
|
else if (flags & MB_SYSTEMMODAL)
|
|
window->SetSysModalWindow();
|
|
#endif
|
|
else if (window) {
|
|
ei.ModalWnd = window->HWindow; // remember who to re-enable later
|
|
window->EnableWindow(false);
|
|
}
|
|
|
|
// Enter message loop, saving & restoring the current status & getting the
|
|
// returned result.
|
|
//
|
|
int result;
|
|
TRY {
|
|
result = MessageLoop();
|
|
}
|
|
CATCH( (...) {
|
|
termModal(ei);
|
|
stackTop = lastStackTop;
|
|
RETHROW;
|
|
})
|
|
stackTop = lastStackTop;
|
|
termModal(ei); // just in case termModal was missed in EndModal
|
|
|
|
// return the result from the modal window's EndModal call
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Cause the current modal message loop to break and have it return a result
|
|
// Re-enable the disabled windows here, if the EnumInfo is available.
|
|
//
|
|
void
|
|
TApplication::EndModal(int result)
|
|
{
|
|
MessageLoopResult = result;
|
|
BreakMessageLoop = true;
|
|
if (stackTop)
|
|
termModal(*stackTop);
|
|
}
|
|
|
|
#endif
|
|
//----------------------------------------------------------------------------
|
|
#if !defined(SECTION) || SECTION == 3
|
|
|
|
//
|
|
// Predefined DLLs that TApplication knows how to find.
|
|
//
|
|
#if defined(BI_PLAT_WIN32)
|
|
const char BwccDllName[] = "BWCC32.DLL";
|
|
const char Ctl3dDllName[] = "CTL3D32.DLL";
|
|
#elif defined(BI_PLAT_WIN16)
|
|
const char BwccDllName[] = "BWCC.DLL";
|
|
const char Ctl3dDllName[] = "CTL3DV2.DLL";
|
|
#endif
|
|
|
|
//
|
|
// Load the BWCC DLL if needed & set the BWCC on flag according to 'enable'
|
|
// Library is not free'd on disable to reduce re-loads if enabling on the fly
|
|
//
|
|
void
|
|
TApplication::EnableBWCC(bool enable, uint language)
|
|
{
|
|
if (enable) {
|
|
if (!BWCCModule) {
|
|
BWCCModule = new TModule(BwccDllName, true);
|
|
|
|
bool FAR PASCAL(*bwccIntlInit)(uint);
|
|
(FARPROC)bwccIntlInit = BWCCModule->GetProcAddress("BWCCIntlInit");
|
|
if (bwccIntlInit)
|
|
bwccIntlInit(language);
|
|
|
|
bool FAR PASCAL(*bwccRegister)(HINSTANCE);
|
|
(FARPROC)bwccRegister = BWCCModule->GetProcAddress("BWCCRegister");
|
|
if (bwccRegister)
|
|
bwccRegister(GetInstance());
|
|
}
|
|
BWCCOn = true;
|
|
}
|
|
else
|
|
BWCCOn = false;
|
|
}
|
|
|
|
//
|
|
// enable or disable the use of the CTL3D DLL. Loads it if needed.
|
|
//
|
|
void
|
|
TApplication::EnableCtl3d(bool enable)
|
|
{
|
|
//
|
|
// If we are running Win4.0 or greater, then ctl3d is unnecessary
|
|
//
|
|
#if defined(BI_PLAT_WIN32)
|
|
if ((::GetVersion()&0xFF) >= 4)
|
|
#else
|
|
if ((::GetVersion()&0xFF) >= 3 && ((::GetVersion()>>8)&0xFF) >= 95)
|
|
#endif
|
|
return;
|
|
|
|
//
|
|
// Load the Ctl3d DLL if needed & register our instance
|
|
//
|
|
if (enable) {
|
|
if (!Ctl3dModule) {
|
|
Ctl3dModule = new TModule(Ctl3dDllName, true);
|
|
bool FAR PASCAL(*ctl3dRegister)(HANDLE);
|
|
(FARPROC)ctl3dRegister = Ctl3dModule->GetProcAddress("Ctl3dRegister");
|
|
if (ctl3dRegister)
|
|
ctl3dRegister(*this);
|
|
else {
|
|
delete Ctl3dModule;
|
|
Ctl3dModule = 0;
|
|
return; // could not register--don't change Ctl3dOn flag
|
|
}
|
|
}
|
|
}
|
|
Ctl3dOn = enable;
|
|
}
|
|
|
|
//
|
|
// enable or disable CTL3D's use of auto-subclassing. Turn on autosubclassing
|
|
// before creating a non-owl dialog to make it 3d, & turn it off immediatly
|
|
// after.
|
|
//
|
|
void
|
|
TApplication::EnableCtl3dAutosubclass(bool enable)
|
|
{
|
|
if (Ctl3dEnabled()) {
|
|
if (enable) {
|
|
bool FAR PASCAL(*ctl3dRegister)(HANDLE);
|
|
(FARPROC)ctl3dRegister = Ctl3dModule->GetProcAddress("Ctl3dRegister");
|
|
if (ctl3dRegister) {
|
|
ctl3dRegister(*this);
|
|
|
|
bool FAR PASCAL(*ctl3dAutoSubclass)(HANDLE);
|
|
(FARPROC)ctl3dAutoSubclass = Ctl3dModule->GetProcAddress("Ctl3dAutoSubclass");
|
|
if (ctl3dAutoSubclass)
|
|
ctl3dAutoSubclass(*this);
|
|
}
|
|
}
|
|
else {
|
|
bool FAR PASCAL(*Ctl3dUnregister)(HANDLE);
|
|
(FARPROC)Ctl3dUnregister = Ctl3dModule->GetProcAddress("Ctl3dUnregister");
|
|
if (Ctl3dUnregister)
|
|
Ctl3dUnregister(*this);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
//----------------------------------------------------------------------------
|
|
#if !defined(SECTION) || SECTION == 4
|
|
|
|
IMPLEMENT_STREAMABLE1(TApplication, TModule);
|
|
|
|
void*
|
|
TApplication::Streamer::Read(ipstream& is, uint32 /*version*/) const
|
|
{
|
|
TApplication* o = GetObject();
|
|
if (o != ::Module)
|
|
is >> *::Module; // set reference to OWL module
|
|
return o;
|
|
}
|
|
|
|
void
|
|
TApplication::Streamer::Write(opstream& os) const
|
|
{
|
|
TApplication* o = GetObject();
|
|
if (o != ::Module)
|
|
os << *::Module; // write out reference to OWL module, no data written
|
|
}
|
|
|
|
#endif // SECTION
|