bug#20481: 24.5; , Newlines in message-box output don't work on Windows
On 19/08/2024 19:44, Eli Zaretskii wrote: Date: Mon, 19 Aug 2024 18:13:31 +0200 From: Cecilio Pardo This patch adds support on Windows Vista an later for dialog boxes using TaskDialog. Thanks. First, to accept a contribution of this size we'll need a copyright-assignment paperwork from you. Should I send you the form to fill with instructions to go with it, so you could start the paperwork rolling? A few comments about the patch: Hello, The copyright assignment is ready. Here is the patch with your comments addressed. I also attach a couple of manual tests. -- Cecilio Pardo diff --git a/src/menu.c b/src/menu.c index de4d0964e9c..6b4aaef1715 100644 --- a/src/menu.c +++ b/src/menu.c @@ -1594,9 +1594,10 @@ DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0, Lisp_Object selection = FRAME_TERMINAL (f)->popup_dialog_hook (f, header, contents); #ifdef HAVE_NTGUI - /* NTGUI supports only simple dialogs with Yes/No choices. For -other dialogs, it returns the symbol 'unsupported--w32-dialog', -as a signal for the caller to fall back to the emulation code. */ + /* NTGUI on Windows versions before Vista supports only simple +dialogs with Yes/No choices. For other dialogs, it returns the +symbol 'unsupported--w32-dialog', as a signal for the caller to +fall back to the emulation code. */ if (!EQ (selection, Qunsupported__w32_dialog)) #endif return selection; diff --git a/src/w32menu.c b/src/w32menu.c index cea4f4892a4..3c7ebf64abe 100644 --- a/src/w32menu.c +++ b/src/w32menu.c @@ -52,6 +52,9 @@ #include "w32common.h" /* for osinfo_cache */ +#include "commctrl.h" + +/* This only applies to OS versions prior to Vista. */ #undef HAVE_DIALOGS /* TODO: Implement native dialogs. */ #ifndef TRUE @@ -76,6 +79,11 @@ #define FALSE 0 IN const WCHAR *text, IN const WCHAR *caption, IN UINT type); +typedef HRESULT (WINAPI *TaskDialogIndirect_Proc) ( +IN const TASKDIALOGCONFIG *pTaskConfig, +OUT int *pnButton, +OUT int *pnRadioButton, +OUT BOOL *pfVerificationFlagChecked); #ifdef NTGUI_UNICODE GetMenuItemInfoA_Proc get_menu_item_info = GetMenuItemInfoA; @@ -89,6 +97,8 @@ #define FALSE 0 MessageBoxW_Proc unicode_message_box = NULL; #endif /* NTGUI_UNICODE */ +TaskDialogIndirect_Proc task_dialog_indirect; + #ifdef HAVE_DIALOGS static Lisp_Object w32_dialog_show (struct frame *, Lisp_Object, Lisp_Object, char **); #else @@ -101,14 +111,155 @@ #define FALSE 0 void w32_free_menu_strings (HWND); +#define TASK_DIALOG_MAX_BUTTONS 10 + +static HRESULT +task_dialog_callback (HWND hwnd, UINT msg, WPARAM wParam, + LPARAM lParam, LONG_PTR callback_data) +{ + switch (msg) +{ +case TDN_CREATED: + /* Disable all buttons with ID >= 2000 */ + for (int i = 0; i < TASK_DIALOG_MAX_BUTTONS; i++) +SendMessage (hwnd, TDM_ENABLE_BUTTON, 2000 + i, FALSE); + break; +} + return S_OK; +} + Lisp_Object w32_popup_dialog (struct frame *f, Lisp_Object header, Lisp_Object contents) { - check_window_system (f); -#ifndef HAVE_DIALOGS + if (task_dialog_indirect) +{ + int wide_len; + + CHECK_CONS (contents); + /* Get the title as an UTF-16 string. */ + char *title = SSDATA (ENCODE_UTF_8 (XCAR (contents))); + wide_len = sizeof (WCHAR) * + pMultiByteToWideChar (CP_UTF8, 0, title, -1, NULL, 0); + WCHAR *title_w = alloca (wide_len); + pMultiByteToWideChar (CP_UTF8, 0, title, -1, title_w, wide_len); + + /* Prepare the arrays with the dialog's buttons and return values. */ + TASKDIALOG_BUTTON buttons[TASK_DIALOG_MAX_BUTTONS]; + Lisp_Object button_values[TASK_DIALOG_MAX_BUTTONS]; + int button_count = 0; + Lisp_Object b = XCDR (contents); + + while (!NILP (b)) { + if (button_count >= TASK_DIALOG_MAX_BUTTONS) + { + /* We have too many buttons. We ignore the rest. */ + break; + } + + Lisp_Object item = XCAR (b); + + if (Fconsp (item)) + { + /* A normal item (text . value) */ + Lisp_Object item_name = XCAR (item); + Lisp_Object item_value = XCDR (item); + + CHECK_STRING (item_name); + + item_name = ENCODE_UTF_8 (item_name); + wide_len = sizeof (WCHAR) * + pMultiByteToWideChar (CP_UTF8, 0, SSDATA (item_name), + -1, NULL, 0); + buttons[button_count].pszButtonText = alloca (wide_len); + pMultiByteToWideChar (CP_UTF8, 0, SSDATA (item_name), -1, + (LPWSTR) + buttons[button_count].pszButtonText, + wide_len); + buttons[button_count].nButtonID = 1000 + button_count; + button_valu
bug#20481: 24.5; , Newlines in message-box output don't work on Windows
On 12/09/2024 4:49, Po Lu wrote: Thanks. Following are a number of minor stylistic comments. Sorry I missed those. They are corrected in the attached patch. Lastly, I observe that you have implemented a bespoke dialog parser for Windows, the likes of which have been a source of difficulties in the past. Is there any particular reason that you decided against implementing the w32_dialog_show function called in the "#ifdef HAVE_DIALOGS" version of w32_popup_dialog? I left w32_dialog_show as it was in case an implementation for older versions of windows became available. I can rewrite it if you think is better that way. diff --git a/src/menu.c b/src/menu.c index de4d0964e9c..6b4aaef1715 100644 --- a/src/menu.c +++ b/src/menu.c @@ -1594,9 +1594,10 @@ DEFUN ("x-popup-dialog", Fx_popup_dialog, Sx_popup_dialog, 2, 3, 0, Lisp_Object selection = FRAME_TERMINAL (f)->popup_dialog_hook (f, header, contents); #ifdef HAVE_NTGUI - /* NTGUI supports only simple dialogs with Yes/No choices. For -other dialogs, it returns the symbol 'unsupported--w32-dialog', -as a signal for the caller to fall back to the emulation code. */ + /* NTGUI on Windows versions before Vista supports only simple +dialogs with Yes/No choices. For other dialogs, it returns the +symbol 'unsupported--w32-dialog', as a signal for the caller to +fall back to the emulation code. */ if (!EQ (selection, Qunsupported__w32_dialog)) #endif return selection; diff --git a/src/w32menu.c b/src/w32menu.c index cea4f4892a4..8ecf7e8e8a7 100644 --- a/src/w32menu.c +++ b/src/w32menu.c @@ -52,6 +52,9 @@ #include "w32common.h" /* for osinfo_cache */ +#include "commctrl.h" + +/* This only applies to OS versions prior to Vista. */ #undef HAVE_DIALOGS /* TODO: Implement native dialogs. */ #ifndef TRUE @@ -76,6 +79,11 @@ #define FALSE 0 IN const WCHAR *text, IN const WCHAR *caption, IN UINT type); +typedef HRESULT (WINAPI *TaskDialogIndirect_Proc) ( +IN const TASKDIALOGCONFIG *pTaskConfig, +OUT int *pnButton, +OUT int *pnRadioButton, +OUT BOOL *pfVerificationFlagChecked); #ifdef NTGUI_UNICODE GetMenuItemInfoA_Proc get_menu_item_info = GetMenuItemInfoA; @@ -89,6 +97,8 @@ #define FALSE 0 MessageBoxW_Proc unicode_message_box = NULL; #endif /* NTGUI_UNICODE */ +TaskDialogIndirect_Proc task_dialog_indirect; + #ifdef HAVE_DIALOGS static Lisp_Object w32_dialog_show (struct frame *, Lisp_Object, Lisp_Object, char **); #else @@ -101,14 +111,157 @@ #define FALSE 0 void w32_free_menu_strings (HWND); +#define TASK_DIALOG_MAX_BUTTONS 10 + +static HRESULT +task_dialog_callback (HWND hwnd, UINT msg, WPARAM wParam, + LPARAM lParam, LONG_PTR callback_data) +{ + switch (msg) +{ +case TDN_CREATED: + /* Disable all buttons with ID >= 2000 */ + for (int i = 0; i < TASK_DIALOG_MAX_BUTTONS; i++) +SendMessage (hwnd, TDM_ENABLE_BUTTON, 2000 + i, FALSE); + break; +} + return S_OK; +} + Lisp_Object w32_popup_dialog (struct frame *f, Lisp_Object header, Lisp_Object contents) { - check_window_system (f); -#ifndef HAVE_DIALOGS + if (task_dialog_indirect) +{ + int wide_len; + + CHECK_CONS (contents); + + /* Get the title as an UTF-16 string. */ + char *title = SSDATA (ENCODE_UTF_8 (XCAR (contents))); + wide_len = (sizeof (WCHAR) + * pMultiByteToWideChar (CP_UTF8, 0, title, -1, NULL, 0)); + WCHAR *title_w = alloca (wide_len); + pMultiByteToWideChar (CP_UTF8, 0, title, -1, title_w, wide_len); + /* Prepare the arrays with the dialog's buttons and return values. */ + TASKDIALOG_BUTTON buttons[TASK_DIALOG_MAX_BUTTONS]; + Lisp_Object button_values[TASK_DIALOG_MAX_BUTTONS]; + int button_count = 0; + Lisp_Object b = XCDR (contents); + + while (!NILP (b)) + { + if (button_count >= TASK_DIALOG_MAX_BUTTONS) + { + /* We have too many buttons. We ignore the rest. */ + break; + } + + Lisp_Object item = XCAR (b); + + if (CONSP (item)) + { + /* A normal item (text . value) */ + Lisp_Object item_name = XCAR (item); + Lisp_Object item_value = XCDR (item); + + CHECK_STRING (item_name); + + item_name = ENCODE_UTF_8 (item_name); + wide_len = (sizeof (WCHAR) + * pMultiByteToWideChar (CP_UTF8, 0, SSDATA (item_name), + -1, NULL, 0)); + buttons[button_count].pszButtonText = alloca (wide_len); + pMultiByteToWideChar (CP_UTF8, 0, SSDATA (item_name), -1, + (LPWSTR) + buttons[button_count].pszButtonText, + wide_len); +