diff --git a/src/w32menu.c b/src/w32menu.c index cea4f4892a4..029e4bbf805 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 @@ -101,14 +104,148 @@ #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 + void *task_dialog_indirect = + get_proc_addr (GetModuleHandle ("comctl32.dll"), "TaskDialogIndirect"); + + if (task_dialog_indirect) + { + /* Get the title as a UTF-16 string. */ + CHECK_STRING (XCAR (contents)); + char *title = SSDATA (XCAR (contents)); + int wide_length = sizeof(WCHAR) * + pMultiByteToWideChar (CP_UTF8, 0, title, -1, NULL, 0); + WCHAR *title_text_wide = alloca (wide_length); + pMultiByteToWideChar (CP_UTF8, 0, title, -1, + title_text_wide, wide_length); + + /* Prepare the struct with the dialog's buttons. */ + 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); + + int wide_length = sizeof(WCHAR) * + pMultiByteToWideChar (CP_UTF8, 0, SSDATA (item_name), -1, + NULL, 0); + buttons[button_count].pszButtonText = alloca (wide_length); + pMultiByteToWideChar (CP_UTF8, 0, SSDATA (item_name), -1, + (LPWSTR) + buttons[button_count].pszButtonText, + wide_length); + buttons[button_count].nButtonID = 1000 + button_count; + button_values[button_count++] = item_value; + } + else if (NILP (item)) + { + /* A nil item means to put all following items on the + right. We ignore this. */ + } + else if (STRINGP(item)) + { + /* A string item means an unselectable button. We add a + button, an then need to disable it on the callback. + We use ids based on 2000 to mark these buttons */ + int wide_length = sizeof(WCHAR) * + pMultiByteToWideChar (CP_UTF8, 0, SSDATA (item), -1, NULL, 0); + buttons[button_count].pszButtonText = alloca (wide_length); + pMultiByteToWideChar (CP_UTF8, 0, SSDATA (item), -1, + (LPWSTR) + buttons[button_count].pszButtonText, + wide_length); + buttons[button_count].nButtonID = 2000 + button_count; + button_values[button_count++] = Qnil; + } + + b = XCDR (b); + } + + int pressed_button = 0; + + TASKDIALOGCONFIG config = { }; + config.hwndParent = FRAME_W32_WINDOW (f); + config.cbSize = sizeof(config); + config.hInstance = hinst; + config.dwFlags = TDF_ALLOW_DIALOG_CANCELLATION; + config.pfCallback = task_dialog_callback; + + config.pszWindowTitle = L"Question"; + if (!NILP (header)) + { + config.pszWindowTitle = L"Information"; + config.pszMainIcon = TD_INFORMATION_ICON; + } + config.pszMainInstruction = title_text_wide; + config.pButtons = buttons; + config.cButtons = button_count; + + if (!SUCCEEDED (TaskDialogIndirect (&config, &pressed_button, + NULL, NULL))) + return quit (); + + + switch (pressed_button) + { + case IDOK: + /* This can only happen if no buttons where + provided. An OK button is automatically added. */ + return Qt; + case IDCANCEL: + /* The user closed the dialog without using the buttons */ + return quit(); + default: + /* One of the specified buttons. */ + int button_index = pressed_button - 1000; + if ( button_index >= 0 && button_index < button_count ) + return button_values[button_index]; + return quit(); + } + } + + /* If we get here, TaskDialog is not supported. Use MessageBox/Menu. */ + + +#ifndef HAVE_DIALOGS /* Handle simple Yes/No choices as MessageBox popups. */ if (is_simple_dialog (contents)) return simple_dialog_show (f, contents, header);