diff --git a/build-qt/HGSolution/HGSaneUI/HGSaneUI.pro b/build-qt/HGSolution/HGSaneUI/HGSaneUI.pro index 4b59cede..91e2e8ae 100644 --- a/build-qt/HGSolution/HGSaneUI/HGSaneUI.pro +++ b/build-qt/HGSolution/HGSaneUI/HGSaneUI.pro @@ -1,6 +1,6 @@ QT += core gui -greaterThan(QT_MAJOR_VERSION, 4): QT += widgets +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets gui-private TEMPLATE = lib @@ -42,9 +42,18 @@ if (contains(DEFINES, OEM_HANWANG) || contains(DEFINES, OEM_LISICHENG) || contai } win32 { + SOURCES += ../../../modules/saneui/dllmain.cpp + SOURCES += ../../../modules/saneui/qmfcapp.cpp + SOURCES += ../../../modules/saneui/qwinhost.cpp + SOURCES += ../../../modules/saneui/qwinwidget.cpp + + HEADERS += ../../../modules/saneui/qmfcapp.h + HEADERS += ../../../modules/saneui/qwinhost.h + HEADERS += ../../../modules/saneui/qwinwidget.h + DEF_FILE = HGSaneUI.def DEFINES += _CRT_SECURE_NO_WARNINGS - LIBS += -ladvapi32 + LIBS += -ladvapi32 -luser32 if (contains(DEFINES, OEM_HANWANG) || contains(DEFINES, OEM_LISICHENG) || contains(DEFINES, OEM_CANGTIAN) || contains(DEFINES, OEM_ZHONGJING) || contains(DEFINES, OEM_ZIGUANG)) { contains(DEFINES, OEM_HANWANG) { diff --git a/build-qt/HGSolution/HGSolution2.pro b/build-qt/HGSolution/HGSolution2.pro index c1e762c2..12b6a732 100644 --- a/build-qt/HGSolution/HGSolution2.pro +++ b/build-qt/HGSolution/HGSolution2.pro @@ -2,4 +2,7 @@ TEMPLATE = subdirs SUBDIRS += \ HGSaneUI \ - HGDemo \ No newline at end of file + HGDemo + +HGDemo.depends = \ + HGSaneUI \ No newline at end of file diff --git a/modules/saneui/HGSaneUI.cpp b/modules/saneui/HGSaneUI.cpp index f754ce8b..15e191d1 100644 --- a/modules/saneui/HGSaneUI.cpp +++ b/modules/saneui/HGSaneUI.cpp @@ -1,28 +1,26 @@ #include "HGSaneUI.h" #include "dialog_device_select.h" #include "dialog_device_scan.h" -#include +#include + +#ifdef HG_CMP_MSC +#include "qwinwidget.h" +extern HINSTANCE g_hInst; +extern bool g_ownApplication; +#endif int show_devlist_ui(SANEAPI* saneApi, HGWindow parent, SANE_Handle *handle, char *devName, unsigned int maxLen) { if (nullptr == saneApi || nullptr == handle) return -1; - if (nullptr == qApp) - { -#ifdef HG_CMP_MSC - // 创建WIN32窗口 - return -2; -#else - return -2; -#endif - } - QWidget *qParent = nullptr; #ifdef HG_CMP_MSC - qParent = QWidget::find((WId)parent); - if (nullptr != parent && nullptr == qParent) - return -2; + if (!g_ownApplication) + g_ownApplication = QMfcApp::pluginInstance(g_hInst); + QWinWidget win(parent); + win.showCentered(); + qParent = &win; #else qParent = parent; #endif @@ -49,16 +47,6 @@ int show_setting_ui(SANEAPI* saneApi, SANE_Handle handle, HGWindow parent) if (nullptr == saneApi || nullptr == handle) return -1; - if (nullptr == qApp) - { -#ifdef HG_CMP_MSC - // 创建WIN32窗口 - return -2; -#else - return -2; -#endif - } - return 0; } @@ -67,21 +55,13 @@ int show_scan_ui(SANEAPI* saneApi, SANE_Handle handle, HGWindow parent, show_sca if (nullptr == saneApi || nullptr == handle) return -1; - if (nullptr == qApp) - { -#ifdef HG_CMP_MSC - // 创建WIN32窗口 - return -2; -#else - return -2; -#endif - } - QWidget *qParent = nullptr; #ifdef HG_CMP_MSC - qParent = QWidget::find((WId)parent); - if (nullptr != parent && nullptr == qParent) - return -2; + if (!g_ownApplication) + g_ownApplication = QMfcApp::pluginInstance(g_hInst); + QWinWidget win(parent); + win.showCentered(); + qParent = &win; #else qParent = parent; #endif diff --git a/modules/saneui/dllmain.cpp b/modules/saneui/dllmain.cpp new file mode 100644 index 00000000..d59b6f7b --- /dev/null +++ b/modules/saneui/dllmain.cpp @@ -0,0 +1,22 @@ +#include "qmfcapp.h" +#include "qwinwidget.h" +#include + +HINSTANCE g_hInst = NULL; +bool g_ownApplication = FALSE; + +BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpvReserved*/) +{ + if (dwReason == DLL_PROCESS_ATTACH) + { + g_hInst = hInstance; + } + else if (dwReason == DLL_PROCESS_DETACH) + { + if (g_ownApplication) + delete qApp; + g_hInst = NULL; + } + + return TRUE; +} diff --git a/modules/saneui/qmfcapp.cpp b/modules/saneui/qmfcapp.cpp new file mode 100644 index 00000000..0b3d10c8 --- /dev/null +++ b/modules/saneui/qmfcapp.cpp @@ -0,0 +1,427 @@ +// Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +// SPDX-License-Identifier: BSD-3-Clause + +// Implementation of the QMfcApp classes + +#ifdef QT3_SUPPORT +#undef QT3_SUPPORT +#endif + +#ifdef UNICODE +#undef UNICODE +#endif + +#include "qmfcapp.h" + +#include +#include +#include + +#ifdef QTWINMIGRATE_WITHMFC +#include +#else +#include +#endif + +#ifdef QTWINMIGRATE_WITHMFC +CWinApp *QMfcApp::mfc_app = 0; +char **QMfcApp::mfc_argv = 0; +int QMfcApp::mfc_argc = 0; +#endif + +#if QT_VERSION >= 0x050000 +#define QT_WA(unicode, ansi) unicode + +QMfcAppEventFilter::QMfcAppEventFilter() : QAbstractNativeEventFilter() +{ +} + +bool QMfcAppEventFilter::nativeEventFilter(const QByteArray &, void *message, long *result) +{ + return static_cast(qApp)->winEventFilter((MSG*)message, result); +} +#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; +} + +#if QT_VERSION >= 0x050000 +Q_GLOBAL_STATIC(QMfcAppEventFilter, qmfcEventFilter); +#endif + +#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(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; +#if QT_VERSION >= 0x050000 + QAbstractEventDispatcher::instance()->installNativeEventFilter(qmfcEventFilter()); +#else + QAbstractEventDispatcher::instance()->setEventFilter(qmfc_eventFilter); +#endif + setQuitOnLastWindowClosed(false); +} +#endif + +QMfcApp::QMfcApp(int &argc, char **argv) : QApplication(argc, argv) +{ +#if QT_VERSION >= 0x050000 + QAbstractEventDispatcher::instance()->installNativeEventFilter(qmfcEventFilter()); +#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((WId)msg->hwnd); + HWND toplevel = 0; + if (widget) { + HWND parent = (HWND)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; +#if QT_VERSION < 0x050000 + return QApplication::winEventFilter(msg, result); +#else + Q_UNUSED(result); + return false; +#endif +} diff --git a/modules/saneui/qmfcapp.h b/modules/saneui/qmfcapp.h new file mode 100644 index 00000000..e11afa59 --- /dev/null +++ b/modules/saneui/qmfcapp.h @@ -0,0 +1,57 @@ +// Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +// SPDX-License-Identifier: BSD-3-Clause + + +// Declaration of the QMfcApp classes + +#ifndef QMFCAPP_H +#define QMFCAPP_H + +#include + +#if defined(_AFXDLL) && defined(_MSC_VER) +#define QTWINMIGRATE_WITHMFC +class CWinApp; +#endif + +#if QT_VERSION >= 0x050000 +#include + +class QMfcAppEventFilter : public QAbstractNativeEventFilter +{ +public: + QMfcAppEventFilter(); + bool nativeEventFilter(const QByteArray &eventType, void *message, long *result); +}; +#endif + +class QMfcApp : public QApplication +{ +public: + static bool pluginInstance(Qt::HANDLE plugin = 0); + +#ifdef QTWINMIGRATE_WITHMFC + static int run(CWinApp *mfcApp); + static QApplication *instance(CWinApp *mfcApp); + QMfcApp(CWinApp *mfcApp, int &argc, char **argv); +#endif + QMfcApp(int &argc, char **argv); + ~QMfcApp(); + + bool winEventFilter(MSG *msg, long *result); + + static void enterModalLoop(); + static void exitModalLoop(); + +private: +#ifdef QTWINMIGRATE_WITHMFC + static char ** mfc_argv; + static int mfc_argc; + static CWinApp *mfc_app; +#endif + + int idleCount; + bool doIdle; +}; + +#endif // QMFCAPP_H diff --git a/modules/saneui/qwinhost.cpp b/modules/saneui/qwinhost.cpp new file mode 100644 index 00000000..c1f84b0d --- /dev/null +++ b/modules/saneui/qwinhost.cpp @@ -0,0 +1,325 @@ +// Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +// SPDX-License-Identifier: BSD-3-Clause + +// Implementation of the QWinHost classes + +#ifdef QT3_SUPPORT +#undef QT3_SUPPORT +#endif + +#include "qwinhost.h" + +#include +#include + +#if QT_VERSION >= 0x050000 +#define QT_WA(unicode, ansi) unicode +#endif + +/*! + \class QWinHost qwinhost.h + \brief The QWinHost class provides an API to use native Win32 + windows in Qt applications. + + QWinHost exists to provide a QWidget that can act as a parent for + any native Win32 control. Since QWinHost is a proper QWidget, it + can be used as a toplevel widget (e.g. 0 parent) or as a child of + any other QWidget. + + QWinHost integrates the native control into the Qt user interface, + e.g. handles focus switches and laying out. + + Applications moving to Qt may have custom Win32 controls that will + take time to rewrite with Qt. Such applications can use these + custom controls as children of QWinHost widgets. This allows the + application's user interface to be replaced gradually. + + When the QWinHost is destroyed, and the Win32 window hasn't been + set with setWindow(), the window will also be destroyed. +*/ + +/*! + Creates an instance of QWinHost. \a parent and \a f are + passed on to the QWidget constructor. The widget has by default + no background. + + \warning You cannot change the parent widget of the QWinHost instance + after the native window has been created, i.e. do not call + QWidget::setParent or move the QWinHost into a different layout. +*/ +QWinHost::QWinHost(QWidget *parent, Qt::WindowFlags f) +: QWidget(parent, f), wndproc(0),own_hwnd(false), hwnd(0) +{ + setAttribute(Qt::WA_NoBackground); + setAttribute(Qt::WA_NoSystemBackground); +} + +/*! + Destroys the QWinHost object. If the hosted Win32 window has not + been set explicitly using setWindow() the window will be + destroyed. +*/ +QWinHost::~QWinHost() +{ + if (wndproc) { +#if defined(GWLP_WNDPROC) + QT_WA({ + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)wndproc); + },{ + SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)wndproc); + }) +#else + QT_WA({ + SetWindowLong(hwnd, GWL_WNDPROC, (LONG)wndproc); + },{ + SetWindowLongA(hwnd, GWL_WNDPROC, (LONG)wndproc); + }) +#endif + } + + if (hwnd && own_hwnd) + DestroyWindow(hwnd); +} + +/*! + Reimplement this virtual function to create and return the native + Win32 window. \a parent is the handle to this widget, and \a + instance is the handle to the application instance. The returned HWND + must be a child of the \a parent HWND. + + The default implementation returns null. The window returned by a + reimplementation of this function is owned by this QWinHost + instance and will be destroyed in the destructor. + + This function is called by the implementation of polish() if no + window has been set explicitly using setWindow(). Call polish() to + force this function to be called. + + \sa setWindow() +*/ +HWND QWinHost::createWindow(HWND parent, HINSTANCE instance) +{ + Q_UNUSED(parent); + Q_UNUSED(instance); + return 0; +} + +/*! + Ensures that the window provided a child of this widget, unless + it is a WS_OVERLAPPED window. +*/ +void QWinHost::fixParent() +{ + if (!hwnd) + return; + if (!::IsWindow(hwnd)) { + hwnd = 0; + return; + } + if (::GetParent(hwnd) == (HWND)winId()) + return; + long style = GetWindowLong(hwnd, GWL_STYLE); + if (style & WS_OVERLAPPED) + return; + ::SetParent(hwnd, (HWND)winId()); +} + +/*! + Sets the native Win32 window to \a window. If \a window is not a child + window of this widget, then it is reparented to become one. If \a window + is not a child window (i.e. WS_OVERLAPPED is set), then this function does nothing. + + The lifetime of the window handle will be managed by Windows, QWinHost does not + call DestroyWindow. To verify that the handle is destroyed when expected, handle + WM_DESTROY in the window procedure. + + \sa window(), createWindow() +*/ +void QWinHost::setWindow(HWND window) +{ + if (hwnd && own_hwnd) + DestroyWindow(hwnd); + + hwnd = window; + fixParent(); + + own_hwnd = false; +} + +/*! + Returns the handle to the native Win32 window, or null if no + window has been set or created yet. + + \sa setWindow(), createWindow() +*/ +HWND QWinHost::window() const +{ + return hwnd; +} + +void *getWindowProc(QWinHost *host) +{ + return host ? host->wndproc : 0; +} + +LRESULT CALLBACK WinHostProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + QWinHost *widget = qobject_cast(QWidget::find((WId)::GetParent(hwnd))); + WNDPROC oldproc = (WNDPROC)getWindowProc(widget); + if (widget) { + switch(msg) { + case WM_LBUTTONDOWN: + if (::GetFocus() != hwnd && (widget->focusPolicy() & Qt::ClickFocus)) { + widget->setFocus(Qt::MouseFocusReason); + } + break; + + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + QT_WA({ + SendMessage((HWND)widget->winId(), msg, wParam, lParam); + }, { + SendMessageA((HWND)widget->winId(), msg, wParam, lParam); + }) + break; + + case WM_KEYDOWN: + if (wParam == VK_TAB) { + QT_WA({ + SendMessage((HWND)widget->winId(), msg, wParam, lParam); + }, { + SendMessageA((HWND)widget->winId(), msg, wParam, lParam); + }) + } + break; + + default: + break; + } + } + + QT_WA({ + if (oldproc) + return CallWindowProc(oldproc, hwnd, msg, wParam, lParam); + return DefWindowProc(hwnd,msg,wParam,lParam); + }, { + if (oldproc) + return CallWindowProcA(oldproc, hwnd, msg, wParam, lParam); + return DefWindowProcA(hwnd,msg,wParam,lParam); + }) +} + +/*! + \reimp +*/ +bool QWinHost::event(QEvent *e) +{ + switch(e->type()) { + case QEvent::Polish: + if (!hwnd) { + hwnd = createWindow(HWND(winId()), GetModuleHandle(0)); + fixParent(); + own_hwnd = hwnd != 0; + } + if (hwnd && !wndproc && GetParent(hwnd) == (HWND)winId()) { +#if defined(GWLP_WNDPROC) + QT_WA({ + wndproc = (void*)GetWindowLongPtr(hwnd, GWLP_WNDPROC); + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR)WinHostProc); + }, { + wndproc = (void*)GetWindowLongPtrA(hwnd, GWLP_WNDPROC); + SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONG_PTR)WinHostProc); + }) +#else + QT_WA({ + wndproc = (void*)GetWindowLong(hwnd, GWL_WNDPROC); + SetWindowLong(hwnd, GWL_WNDPROC, (LONG)WinHostProc); + }, { + wndproc = (void*)GetWindowLongA(hwnd, GWL_WNDPROC); + SetWindowLongA(hwnd, GWL_WNDPROC, (LONG)WinHostProc); + }) +#endif + + LONG style; + QT_WA({ + style = GetWindowLong(hwnd, GWL_STYLE); + }, { + style = GetWindowLongA(hwnd, GWL_STYLE); + }) + if (style & WS_TABSTOP) + setFocusPolicy(Qt::FocusPolicy(focusPolicy() | Qt::StrongFocus)); + } + break; + case QEvent::WindowBlocked: + if (hwnd) + EnableWindow(hwnd, false); + break; + case QEvent::WindowUnblocked: + if (hwnd) + EnableWindow(hwnd, true); + break; + } + return QWidget::event(e); +} + +/*! + \reimp +*/ +void QWinHost::showEvent(QShowEvent *e) +{ + QWidget::showEvent(e); + + if (hwnd) + SetWindowPos(hwnd, HWND_TOP, 0, 0, width(), height(), SWP_SHOWWINDOW); +} + +/*! + \reimp +*/ +void QWinHost::focusInEvent(QFocusEvent *e) +{ + QWidget::focusInEvent(e); + + if (hwnd) + ::SetFocus(hwnd); +} + +/*! + \reimp +*/ +void QWinHost::resizeEvent(QResizeEvent *e) +{ + QWidget::resizeEvent(e); + + if (hwnd) + SetWindowPos(hwnd, HWND_TOP, 0, 0, width(), height(), 0); +} + +/*! + \reimp +*/ +#if QT_VERSION >= 0x050000 +bool QWinHost::nativeEvent(const QByteArray &eventType, void *message, long *result) +#else +bool QWinHost::winEvent(MSG *msg, long *result) +#endif +{ +#if QT_VERSION >= 0x050000 + MSG *msg = (MSG *)message; +#endif + switch (msg->message) + { + case WM_SETFOCUS: + if (hwnd) { + ::SetFocus(hwnd); + return true; + } + default: + break; + } +#if QT_VERSION >= 0x050000 + return QWidget::nativeEvent(eventType, message, result); +#else + return QWidget::winEvent(msg, result); +#endif +} diff --git a/modules/saneui/qwinhost.h b/modules/saneui/qwinhost.h new file mode 100644 index 00000000..7e4e1de6 --- /dev/null +++ b/modules/saneui/qwinhost.h @@ -0,0 +1,45 @@ +// Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +// SPDX-License-Identifier: BSD-3-Clause + + +// Declaration of the QWinHost classes + +#ifndef QWINHOST_H +#define QWINHOST_H + +#include + +class QWinHost : public QWidget +{ + Q_OBJECT +public: + QWinHost(QWidget *parent = 0, Qt::WindowFlags f = 0); + ~QWinHost(); + + void setWindow(HWND); + HWND window() const; + +protected: + virtual HWND createWindow(HWND parent, HINSTANCE instance); + + bool event(QEvent *e); + void showEvent(QShowEvent *); + void focusInEvent(QFocusEvent*); + void resizeEvent(QResizeEvent*); + +#if QT_VERSION >= 0x050000 + bool nativeEvent(const QByteArray &eventType, void *message, long *result); +#else + bool winEvent(MSG *msg, long *result); +#endif + +private: + void fixParent(); + friend void* getWindowProc(QWinHost*); + + void *wndproc; + bool own_hwnd; + HWND hwnd; +}; + +#endif // QWINHOST_H diff --git a/modules/saneui/qwinwidget.cpp b/modules/saneui/qwinwidget.cpp new file mode 100644 index 00000000..623994ca --- /dev/null +++ b/modules/saneui/qwinwidget.cpp @@ -0,0 +1,359 @@ +// Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +// SPDX-License-Identifier: BSD-3-Clause + +// Implementation of the QWinWidget classes + +#ifdef QT3_SUPPORT +#undef QT3_SUPPORT +#endif + +#ifdef UNICODE +#undef UNICODE +#endif + +#include "qmfcapp.h" + +#ifdef QTWINMIGRATE_WITHMFC +#include +#endif + +#include + +#include "qwinwidget.h" + +#include + +#if QT_VERSION >= 0x050000 +#include +#include +#define QT_WA(unicode, ansi) unicode +#endif + +/*! + \class QWinWidget qwinwidget.h + \brief The QWinWidget class is a Qt widget that can be child of a + native Win32 widget. + + The QWinWidget class is the bridge between an existing application + user interface developed using native Win32 APIs or toolkits like + MFC, and Qt based GUI elements. + + Using QWinWidget as the parent of QDialogs will ensure that + modality, placement and stacking works properly throughout the + entire application. If the child widget is a top level window that + uses the \c WDestructiveClose flag, QWinWidget will destroy itself + when the child window closes down. + + Applications moving to Qt can use QWinWidget to add new + functionality, and gradually replace the existing interface. +*/ + +/*! + Creates an instance of QWinWidget. \a hParentWnd is the handle to + the native Win32 parent. If a \a parent is provided the object is + owned by that QObject. \a f is passed on to the QWidget constructor. +*/ +QWinWidget::QWinWidget(HWND hParentWnd, QObject *parent, Qt::WindowFlags f) +: QWidget(0, f), hParent(hParentWnd), prevFocus(0), reenable_parent(false) +{ + if (parent) + QObject::setParent(parent); + + init(); +} + +#ifdef QTWINMIGRATE_WITHMFC +/*! + \overload + + Creates an instance of QWinWidget. \a parentWnd is a pointer to an + MFC window object. If a \a parent is provided the object is owned + by that QObject. \a f is passed on to the QWidget constructor. +*/ +QWinWidget::QWinWidget(CWnd *parentWnd, QObject *parent, Qt::WindowFlags f) +: QWidget(0, f), hParent(parentWnd ? parentWnd->m_hWnd : 0), prevFocus(0), reenable_parent(false) +{ + if (parent) + QObject::setParent(parent); + + init(); +} +#endif + + +void QWinWidget::init() +{ + Q_ASSERT(hParent); + + if (hParent) { +#if QT_VERSION >= 0x050000 + setProperty("_q_embedded_native_parent_handle", WId(hParent)); +#endif + // make the widget window style be WS_CHILD so SetParent will work + QT_WA({ + SetWindowLong((HWND)winId(), GWL_STYLE, WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); + }, { + SetWindowLongA((HWND)winId(), GWL_STYLE, WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); + }) +#if QT_VERSION >= 0x050000 + QWindow *window = windowHandle(); + HWND h = static_cast(QGuiApplication::platformNativeInterface()-> + nativeResourceForWindow("handle", window)); + SetParent(h, hParent); + window->setFlags(Qt::FramelessWindowHint); +#else + SetParent(winId(), hParent); +#endif + QEvent e(QEvent::EmbeddingControl); + QApplication::sendEvent(this, &e); + } +} + +/*! + Destroys this object, freeing all allocated resources. +*/ +QWinWidget::~QWinWidget() +{ +} + +/*! + Returns the handle of the native Win32 parent window. +*/ +HWND QWinWidget::parentWindow() const +{ + return hParent; +} + +/*! + \reimp +*/ +void QWinWidget::childEvent(QChildEvent *e) +{ + QObject *obj = e->child(); + if (obj->isWidgetType()) { + if (e->added()) { + if (obj->isWidgetType()) { + obj->installEventFilter(this); + } + } else if (e->removed() && reenable_parent) { + reenable_parent = false; + EnableWindow(hParent, true); + obj->removeEventFilter(this); + } + } + QWidget::childEvent(e); +} + +/*! \internal */ +void QWinWidget::saveFocus() +{ + if (!prevFocus) + prevFocus = ::GetFocus(); + if (!prevFocus) + prevFocus = parentWindow(); +} + +/*! + Shows this widget. Overrides QWidget::show(). + + \sa showCentered() +*/ +void QWinWidget::show() +{ + saveFocus(); + QWidget::show(); +} + +/*! + Centers this widget over the native parent window. Use this + function to have Qt toplevel windows (i.e. dialogs) positioned + correctly over their native parent windows. + + \code + QWinWidget qwin(hParent); + qwin.center(); + + QMessageBox::information(&qwin, "Caption", "Information Text"); + \endcode + + This will center the message box over the client area of hParent. +*/ +void QWinWidget::center() +{ + const QWidget *child = findChild(); + if (child && !child->isWindow()) { + qWarning("QWinWidget::center: Call this function only for QWinWidgets with toplevel children"); + } + RECT r; + GetWindowRect(hParent, &r); + setGeometry((r.right-r.left)/2+r.left, (r.bottom-r.top)/2+r.top,0,0); +} + +/*! + \obsolete + + Call center() instead. +*/ +void QWinWidget::showCentered() +{ + center(); + show(); +} + +/*! + Sets the focus to the window that had the focus before this widget + was shown, or if there was no previous window, sets the focus to + the parent window. +*/ +void QWinWidget::resetFocus() +{ + if (prevFocus) + ::SetFocus(prevFocus); + else + ::SetFocus(parentWindow()); +} + +/*! \reimp +*/ +#if QT_VERSION >= 0x050000 +bool QWinWidget::nativeEvent(const QByteArray &, void *message, long *) +#else +bool QWinWidget::winEvent(MSG *msg, long *) +#endif +{ +#if QT_VERSION >= 0x050000 + MSG *msg = (MSG *)message; +#endif + if (msg->message == WM_SETFOCUS) { + Qt::FocusReason reason; + if (::GetKeyState(VK_LBUTTON) < 0 || ::GetKeyState(VK_RBUTTON) < 0) + reason = Qt::MouseFocusReason; + else if (::GetKeyState(VK_SHIFT) < 0) + reason = Qt::BacktabFocusReason; + else + reason = Qt::TabFocusReason; + QFocusEvent e(QEvent::FocusIn, reason); + QApplication::sendEvent(this, &e); + } + + return false; +} + +/*! + \reimp +*/ +bool QWinWidget::eventFilter(QObject *o, QEvent *e) +{ + QWidget *w = (QWidget*)o; + + switch (e->type()) { + case QEvent::WindowDeactivate: + if (w->isModal() && w->isHidden()) + BringWindowToTop(hParent); + break; + + case QEvent::Hide: + if (reenable_parent) { + EnableWindow(hParent, true); + reenable_parent = false; + } + resetFocus(); + if (w->testAttribute(Qt::WA_DeleteOnClose) && w->isWindow()) + deleteLater(); + break; + + case QEvent::Show: + if (w->isWindow()) { + saveFocus(); + hide(); + if (w->isModal() && !reenable_parent) { + EnableWindow(hParent, false); + reenable_parent = true; + } + } + break; + + case QEvent::Close: + ::SetActiveWindow(hParent); + if (w->testAttribute(Qt::WA_DeleteOnClose)) + deleteLater(); + break; + + default: + break; + } + + return QWidget::eventFilter(o, e); +} + +/*! \reimp +*/ +void QWinWidget::focusInEvent(QFocusEvent *e) +{ + QWidget *candidate = this; + + switch (e->reason()) { + case Qt::TabFocusReason: + case Qt::BacktabFocusReason: + while (!(candidate->focusPolicy() & Qt::TabFocus)) { + candidate = candidate->nextInFocusChain(); + if (candidate == this) { + candidate = 0; + break; + } + } + if (candidate) { + candidate->setFocus(e->reason()); + if (e->reason() == Qt::BacktabFocusReason || e->reason() == Qt::TabFocusReason) { + candidate->setAttribute(Qt::WA_KeyboardFocusChange); + candidate->window()->setAttribute(Qt::WA_KeyboardFocusChange); + } + if (e->reason() == Qt::BacktabFocusReason) + QWidget::focusNextPrevChild(false); + } + break; + default: + break; + } +} + +/*! \reimp +*/ +bool QWinWidget::focusNextPrevChild(bool next) +{ + QWidget *curFocus = focusWidget(); + if (!next) { + if (!curFocus->isWindow()) { + QWidget *nextFocus = curFocus->nextInFocusChain(); + QWidget *prevFocus = 0; + QWidget *topLevel = 0; + while (nextFocus != curFocus) { + if (nextFocus->focusPolicy() & Qt::TabFocus) { + prevFocus = nextFocus; + topLevel = 0; + } else if (nextFocus->isWindow()) { + topLevel = nextFocus; + } + nextFocus = nextFocus->nextInFocusChain(); + } + + if (!topLevel) { + return QWidget::focusNextPrevChild(false); + } + } + } else { + QWidget *nextFocus = curFocus; + while (1) { + nextFocus = nextFocus->nextInFocusChain(); + if (nextFocus->isWindow()) + break; + if (nextFocus->focusPolicy() & Qt::TabFocus) { + return QWidget::focusNextPrevChild(true); + } + } + } + + ::SetFocus(hParent); + + return true; +} diff --git a/modules/saneui/qwinwidget.h b/modules/saneui/qwinwidget.h new file mode 100644 index 00000000..5b4e1fbe --- /dev/null +++ b/modules/saneui/qwinwidget.h @@ -0,0 +1,54 @@ +// Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +// SPDX-License-Identifier: BSD-3-Clause + + +// Declaration of the QWinWidget classes + +#ifndef QWINWIDGET_H +#define QWINWIDGET_H + +#include +#include "qmfcapp.h" + +class CWnd; + +class QWinWidget : public QWidget +{ + Q_OBJECT +public: + QWinWidget( HWND hParentWnd, QObject *parent = 0, Qt::WindowFlags f = 0 ); +#ifdef QTWINMIGRATE_WITHMFC + QWinWidget( CWnd *parnetWnd, QObject *parent = 0, Qt::WindowFlags f = 0 ); +#endif + ~QWinWidget(); + + void show(); + void center(); + void showCentered(); + + HWND parentWindow() const; + +protected: + void childEvent( QChildEvent *e ); + bool eventFilter( QObject *o, QEvent *e ); + + bool focusNextPrevChild(bool next); + void focusInEvent(QFocusEvent *e); +#if QT_VERSION >= 0x050000 + bool nativeEvent(const QByteArray &eventType, void *message, long *result); +#else + bool winEvent(MSG *msg, long *result); +#endif + +private: + void init(); + + void saveFocus(); + void resetFocus(); + + HWND hParent; + HWND prevFocus; + bool reenable_parent; +}; + +#endif // QWINWIDGET_H