View Issue Details

IDProjectCategoryView StatusLast Update
0001408OpenMPTUser Interfacepublic2025-08-27 19:28
ReporterSaga Musix Assigned To 
PrioritynormalSeverityfeatureReproducibilityN/A
Status newResolutionopen 
Target VersionOpenMPT 1.33 / libopenmpt 0.9 (goals) 
Summary0001408: Migrate MessageBoxes to Vista TaskDialog
Description

It's well known that Yes/No/Cancel style dialogs are bad UX, as action buttons should make it clear which action they are actually going to carry out. As OpenMPT no longer supports Windows XP, it would be considerably easier to migrate existing MessageBoxes (currently abstraced through the Reporting namespace) to TaskDialog windows with more describe buttons than "Yes" and "No". Wine 1.8 supports TaskDialogs, but we should probably verify to what extent, as the implementation is definitely much smaller than in later versions.

TagsNo tags attached.
Has the bug occurred in previous versions?
Tested code revision (in case you know it)

Relationships

related to 0001687 resolvedSaga Musix Use modern folder picker dialog on Windows Vista and later 

Activities

Saga Musix

Saga Musix

2021-01-16 15:17

administrator   ~0004612

MFC has a wrapper, CTaskDialog.

Saga Musix

Saga Musix

2021-01-16 15:56

administrator   ~0004613

Last edited: 2021-01-16 15:58

This is the first meaningful TaskDialog implementation in Wine that supports more than just plain old Yes/No/Cancel buttons:

https://github.com/wine-mirror/wine/commit/995b6900a7002b716654edbe28ab2ba992433c4c#diff-9e4518b138da0b92fbc6f7a8fd89c981c445f646c212e05571dab2d191fbf451

This would raise the required Wine version to at least 2.5 (2.11 for custom buttons). We appear to have no Wine 2.x users according to update statistics, but a handful of people are still on 1.8.

manx

manx

2022-02-16 17:36

administrator   ~0005097

Requiring Wine 3.0 should be trivial by now, however, we still do have Windows XP builds.

We could still use Vista dialogs and fallback to even worse UX for Windows XP: "click Yes for $ACTION, click No for $OTHERACTION".

Saga Musix

Saga Musix

2022-02-16 17:57

administrator   ~0005098

For those messages currently supported by the Reporting namespace, that would work, yes (and if the prompt is worded properly, the same prompt can be used for both dialogs without any additions). However, this wouldn't work for other dialogs - I particularly wanted to replace the MsgBoxHidable implementation. But since this only affects XP, I guess we could simply assume that the user was going to hide the message box (which is the default action anyway) for a fallback.

manx

manx

2022-02-16 18:06

administrator   ~0005099

That would be OK, I think. RETRO builds can have reduced functionality.

Saga Musix

Saga Musix

2022-02-18 23:12

administrator   ~0005103

Technically only Reporting::Confirm and Reporting::RetryCancel need to be ported. I suppose the API could pretty much stay the same, with the only difference that labels for the different actions would have to be provided as additional arguments to those functions. This would make it very easy to keep the dialogs usable as messageboxes as well.

Saga Musix

Saga Musix

2024-11-10 16:10

administrator   ~0006166

r22143 reimplements the MsgBoxHidable for Windows Vista and Wine 3.13 and newer; on older systems, the message is shown once and then always hidden.

Saga Musix

Saga Musix

2025-08-27 19:28

administrator   ~0006453

A first stab at a wrapper class for CTaskDialog, with MessageBox fallback. The other Reporting functions have been modified to use this wrapper instead of plain MessageBox, so there's no functional difference yet. As a next step, usage of those other functions needs to be adjusted to using the new TaskDlg class instead.

TaskDialog.patch (20,882 bytes)   
Index: mptrack/dlg_misc.cpp
===================================================================
--- mptrack/dlg_misc.cpp	(revision 24083)
+++ mptrack/dlg_misc.cpp	(working copy)
@@ -1573,27 +1573,29 @@
 	if((TrackerSettings::Instance().gnMsgBoxVisiblityFlags & msg.mask) == 0)
 		return;
 
