summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--alf/alf.cpp19
-rw-r--r--alf/alf.h48
-rw-r--r--alf/alftoplevel.cpp53
-rw-r--r--widgetfactory.cpp4
4 files changed, 111 insertions, 13 deletions
diff --git a/alf/alf.cpp b/alf/alf.cpp
index 84ba71d..a4abca6 100644
--- a/alf/alf.cpp
+++ b/alf/alf.cpp
@@ -411,3 +411,22 @@ ALF_SetFocus(HWND target)
else
SendMessage(target, WM_NEXTDLGCTL, (WPARAM)target, TRUE);
}
+
+int
+ALF_MessageBox(HWND hwnd, const TCHAR *text, const TCHAR *caption, UINT type)
+{
+ ALFApplication *app = NULL;
+
+ if (GetWindowThreadProcessId(hwnd, NULL) == GetCurrentThreadId())
+ app = ALF_Toplevel_Application(hwnd);
+
+ if (app)
+ ALF_Application_DisableWindowsForModal(app, hwnd);
+
+ int retval = MessageBox(hwnd, text, caption, type);
+
+ if (app)
+ ALF_Application_ReenableWindowsForModal(app, hwnd);
+
+ return retval;
+}
diff --git a/alf/alf.h b/alf/alf.h
index cc1d936..2399569 100644
--- a/alf/alf.h
+++ b/alf/alf.h
@@ -302,13 +302,21 @@ ALF_FillRect(HDC dc, const RECT *rc, ALFColor color);
void
ALF_DestroyWidget(HWND win, WORD id);
+// wrapper for MessageBox() that handles modality for multiple toplevels with ALFApplication
+int
+ALF_MessageBox(HWND hwnd, const TCHAR *text, const TCHAR *caption, UINT type);
+
// application
-// an application is a container for multiple top-level windows
+// ALFApplication is a container for multiple top-level windows.
+//
+// Having an ALFApplication is not required, but it enables you to get modality
+// right in the face of multiple top-level windows.
+//
// NOTE: ALFApplication is not thread-safe. It must be accessed only from the
// thread that created it, and all toplevels associated with the application
-// must live on that same thread.
+// must live on that same thread. There must be at most one application per thread.
typedef struct tagALFApplication ALFApplication;
@@ -335,12 +343,41 @@ ALF_Application_SetPreTranslateMessageHandler(ALFApplication *app, BOOL(*handler
BOOL
ALF_Application_PreTranslateMessage(ALFApplication *app, MSG *msg, HWND modalToplevel);
-// TODO: implement modal stuff
-/*void
+// Modality with multiple toplevels
+// --------------------------------
+//
+// How it works:
+// * A "modal disable" counter is managed for each ALF toplevel
+// * Calling ALF_Application_DisableWindowsForModal() will increase the counter
+// for each toplevel of the application, except for the HWND skip
+// * Calling ALF_Application_ReenableWindowsForModal() will decrease the counter
+// for each toplevel of the application, except for the HWND skip
+//
+// How to show a modal ALF toplevel:
+// (1) It is assumed that we're running on the thread that owns the modal toplevel
+// (2) Show the modal toplevel
+// (3) If the modal has an ALFApplication:
+// (3.1) Call DisableWindowsForModal on that app with skip=modal
+// (4) Additionally, disable the owner with EnableWindow()
+// The owner might be a non-ALF window or live on another thread, in which
+// case it is not being disabled in step 3.1.
+// (5) Run the modal message loop
+// (6) Undo (4) via EnableWindow
+// (7) Undo (3.1) via EnableWindowsForModal with skip=modal
+// (8) Hide the modal window
+// --> see ALF_Toplevel_ShowModal() for an example of how to do this
+//
+// How to show a modal window that adheres to Win32 modality conventions:
+// (1) If the owner window lives on the same thread and has an ALFApplication:
+// (1.1) Call DisableWindowsForModal on that app with skip=owner
+// (2) Run the modal window
+// (3) undo 1.1 via EnableWindowsForModal with skip=owner
+// --> see ALF_MessageBox() for an example of how to do this
+void
ALF_Application_DisableWindowsForModal(ALFApplication *app, HWND skip);
void
-ALF_Application_ReenableWindowsForModal(ALFApplication *app, HWND skip);*/
+ALF_Application_ReenableWindowsForModal(ALFApplication *app, HWND skip);
// also destroys all toplevels associated with the application
void
@@ -349,6 +386,7 @@ ALF_DestroyApplication(ALFApplication *app);
// toplevel window
+// NOTE about app: If app==NULL, the owner's app will be copied if the owner lives on the same thread
HWND
ALF_CreateToplevelWindow(DWORD exstyle, DWORD style, HWND hwndOwner, ALFApplication *app, ALFToplevelVTable *vtbl, void *closure);
diff --git a/alf/alftoplevel.cpp b/alf/alftoplevel.cpp
index b9f2e7b..d54d4b3 100644
--- a/alf/alftoplevel.cpp
+++ b/alf/alftoplevel.cpp
@@ -8,6 +8,7 @@ typedef struct {
void *closure;
ALFListHeader toplevelList;
ALFApplication *app;
+ DWORD modalDisableDepth;
DWORD flags;
LPARAM modalResult;
ALFLayout layout;
@@ -461,6 +462,10 @@ ALF_CreateToplevelWindow(DWORD exstyle, DWORD style, HWND hwndOwner, ALFApplicat
if (hwndOwner && GetWindowLong(hwndOwner, GWL_STYLE) & WS_CHILD)
hwndOwner = GetParent(hwndOwner);
+ // copy app of owner, but only if we're in the same thread
+ if (!app && hwndOwner && GetWindowThreadProcessId(hwndOwner, NULL) == GetCurrentThreadId())
+ app = ALF_Toplevel_Application(hwndOwner);
+
struct ALFToplevel_CreateParams params;
params.vtbl = vtbl;
params.closure = closure;
@@ -487,6 +492,9 @@ ALF_Toplevel_ShowModal(HWND toplevel)
ShowWindow(toplevel, SW_SHOW);
+ if (priv->app)
+ ALF_Application_DisableWindowsForModal(priv->app, toplevel);
+
if (owner)
ownerEnabled = !EnableWindow(owner, FALSE);
@@ -504,8 +512,11 @@ ALF_Toplevel_ShowModal(HWND toplevel)
}
}
- if (owner)
- EnableWindow(owner, ownerEnabled);
+ if (owner && ownerEnabled)
+ EnableWindow(owner, TRUE);
+
+ if (priv->app)
+ ALF_Application_ReenableWindowsForModal(priv->app, toplevel);
ShowWindow(toplevel, SW_HIDE);
@@ -636,12 +647,42 @@ ALF_Application_PreTranslateMessage(ALFApplication *app, MSG *msg, HWND modalTop
|| (app->activeToplevel && app->activeToplevel->hwnd != modalToplevel && ALF_Toplevel_PreTranslateMessage(app->activeToplevel->hwnd, msg));
}
-// TODO: implement modal stuff
-/*void
-ALF_Application_DisableWindowsForModal(ALFApplication *app, HWND skip);
+void
+ALF_Application_DisableWindowsForModal(ALFApplication *app, HWND skip)
+{
+ ALF_FOR_LIST(ALFToplevelPriv, toplevelList, &app->toplevelList, t) {
+ if (t->hwnd == skip)
+ continue;
+
+ DWORD c = t->modalDisableDepth & 0x7fffffff;
+ if (c == 0) {
+ t->modalDisableDepth = 1;
+ if (EnableWindow(t->hwnd, FALSE))
+ t->modalDisableDepth |= 0x80000000; // flag: was already disabled
+ } else {
+ t->modalDisableDepth++;
+ }
+ }
+}
void
-ALF_Application_ReenableWindowsForModal(ALFApplication *app, HWND skip);*/
+ALF_Application_ReenableWindowsForModal(ALFApplication *app, HWND skip)
+{
+ ALF_FOR_LIST(ALFToplevelPriv, toplevelList, &app->toplevelList, t) {
+ if (t->hwnd == skip)
+ continue;
+
+ DWORD c = t->modalDisableDepth & 0x7fffffff;
+ if (c == 1) {
+ if (!(t->modalDisableDepth & 0x80000000))
+ EnableWindow(t->hwnd, TRUE);
+
+ t->modalDisableDepth = 0;
+ } else {
+ t->modalDisableDepth--;
+ }
+ }
+}
void
ALF_DestroyApplication(ALFApplication *app)
diff --git a/widgetfactory.cpp b/widgetfactory.cpp
index f761d2b..bced64a 100644
--- a/widgetfactory.cpp
+++ b/widgetfactory.cpp
@@ -775,7 +775,7 @@ handleCommand(void *closure, HWND window, WORD notificationcode, WORD sourceid,
ALF_ComboBoxSetCurrentIndex(combo, -1);
}
if (sourceid == ID_HELLO) {
- MessageBox(window, TEXT("Hello World!"), TEXT("Hello"), MB_ICONASTERISK|MB_OK);
+ ALF_MessageBox(window, TEXT("Hello World!"), TEXT("Hello"), MB_ICONASTERISK|MB_OK);
}
if (control != NULL && sourceid == ID_B_TABBGSOLID) {
HWND nb = ALF_WidgetHwndById(window, ID_NOTEBOOK);
@@ -879,7 +879,7 @@ wfModalDialogRun(HWND owner, int pane)
{
HWND dlg = ALF_CreateToplevelWindow(WS_EX_DLGMODALFRAME,
WS_POPUPWINDOW | WS_CAPTION | WS_THICKFRAME,
- owner, g_application,
+ owner, NULL,
&g_wfModalDialogVtbl,
(void*)(LONG_PTR)pane);
ALF_Toplevel_ShowModal(dlg);