diff options
Diffstat (limited to 'qtwinmigrate/qmfcapp.cpp')
-rw-r--r-- | qtwinmigrate/qmfcapp.cpp | 392 |
1 files changed, 392 insertions, 0 deletions
diff --git a/qtwinmigrate/qmfcapp.cpp b/qtwinmigrate/qmfcapp.cpp new file mode 100644 index 0000000..79d68f2 --- /dev/null +++ b/qtwinmigrate/qmfcapp.cpp @@ -0,0 +1,392 @@ +// Implementation of the QMfcApp classes + +#ifdef QT3_SUPPORT +#undef QT3_SUPPORT +#endif + +#ifdef UNICODE +#undef UNICODE +#endif + +#include "qmfcapp.h" + +#include <QtCore/QEventLoop> +#include <QtCore/QAbstractEventDispatcher> +#include <QtGui/QWidget> + +#ifdef QTWINMIGRATE_WITHMFC +#include <afxwin.h> +#else +#include <qt_windows.h> +#endif + +#ifdef QTWINMIGRATE_WITHMFC +CWinApp *QMfcApp::mfc_app = 0; +char **QMfcApp::mfc_argv = 0; +int QMfcApp::mfc_argc = 0; +#endif + +/*! \class QMfcApp qmfcapp.h + \brief The QMfcApp class provides merging of the MFC and Qt event loops. + + QMfcApp is responsible for driving both the Qt and MFC event loop. + It replaces the standard MFC event loop provided by + CWinApp::Run(), and is used instead of the QApplication parent + class. + + To replace the MFC event loop reimplement the CWinApp::Run() + function in the CWinApp subclass usually created by the MFC + Application Wizard, and use either the static run() function, or + an instance of QMfcApp created earlier through the static + instance() function or the constructor. + + The QMfcApp class also provides a static API pluginInstance() that + drives the Qt event loop when loaded into an MFC or Win32 application. + This is useful for developing Qt based DLLs or plugins, or if the + MFC application's event handling can not be modified. +*/ + +static int modalLoopCount = 0; + +HHOOK hhook; +LRESULT CALLBACK QtFilterProc(int nCode, WPARAM wParam, LPARAM lParam) +{ + if (qApp) { + // don't process deferred-deletes while in a modal loop + if (modalLoopCount) + qApp->sendPostedEvents(); + else + qApp->sendPostedEvents(0, -1); + } + + return CallNextHookEx(hhook, nCode, wParam, lParam); +} + +/*! + Inform Qt that a modal loop is about to be entered, and that DeferredDelete + events should not be processed. Call this function before calling Win32 + or MFC functions that enter a modal event loop (i.e. MessageBox). + + This is only required if the Qt UI code hooks into an existing Win32 + event loop using QMfcApp::pluginInstance. + + \sa exitModalLoop() +*/ +void QMfcApp::enterModalLoop() +{ + ++modalLoopCount; +} + +/*! + Inform Qt that a modal loop has been exited, and that DeferredDelete + events should not be processed. Call this function after the blocking + Win32 or MFC function (i.e. MessageBox) returned. + + This is only required if the Qt UI code hooks into an existing Win32 + event loop using QMfcApp::pluginInstance. + + \sa enterModalLoop() +*/ +void QMfcApp::exitModalLoop() +{ + --modalLoopCount; + Q_ASSERT(modalLoopCount >= 0); +} + +/*! + If there is no global QApplication object (i.e. qApp is null) this + function creates a QApplication instance and returns true; + otherwise it does nothing and returns false. + + The application installs an event filter that drives the Qt event + loop while the MFC or Win32 application continues to own the event + loop. + + Use this static function if the application event loop code can not be + easily modified, or when developing a plugin or DLL that will be loaded + into an existing Win32 or MFC application. If \a plugin is non-null then + the function loads the respective DLL explicitly to avoid unloading from + memory. + + \code + BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpvReserved) + { + if (dwReason == DLL_PROCESS_ATTACH) + QMfcApp::pluginInstance(hInstance); + + return TRUE; + } + \endcode + + Set \a plugin to 0 when calling this function from within the same executable + module. + + If this function is used, call enterModalLoop and exitModalLoop whenever you + call a Win32 or MFC function that opens a local event loop. + + \code + void Dialog::someSlot() + { + QMfcApp::enterModalLoop(); + MessageBox(...); + QMfcApp::exitModalLoop(); + } + \endcode +*/ +bool QMfcApp::pluginInstance(Qt::HANDLE plugin) +{ + if (qApp) + return FALSE; + + QT_WA({ + hhook = SetWindowsHookExW(WH_GETMESSAGE, QtFilterProc, 0, GetCurrentThreadId()); + }, { + hhook = SetWindowsHookExA(WH_GETMESSAGE, QtFilterProc, 0, GetCurrentThreadId()); + }); + + int argc = 0; + (void)new QApplication(argc, 0); + + if (plugin) { + char filename[256]; + if (GetModuleFileNameA((HINSTANCE)plugin, filename, 255)) + LoadLibraryA(filename); + } + + return TRUE; +} + +#ifdef QTWINMIGRATE_WITHMFC +/*! + Runs the event loop for both Qt and the MFC application object \a + mfcApp, and returns the result. This function calls \c instance() + if no QApplication object exists and deletes the object it + created. + + Calling this static function in a reimplementation of + CWinApp::Run() is the simpliest way to use the QMfcApp class: + + \code + int MyMfcApp::Run() + { + return QMfcApp::run(this); + } + \endcode + + Since a QApplication object must exist before Qt widgets can be + created you cannot use this function if you want to use Qt-based + user interface elements in, for example, the InitInstance() + function of CWinApp. In such cases, create an instance of + QApplication explicitly using instance() or the constructor. + + \sa instance() +*/ +int QMfcApp::run(CWinApp *mfcApp) +{ + bool ownInstance = !qApp; + if (ownInstance) + instance(mfcApp); + int result = qApp->exec(); + + if (mfcApp) { + int mfcRes = mfcApp->ExitInstance(); + if (mfcRes && !result) + result = mfcRes; + } + + if (ownInstance) + delete qApp; + + return result; +} + +/*! + Creates an instance of QApplication, passing the command line of + \a mfcApp to the QApplication constructor, and returns the new + object. The returned object must be destroyed by the caller. + + Use this static function if you want to perform additional + initializations after creating the application object, or if you + want to create Qt GUI elements in the InitInstance() + reimplementation of CWinApp: + + \code + BOOL MyMfcApp::InitInstance() + { + // standard MFC initialization + // ... + + // This sets the global qApp pointer + QMfcApp::instance(this); + + // Qt GUI initialization + } + + BOOL MyMfcApp::Run() + { + int result = QMfcApp::run(this); + delete qApp; + return result; + } + \endcode + + \sa run() +*/ +QApplication *QMfcApp::instance(CWinApp *mfcApp) +{ + mfc_app = mfcApp; + if (mfc_app) { +#if defined(UNICODE) + QString exeName((QChar*)mfc_app->m_pszExeName, wcslen(mfc_app->m_pszExeName)); + QString cmdLine((QChar*)mfc_app->m_lpCmdLine, wcslen(mfc_app->m_lpCmdLine)); +#else + QString exeName = QString::fromLocal8Bit(mfc_app->m_pszExeName); + QString cmdLine = QString::fromLocal8Bit(mfc_app->m_lpCmdLine); +#endif + QStringList arglist = QString(exeName + " " + cmdLine).split(' '); + + mfc_argc = arglist.count(); + mfc_argv = new char*[mfc_argc+1]; + int a; + for (a = 0; a < mfc_argc; ++a) { + QString arg = arglist[a]; + mfc_argv[a] = new char[arg.length()+1]; + qstrcpy(mfc_argv[a], arg.toLocal8Bit().data()); + } + mfc_argv[a] = 0; + } + + return new QMfcApp(mfcApp, mfc_argc, mfc_argv); +} + + +static bool qmfc_eventFilter(void *message) +{ + long result = 0; + return static_cast<QMfcApp*>(qApp)->winEventFilter((MSG*)message, &result); +} + +/*! + Creates an instance of QMfcApp. \a mfcApp must point to the + existing instance of CWinApp. \a argc and \a argv are passed on + to the QApplication constructor. + + Use the static function instance() to automatically use the + command line passed to the CWinApp. + + \code + QMfcApp *qtApp; + + BOOL MyMfcApp::InitInstance() + { + // standard MFC initialization + + int argc = ... + char **argv = ... + + qtApp = new QMfcApp(this, argc, argv); + + // Qt GUI initialization + } + + BOOL MyMfcApp::Run() + { + int result = qtApp->exec(); + delete qtApp; + qtApp = 0; + + return result; + } + \endcode + + \sa instance() run() +*/ +QMfcApp::QMfcApp(CWinApp *mfcApp, int &argc, char **argv) +: QApplication(argc, argv), idleCount(0), doIdle(FALSE) +{ + mfc_app = mfcApp; + QAbstractEventDispatcher::instance()->setEventFilter(qmfc_eventFilter); + setQuitOnLastWindowClosed(false); +} +#endif + +/*! + Destroys the QMfcApp object, freeing all allocated resources. +*/ +QMfcApp::~QMfcApp() +{ + if (hhook) { + UnhookWindowsHookEx(hhook); + hhook = 0; + } + +#ifdef QTWINMIGRATE_WITHMFC + for (int a = 0; a < mfc_argc; ++a) { + char *arg = mfc_argv[a]; + delete[] arg; + } + delete []mfc_argv; + + mfc_argc = 0; + mfc_argv = 0; + mfc_app = 0; +#endif +} + +/*! + \reimp +*/ +bool QMfcApp::winEventFilter(MSG *msg, long *result) +{ + static bool recursion = false; + if (recursion) + return false; + + recursion = true; + + QWidget *widget = QWidget::find(msg->hwnd); + HWND toplevel = 0; + if (widget) { + HWND parent = widget->winId(); + while(parent) { + toplevel = parent; + parent = GetParent(parent); + } + HMENU menu = toplevel ? GetMenu(toplevel) : 0; + if (menu && GetFocus() == msg->hwnd) { + if (msg->message == WM_SYSKEYUP && msg->wParam == VK_MENU) { + // activate menubar on Alt-up and move focus away + SetFocus(toplevel); + SendMessage(toplevel, msg->message, msg->wParam, msg->lParam); + widget->setFocus(); + recursion = false; + return TRUE; + } else if (msg->message == WM_SYSKEYDOWN && msg->wParam != VK_MENU) { + SendMessage(toplevel, msg->message, msg->wParam, msg->lParam); + SendMessage(toplevel, WM_SYSKEYUP, VK_MENU, msg->lParam); + recursion = false; + return TRUE; + } + } + } +#ifdef QTWINMIGRATE_WITHMFC + else if (mfc_app) { + MSG tmp; + while (doIdle && !PeekMessage(&tmp, 0, 0, 0, PM_NOREMOVE)) { + if (!mfc_app->OnIdle(idleCount++)) + doIdle = FALSE; + } + if (mfc_app->IsIdleMessage(msg)) { + doIdle = TRUE; + idleCount = 0; + } + } + if (mfc_app && mfc_app->PreTranslateMessage(msg)) { + recursion = false; + return TRUE; + } +#endif + + recursion = false; + return QApplication::winEventFilter(msg, result); +} |