-#if MPT_WINNT_AT_LEAST(MPT_WIN_VISTA) && defined(UNICODE)
-	if(CTaskDialog::IsSupported()
-	   && !(mpt::OS::Windows::IsWine() && theApp.GetWineVersion()->Version().IsBefore(mpt::OS::Wine::Version(3, 13, 0))))
+	TaskDlg dlg = TaskDlg{}
+		.WindowTitle(msg.mainTitle)
+		.Icon(MB_ICONINFORMATION);
+
+	bool dontShowAgain;
+	if(TaskDlg::ModernTaskDialogSupported())
 	{
-		CTaskDialog taskDialog(msg.message, msg.mainTitle ? CString{msg.mainTitle} : CString{}, AfxGetAppName(), TDCBF_OK_BUTTON);
-		taskDialog.SetVerificationCheckboxText(_T("Do not show this message again"));
-		taskDialog.SetVerificationCheckbox(msg.defaultDontShowAgainStatus);
-		taskDialog.DoModal();
-
-		if(taskDialog.GetVerificationCheckboxState())
-			TrackerSettings::Instance().gnMsgBoxVisiblityFlags &= ~msg.mask;
-		else
-			TrackerSettings::Instance().gnMsgBoxVisiblityFlags |= msg.mask;
+		dlg.Description(msg.message)
+			.VerificationText(_T("Do not show this message again"))
+			.VerificationChecked(msg.defaultDontShowAgainStatus);
+		dontShowAgain = dlg.DoModal().verificationChecked;
 	} else
-#endif
 	{
-		if(Reporting::Confirm(msg.message + CString(_T("\n\nShow this message again?")), msg.mainTitle ? CString{msg.mainTitle} : CString{}, msg.defaultDontShowAgainStatus) == cnfNo)
-			TrackerSettings::Instance().gnMsgBoxVisiblityFlags &= ~msg.mask;
-		else
-			TrackerSettings::Instance().gnMsgBoxVisiblityFlags |= msg.mask;
+		dlg.Description(CString{msg.message} + _T("\n\nShow this message again?"))
+			.Buttons({{_T("Yes"), IDYES}, {_T("No"), IDNO} })
+			.DefaultButton(msg.defaultDontShowAgainStatus ? IDNO : IDYES);
+		dontShowAgain = dlg.DoModal().buttonId == IDNO;
 	}
+
+	if(dontShowAgain)
+		TrackerSettings::Instance().gnMsgBoxVisiblityFlags &= ~msg.mask;
+	else
+		TrackerSettings::Instance().gnMsgBoxVisiblityFlags |= msg.mask;
 }
 
 
Index: mptrack/Reporting.cpp
===================================================================
--- mptrack/Reporting.cpp	(revision 24083)
+++ mptrack/Reporting.cpp	(working copy)
@@ -10,23 +10,27 @@
 
 #include "stdafx.h"
 #include "Reporting.h"
-#include "../mptrack/Mainfrm.h"
+#include "Mainfrm.h"
 
+#if MPT_WINNT_AT_LEAST(MPT_WIN_VISTA) && defined(UNICODE)
+#include "Mptrack.h"
+#include <afxtaskdialog.h>
+#endif
 
 OPENMPT_NAMESPACE_BEGIN
 
 
-static inline UINT LogLevelToFlags(LogLevel level)
+static inline UINT LogLevelToIcon(LogLevel level)
 {
 	switch(level)
 	{
-	case LogDebug: return MB_OK; break;
-	case LogNotification: return MB_OK; break;
-	case LogInformation: return MB_OK | MB_ICONINFORMATION; break;
-	case LogWarning: return MB_OK | MB_ICONWARNING; break;
-	case LogError: return MB_OK | MB_ICONERROR; break;
+	case LogDebug: return 0;
+	case LogNotification: return 0;
+	case LogInformation: return MB_ICONINFORMATION;
+	case LogWarning: return MB_ICONWARNING;
+	case LogError: return MB_ICONERROR;
 	}
-	return MB_OK;
+	return 0;
 }
 
 
@@ -80,70 +84,264 @@
 		text.SetAt(uint16_max - 2, _T('.'));
 		text.SetAt(uint16_max - 3, _T('.'));
 	}
-	UINT result = ::MessageBox(parent->GetSafeHwnd(), text, caption.IsEmpty() ? CString(MAINFRAME_TITLE) : caption, flags);
+	UINT result = ::MessageBox(parent->GetSafeHwnd(), text, caption.IsEmpty() ? CString{MAINFRAME_TITLE} : caption, flags);
 	return result;
 }
 
 
+static const TCHAR *GetButtonString(UINT id)
+{
+	mpt::Library user32{mpt::LibraryPath::System(P_("user32"))};
+	using PMB_GETSTRING = LPCWSTR(WINAPI *)(UINT);
+	PMB_GETSTRING MB_GetString = nullptr;
+	user32.Bind(MB_GetString, "MB_GetString");
+	if(MB_GetString)
+		return MB_GetString(id - 1);
+
+	switch(id)
+	{
+	case IDOK: return _T("&OK");
+	case IDCANCEL: return _T("&Cancel");
+	case IDABORT: return _T("&Abort");
+	case IDRETRY: return _T("&Retry");
+	case IDIGNORE: return _T("&Ignore");
+	case IDYES: return _T("&Yes");
+	case IDNO: return _T("&No");
+	case IDCLOSE: return _T("&Close");
+	case IDHELP: return _T("Help");
+	case IDTRYAGAIN: return _T("&Try Again");
+	case IDCONTINUE: return _T("&Continue");
+	default: return _T("");
+	}
+}
+
+
+bool TaskDlg::ModernTaskDialogSupported()
+{
+#if MPT_WINNT_AT_LEAST(MPT_WIN_VISTA) && defined(UNICODE)
+	return CTaskDialog::IsSupported()
+		&& !(mpt::OS::Windows::IsWine() && theApp.GetWineVersion()->Version().IsBefore(mpt::OS::Wine::Version(3, 13, 0)));
+#else
+	return false;
+#endif
+}
+
+
+TaskDlg::Result TaskDlg::DoModal(const CWnd *parent) const
+{
+	if(!parent)
+		parent = CMainFrame::GetActiveWindow();
+
+#if MPT_WINNT_AT_LEAST(MPT_WIN_VISTA) && defined(UNICODE)
+	if(ModernTaskDialogSupported())
+	{
+		CTaskDialog taskDialog{
+			description,
+			headline,
+			windowTitle.IsEmpty() ? GetTitle() : windowTitle,
+			buttons.empty() ? TDCBF_OK_BUTTON : 0,
+			bigButtons ? TDF_ENABLE_HYPERLINKS | TDF_USE_COMMAND_LINKS : 0};
+
+		if(icon == MB_ICONINFORMATION)
+			taskDialog.SetMainIcon(TD_INFORMATION_ICON);
+		else if(icon == MB_ICONWARNING)
+			taskDialog.SetMainIcon(TD_WARNING_ICON);
+		else if(icon == MB_ICONERROR)
+			taskDialog.SetMainIcon(TD_ERROR_ICON);
+		else if(icon == MB_ICONQUESTION)
+			taskDialog.SetMainIcon(TD_INFORMATION_ICON);  // theApp.LoadStandardIcon(IDI_QUESTION) - this icon hasn't been updated since Vista, and shouldn't be used according to guidelines anyway...
+
+		for(const auto &button : buttons)
+		{
+			taskDialog.AddCommandControl(button.commandId, button.text);
+		}
+
+		if(!verificationText.IsEmpty())
+		{
+			taskDialog.SetVerificationCheckboxText(verificationText);
+			taskDialog.SetVerificationCheckbox(verificationChecked);
+		}
+
+		if(defaultButton)
+			taskDialog.SetDefaultCommandControl(defaultButton);
+
+		UINT result = static_cast<UINT>(taskDialog.DoModal(parent->GetSafeHwnd()));
+		return {result, !verificationText.IsEmpty() ? !!taskDialog.GetVerificationCheckboxState() : false};
+	}
+#endif
+
+	// Fallback using regular MessageBox
+	UINT flags = MB_OK;
+	if(buttons.size() <= 1)
+	{
+		flags = MB_OK;
+	} else if(buttons.size() == 2)
+	{
+		uint32 hasButton = 0;
+		for(const auto &button : buttons)
+		{
+			hasButton |= (1 << button.commandId);
+		}
+		if(hasButton == ((1 << IDOK) | (1 << IDCANCEL)))
+			flags = MB_OKCANCEL;
+		else if(hasButton == ((1 << IDYES) | (1 << IDNO)))
+			flags = MB_YESNO;
+		else if(hasButton == ((1 << IDRETRY) | (1 << IDCANCEL)))
+			flags = MB_RETRYCANCEL;
+		else
+			flags = MB_OKCANCEL;
+	} else if(buttons.size() == 3)
+	{
+		flags = MB_YESNOCANCEL;
+	}
+
+	if(defaultButton != IDOK)
+	{
+		if(defaultButton == IDCANCEL)
+			flags |= MB_DEFBUTTON3;
+		else if(defaultButton == IDNO)
+			flags |= MB_DEFBUTTON2;
+	}
+
+	CString messageText = description;
+	if(!headline.IsEmpty())
+		messageText = headline + _T("\n\n") + messageText;
+
+	// Workaround MessageBox text length limitation: Better show a truncated string than no message at all.
+	if(messageText.GetLength() > uint16_max)
+	{
+		messageText.Truncate(uint16_max);
+		messageText.SetAt(uint16_max - 1, _T('.'));
+		messageText.SetAt(uint16_max - 2, _T('.'));
+		messageText.SetAt(uint16_max - 3, _T('.'));
+	}
+	UINT result = ::MessageBox(parent->GetSafeHwnd(), messageText, windowTitle.IsEmpty() ? GetTitle() : windowTitle, flags | icon);
+	return {result, false};
+}
+
+
 void Reporting::Notification(const AnyStringLocale &text, const CWnd *parent)
 {
-	ShowNotificationImpl(mpt::ToCString(text), FillEmptyCaption(CString(), LogNotification), LogLevelToFlags(LogNotification), parent);
+	TaskDlg{}
+		.Description(mpt::ToCString(text))
+		.WindowTitle(FillEmptyCaption({}, LogNotification))
+		.AddButton(GetButtonString(IDOK), IDOK)
+		.DoModal(parent);
 }
 void Reporting::Notification(const AnyStringLocale &text, const AnyStringLocale &caption, const CWnd *parent)
 {
-	ShowNotificationImpl(mpt::ToCString(text), FillEmptyCaption(mpt::ToCString(caption), LogNotification), LogLevelToFlags(LogNotification), parent);
+	TaskDlg{}
+		.Description(mpt::ToCString(text))
+		.WindowTitle(FillEmptyCaption(mpt::ToCString(caption), LogNotification))
+		.AddButton(GetButtonString(IDOK), IDOK)
+		.DoModal(parent);
 }
 
 
 void Reporting::Information(const AnyStringLocale &text, const CWnd *parent)
 {
-	ShowNotificationImpl(mpt::ToCString(text), FillEmptyCaption(CString(), LogInformation), LogLevelToFlags(LogInformation), parent);
+	TaskDlg{}
+		.Description(mpt::ToCString(text))
+		.WindowTitle(FillEmptyCaption({}, LogInformation))
+		.AddButton(GetButtonString(IDOK), IDOK)
+		.Icon(MB_ICONINFORMATION)
+		.DoModal(parent);
 }
 void Reporting::Information(const AnyStringLocale &text, const AnyStringLocale &caption, const CWnd *parent)
 {
-	ShowNotificationImpl(mpt::ToCString(text), FillEmptyCaption(mpt::ToCString(caption), LogInformation), LogLevelToFlags(LogInformation), parent);
+	TaskDlg{}
+		.Description(mpt::ToCString(text))
+		.WindowTitle(FillEmptyCaption(mpt::ToCString(caption), LogInformation))
+		.AddButton(GetButtonString(IDOK), IDOK)
+		.Icon(MB_ICONINFORMATION)
+		.DoModal(parent);
 }
 
 
 void Reporting::Warning(const AnyStringLocale &text, const CWnd *parent)
 {
-	ShowNotificationImpl(mpt::ToCString(text), FillEmptyCaption(CString(), LogWarning), LogLevelToFlags(LogWarning), parent);
+	TaskDlg{}
+		.Description(mpt::ToCString(text))
+		.WindowTitle(FillEmptyCaption({}, LogWarning))
+		.AddButton(GetButtonString(IDOK), IDOK)
+		.Icon(MB_ICONWARNING)
+		.DoModal(parent);
 }
 void Reporting::Warning(const AnyStringLocale &text, const AnyStringLocale &caption, const CWnd *parent)
 {
-	ShowNotificationImpl(mpt::ToCString(text), FillEmptyCaption(mpt::ToCString(caption), LogWarning), LogLevelToFlags(LogWarning), parent);
+	TaskDlg{}
+		.Description(mpt::ToCString(text))
+		.WindowTitle(FillEmptyCaption(mpt::ToCString(caption), LogWarning))
+		.AddButton(GetButtonString(IDOK), IDOK)
+		.Icon(MB_ICONWARNING)
+		.DoModal(parent);
 }
 
 
 void Reporting::Error(const AnyStringLocale &text, const CWnd *parent)
 {
-	ShowNotificationImpl(mpt::ToCString(text), FillEmptyCaption(CString(), LogError), LogLevelToFlags(LogError), parent);
+	TaskDlg{}
+		.Description(mpt::ToCString(text))
+		.WindowTitle(FillEmptyCaption({}, LogError))
+		.AddButton(GetButtonString(IDOK), IDOK)
+		.Icon(MB_ICONERROR)
+		.DoModal(parent);
 }
 void Reporting::Error(const AnyStringLocale &text, const AnyStringLocale &caption, const CWnd *parent)
 {
-	ShowNotificationImpl(mpt::ToCString(text), FillEmptyCaption(mpt::ToCString(caption), LogError), LogLevelToFlags(LogError), parent);
+	TaskDlg{}
+		.Description(mpt::ToCString(text))
+		.WindowTitle(FillEmptyCaption(mpt::ToCString(caption), LogError))
+		.AddButton(GetButtonString(IDOK), IDOK)
+		.Icon(MB_ICONERROR)
+		.DoModal(parent);
 }
 
 
 void Reporting::Message(LogLevel level, const AnyStringLocale &text, const CWnd *parent)
 {
-	ShowNotificationImpl(mpt::ToCString(text), FillEmptyCaption(CString(), level), LogLevelToFlags(level), parent);
+	TaskDlg{}
+		.Description(mpt::ToCString(text))
+		.WindowTitle(FillEmptyCaption({}, level))
+		.AddButton(GetButtonString(IDOK), IDOK)
+		.Icon(LogLevelToIcon(level))
+		.DoModal(parent);
 }
 void Reporting::Message(LogLevel level, const AnyStringLocale &text, const AnyStringLocale &caption, const CWnd *parent)
 {
-	ShowNotificationImpl(mpt::ToCString(text), FillEmptyCaption(mpt::ToCString(caption), level), LogLevelToFlags(level), parent);
+	TaskDlg{}
+		.Description(mpt::ToCString(text))
+		.WindowTitle(FillEmptyCaption(mpt::ToCString(caption), level))
+		.AddButton(GetButtonString(IDOK), IDOK)
+		.Icon(LogLevelToIcon(level))
+		.DoModal(parent);
 }
 
 
 ConfirmAnswer Reporting::Confirm(const AnyStringLocale &text, bool showCancel, bool defaultNo, const CWnd *parent)
 {
-	return Confirm(mpt::ToCString(text), GetTitle() + _T(" - Confirmation"), showCancel, defaultNo, parent);
+	return Confirm(text, GetTitle() + _T(" - Confirmation"), showCancel, defaultNo, parent);
 }
+ConfirmAnswer Reporting::Confirm(const AnyStringLocale &text, const AnyStringLocale &caption, bool showCancel, bool defaultNo, const CWnd *parent)
+{
+	std::vector<TaskDlg::Button> buttonList =
+	{
+		{GetButtonString(IDYES), IDYES},
+		{GetButtonString(IDNO), IDNO}
+	};
+	if(showCancel)
+		buttonList.push_back({GetButtonString(IDCANCEL), IDCANCEL});
 
+	TaskDlg params = TaskDlg{}
+		.Description(mpt::ToCString(text))
+		.WindowTitle(FillEmptyCaption(mpt::ToCString(caption)))
+		.Buttons(buttonList)
+		.Icon(MB_ICONQUESTION);
 
-ConfirmAnswer Reporting::Confirm(const AnyStringLocale &text, const AnyStringLocale &caption, bool showCancel, bool defaultNo, const CWnd *parent)
-{
-	UINT result = ShowNotificationImpl(mpt::ToCString(text), FillEmptyCaption(mpt::ToCString(caption)), (showCancel ? MB_YESNOCANCEL : MB_YESNO) | MB_ICONQUESTION | (defaultNo ? MB_DEFBUTTON2 : 0), parent);
+	if(defaultNo)
+		params.DefaultButton(IDNO);
+
+	auto result = params.DoModal(parent).buttonId;
 	switch(result)
 	{
 	case IDYES:
@@ -155,25 +353,18 @@
 		return cnfCancel;
 	}
 }
-
-
 RetryAnswer Reporting::RetryCancel(const AnyStringLocale &text, const CWnd *parent)
 {
-	return RetryCancel(mpt::ToCString(text), GetTitle(), parent);
+	return RetryCancel(text, GetTitle(), parent);
 }
-
-
 RetryAnswer Reporting::RetryCancel(const AnyStringLocale &text, const AnyStringLocale &caption, const CWnd *parent)
 {
-	UINT result = ShowNotificationImpl(mpt::ToCString(text), FillEmptyCaption(mpt::ToCString(caption)), MB_RETRYCANCEL, parent);
-	switch(result)
-	{
-	case IDRETRY:
-		return rtyRetry;
-	default:
-	case IDCANCEL:
-		return rtyCancel;
-	}
+	auto result = TaskDlg{}
+		.Description(mpt::ToCString(text))
+		.WindowTitle(FillEmptyCaption(mpt::ToCString(caption)))
+		.Buttons({{GetButtonString(IDRETRY), IDRETRY}, {GetButtonString(IDCANCEL), IDCANCEL}})
+		.DoModal(parent).buttonId;
+	return (result == IDRETRY) ? rtyRetry : rtyCancel;
 }
 
 
Index: mptrack/Reporting.h
===================================================================
--- mptrack/Reporting.h	(revision 24083)
+++ mptrack/Reporting.h	(working copy)
@@ -12,10 +12,55 @@
 
 #include "openmpt/all/BuildSettings.hpp"
 
+#include <vector>
 
 OPENMPT_NAMESPACE_BEGIN
 
 
+struct TaskDlg
+{
+	// When assigning a custom command ID instead of IDOK/IDCANCEL/..., use this base offset
+	static constexpr int CUSTOM_ID_BASE = 200;
+
+	struct Button
+	{
+		CString text;
+		UINT commandId;
+	};
+
+	struct Result
+	{
+		UINT buttonId;
+		bool verificationChecked;
+	};
+
+	Result DoModal(const CWnd *parent = nullptr) const;
+
+	static bool ModernTaskDialogSupported();
+
+	TaskDlg &WindowTitle(CString title) { windowTitle = std::move(title); return *this; }
+	TaskDlg &Headline(CString text) { headline = std::move(text); return *this; }
+	TaskDlg &Description(CString text) { description = std::move(text); return *this; }
+	TaskDlg &AddButton(CString text, UINT commandID) { buttons.push_back({std::move(text), commandID}); return *this; }
+	TaskDlg &Buttons(std::vector<Button> buttonList) { buttons = std::move(buttonList); return *this; }
+	TaskDlg &DefaultButton(int commandID) { defaultButton = commandID; return *this; }
+	TaskDlg &Icon(UINT iconType) { icon = iconType; return *this; }
+	TaskDlg &VerificationText(CString text) { verificationText = std::move(text); return *this; }
+	TaskDlg &VerificationChecked(bool checked) { verificationChecked = checked; return *this; }
+	TaskDlg &UseBigButtons(bool useBigButtons) { bigButtons = useBigButtons; return *this; }
+
+protected:
+	CString windowTitle;
+	CString headline;
+	CString description;
+	CString verificationText;
+	std::vector<Button> buttons;
+	int defaultButton = 0;
+	UINT icon = 0;
+	bool verificationChecked = false;
+	bool bigButtons = false;
+};
+
 enum ConfirmAnswer
 {
 	cnfYes,
@@ -31,42 +76,39 @@
 };
 
 
-class Reporting
+namespace Reporting
 {
-
-public:
 	// Show a simple notification
-	static void Notification(const AnyStringLocale &text, const CWnd *parent = nullptr);
-	static void Notification(const AnyStringLocale &text, const AnyStringLocale &caption, const CWnd *parent = nullptr);
+	void Notification(const AnyStringLocale &text, const CWnd *parent = nullptr);
+	void Notification(const AnyStringLocale &text, const AnyStringLocale &caption, const CWnd *parent = nullptr);
 
 	// Show a simple information
-	static void Information(const AnyStringLocale &text, const CWnd *parent = nullptr);
-	static void Information(const AnyStringLocale &text, const AnyStringLocale &caption, const CWnd *parent = nullptr);
+	void Information(const AnyStringLocale &text, const CWnd *parent = nullptr);
+	void Information(const AnyStringLocale &text, const AnyStringLocale &caption, const CWnd *parent = nullptr);
 
 	// Show a simple warning
-	static void Warning(const AnyStringLocale &text, const CWnd *parent = nullptr);
-	static void Warning(const AnyStringLocale &text, const AnyStringLocale &caption, const CWnd *parent = nullptr);
+	void Warning(const AnyStringLocale &text, const CWnd *parent = nullptr);
+	void Warning(const AnyStringLocale &text, const AnyStringLocale &caption, const CWnd *parent = nullptr);
 
 	// Show an error box.
-	static void Error(const AnyStringLocale &text, const CWnd *parent = nullptr);
-	static void Error(const AnyStringLocale &text, const AnyStringLocale &caption, const CWnd *parent = nullptr);
+	void Error(const AnyStringLocale &text, const CWnd *parent = nullptr);
+	void Error(const AnyStringLocale &text, const AnyStringLocale &caption, const CWnd *parent = nullptr);
 
 	// Simplified version of the above
-	static void Message(LogLevel level, const AnyStringLocale &text, const CWnd *parent = nullptr);
-	static void Message(LogLevel level, const AnyStringLocale &text, const AnyStringLocale &caption, const CWnd *parent = nullptr);
+	void Message(LogLevel level, const AnyStringLocale &text, const CWnd *parent = nullptr);
+	void Message(LogLevel level, const AnyStringLocale &text, const AnyStringLocale &caption, const CWnd *parent = nullptr);
 
 	// Show a confirmation dialog.
-	static ConfirmAnswer Confirm(const AnyStringLocale &text, bool showCancel = false, bool defaultNo = false, const CWnd *parent = nullptr);
-	static ConfirmAnswer Confirm(const AnyStringLocale &text, const AnyStringLocale &caption, bool showCancel = false, bool defaultNo = false, const CWnd *parent = nullptr);
+	ConfirmAnswer Confirm(const AnyStringLocale &text, bool showCancel = false, bool defaultNo = false, const CWnd *parent = nullptr);
+	ConfirmAnswer Confirm(const AnyStringLocale &text, const AnyStringLocale &caption, bool showCancel = false, bool defaultNo = false, const CWnd *parent = nullptr);
 	// work-around string literals for caption decaying to bool and catching the wrong overload instead of converting to a string.
-	static ConfirmAnswer Confirm(const AnyStringLocale &text, const char *caption, bool showCancel = false, bool defaultNo = false, const CWnd *parent = nullptr) { return Confirm(text, AnyStringLocale(caption), showCancel, defaultNo, parent); }
-	static ConfirmAnswer Confirm(const AnyStringLocale &text, const wchar_t *caption, bool showCancel = false, bool defaultNo = false, const CWnd *parent = nullptr) { return Confirm(text, AnyStringLocale(caption), showCancel, defaultNo, parent); }
-	static ConfirmAnswer Confirm(const AnyStringLocale &text, const CString &caption, bool showCancel = false, bool defaultNo = false, const CWnd *parent = nullptr) { return Confirm(text, AnyStringLocale(caption), showCancel, defaultNo, parent); }
+	inline ConfirmAnswer Confirm(const AnyStringLocale &text, const char *caption, bool showCancel = false, bool defaultNo = false, const CWnd *parent = nullptr) { return Confirm(text, AnyStringLocale(caption), showCancel, defaultNo, parent); }
+	inline ConfirmAnswer Confirm(const AnyStringLocale &text, const wchar_t *caption, bool showCancel = false, bool defaultNo = false, const CWnd *parent = nullptr) { return Confirm(text, AnyStringLocale(caption), showCancel, defaultNo, parent); }
+	inline ConfirmAnswer Confirm(const AnyStringLocale &text, const CString &caption, bool showCancel = false, bool defaultNo = false, const CWnd *parent = nullptr) { return Confirm(text, AnyStringLocale(caption), showCancel, defaultNo, parent); }
 
 	// Show a confirmation dialog.
-	static RetryAnswer RetryCancel(const AnyStringLocale &text, const CWnd *parent = nullptr);
-	static RetryAnswer RetryCancel(const AnyStringLocale &text, const AnyStringLocale &caption, const CWnd *parent = nullptr);
-
+	RetryAnswer RetryCancel(const AnyStringLocale &text, const CWnd *parent = nullptr);
+	RetryAnswer RetryCancel(const AnyStringLocale &text, const AnyStringLocale &caption, const CWnd *parent = nullptr);
 };
 
 
TaskDialog.patch (20,882 bytes)   

Issue History

Date Modified Username Field Change
2021-01-16 15:16 Saga Musix New Issue
2021-01-16 15:17 Saga Musix Note Added: 0004612
2021-01-16 15:56 Saga Musix Note Added: 0004613
2021-01-16 15:58 Saga Musix Note Edited: 0004613
2021-01-20 09:34 manx Target Version => OpenMPT 1.31.01.00 / libopenmpt 0.7.0 (upgrade first)
2022-02-16 17:36 manx Note Added: 0005097
2022-02-16 17:57 Saga Musix Note Added: 0005098
2022-02-16 18:06 manx Note Added: 0005099
2022-02-18 23:12 Saga Musix Note Added: 0005103
2023-03-19 16:12 Saga Musix Target Version OpenMPT 1.31.01.00 / libopenmpt 0.7.0 (upgrade first) => OpenMPT 1.32.01.00 / libopenmpt 0.8.0 (upgrade first)
2023-04-07 19:46 manx Relationship added related to 0001687
2024-11-10 16:10 Saga Musix Note Added: 0006166
2025-03-23 16:22 Saga Musix Target Version OpenMPT 1.32.01.00 / libopenmpt 0.8.0 (upgrade first) => OpenMPT 1.33 / libopenmpt 0.9 (goals)
2025-08-27 19:28 Saga Musix Note Added: 0006453
2025-08-27 19:28 Saga Musix File Added: TaskDialog.patch