summaryrefslogtreecommitdiff
path: root/plugins/TabSRMM/src/warning.cpp
blob: 0c09468c68f6009fbcbd1ceb121ca9ca3287d67f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
/*
Copyright (C) 2012-22 Miranda NG team (https://miranda-ng.org)

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation version 2
of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include "stdafx.h"

using namespace CWarning;

static MWindowList hWindowList;

class CWarningImpl
{
	ptrW     m_szTitle, m_szText;
	UINT     m_uId;
	HFONT    m_hFontCaption = nullptr;
	uint32_t m_dwFlags;
	HWND     m_hwnd = nullptr;
	bool     m_fIsModal;

public:
	CWarningImpl(const wchar_t *tszTitle, const wchar_t *tszText, const UINT uId, const uint32_t dwFlags) :
		m_szTitle(mir_wstrdup(tszTitle)),
		m_szText(mir_wstrdup(tszText))
	{
		m_uId = uId;
		m_dwFlags = dwFlags;
		m_fIsModal = ((m_dwFlags & MB_YESNO || m_dwFlags & MB_YESNOCANCEL) ? true : false);
	}

	~CWarningImpl()
	{
		if (m_hFontCaption)
			::DeleteObject(m_hFontCaption);
	}

	// static function to construct and show the dialog, returns the user's choice
	LRESULT ShowDialog() const
	{
		if (!m_fIsModal) {
			::CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_WARNING), nullptr, stubDlgProc, LPARAM(this));
			return 0;
		}

		return ::DialogBoxParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_WARNING), nullptr, stubDlgProc, LPARAM(this));
	}

	/////////////////////////////////////////////////////////////////////////////////////////
	// stub dlg procedure.Just register the object pointer in WM_INITDIALOG

	static INT_PTR CALLBACK stubDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
	{
		CWarningImpl *w = reinterpret_cast<CWarningImpl *>(::GetWindowLongPtr(hwnd, GWLP_USERDATA));
		if (w)
			return(w->dlgProc(hwnd, msg, wParam, lParam));

		switch (msg) {
		case WM_INITDIALOG:
			w = reinterpret_cast<CWarningImpl *>(lParam);
			if (w) {
				::SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam);
				return(w->dlgProc(hwnd, msg, wParam, lParam));
			}
			break;
		}
		return FALSE;
	}

	/////////////////////////////////////////////////////////////////////////////////////////
	// dialog procedure for the warning dialog box

	INT_PTR CALLBACK dlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
	{
		switch (msg) {
		case WM_INITDIALOG:
			m_hwnd = hwnd;

			::SetWindowTextW(hwnd, TranslateT("TabSRMM warning message"));
			::Window_SetSkinIcon_IcoLib(hwnd, SKINICON_OTHER_MIRANDA);
			::SendDlgItemMessage(hwnd, IDC_WARNTEXT, EM_AUTOURLDETECT, TRUE, 0);
			::SendDlgItemMessage(hwnd, IDC_WARNTEXT, EM_SETEVENTMASK, 0, ENM_LINK);

			TranslateDialogDefault(hwnd);
			{
				CMStringW str(FORMAT, RTF_DEFAULT_HEADER, 0, 0, 0, 30 * 15);
				str.Append(m_szText);
				str.Append(L"}");
				str.Replace(L"\n", L"\\line ");
				SETTEXTEX stx = {ST_SELECTION, CP_UTF8};
				::SendDlgItemMessage(hwnd, IDC_WARNTEXT, EM_SETTEXTEX, (WPARAM)&stx, T2Utf(str));

				::SetDlgItemTextW(hwnd, IDC_CAPTION, m_szTitle);

				if (m_dwFlags & CWF_NOALLOWHIDE)
					Utils::showDlgControl(hwnd, IDC_DONTSHOWAGAIN, SW_HIDE);
				if (m_dwFlags & MB_YESNO || m_dwFlags & MB_YESNOCANCEL) {
					Utils::showDlgControl(hwnd, IDOK, SW_HIDE);
					::SetFocus(::GetDlgItem(hwnd, IDCANCEL));
				}
				else {
					Utils::showDlgControl(hwnd, IDCANCEL, SW_HIDE);
					Utils::showDlgControl(hwnd, IDYES, SW_HIDE);
					Utils::showDlgControl(hwnd, IDNO, SW_HIDE);
					::SetFocus(::GetDlgItem(hwnd, IDOK));
				}

				UINT uResId = 0;
				if ((m_dwFlags & MB_ICONERROR) || (m_dwFlags & MB_ICONHAND))
					uResId = 32513;
				else if ((m_dwFlags & MB_ICONEXCLAMATION) || (m_dwFlags & MB_ICONWARNING))
					uResId = 32515;
				else if ((m_dwFlags & MB_ICONASTERISK) || (m_dwFlags & MB_ICONINFORMATION))
					uResId = 32516;
				else if (m_dwFlags & MB_ICONQUESTION)
					uResId = 32514;

				HICON hIcon;
				if (uResId)
					hIcon = reinterpret_cast<HICON>(::LoadImage(nullptr, MAKEINTRESOURCE(uResId), IMAGE_ICON, 0, 0, LR_SHARED));
				else
					hIcon = ::Skin_LoadIcon(SKINICON_EVENT_MESSAGE, true);
				::SendDlgItemMessageW(hwnd, IDC_WARNICON, STM_SETICON, reinterpret_cast<WPARAM>(hIcon), 0);

				if (!(m_dwFlags & MB_YESNO || m_dwFlags & MB_YESNOCANCEL))
					::ShowWindow(hwnd, SW_SHOWNORMAL);

				WindowList_Add(hWindowList, hwnd, (UINT_PTR)hwnd);
			}
			return TRUE;

		case WM_CTLCOLORSTATIC:
			{
				HWND hwndChild = reinterpret_cast<HWND>(lParam);
				UINT id = ::GetDlgCtrlID(hwndChild);
				if (nullptr == m_hFontCaption) {
					HFONT hFont = reinterpret_cast<HFONT>(::SendDlgItemMessage(hwnd, IDC_CAPTION, WM_GETFONT, 0, 0));
					LOGFONT lf = {0};

					::GetObject(hFont, sizeof(lf), &lf);
					lf.lfHeight = (int)((double)lf.lfHeight * 1.7f);
					m_hFontCaption = ::CreateFontIndirect(&lf);
					::SendDlgItemMessage(hwnd, IDC_CAPTION, WM_SETFONT, (WPARAM)m_hFontCaption, FALSE);
				}

				if (IDC_CAPTION == id) {
					::SetTextColor(reinterpret_cast<HDC>(wParam), ::GetSysColor(COLOR_HIGHLIGHT));
					::SendMessage(hwndChild, WM_SETFONT, (WPARAM)m_hFontCaption, FALSE);
				}

				if (IDC_WARNGROUP != id && IDC_DONTSHOWAGAIN != id) {
					::SetBkColor((HDC)wParam, ::GetSysColor(COLOR_WINDOW));
					return reinterpret_cast<INT_PTR>(::GetSysColorBrush(COLOR_WINDOW));
				}
			}
			break;

		case WM_COMMAND:
			switch (LOWORD(wParam)) {
			case IDOK:
			case IDYES:
			case IDNO:
				if (::IsDlgButtonChecked(hwnd, IDC_DONTSHOWAGAIN)) {
					uint32_t newVal = M.GetDword("cWarningsL", 0) | ((uint32_t)1L << m_uId);
					db_set_dw(0, SRMSGMOD_T, "cWarningsL", newVal);

					if (LOWORD(wParam) != IDNO) {
						newVal = M.GetDword("cWarningsV", 0) | ((uint32_t)1L << m_uId);
						db_set_dw(0, SRMSGMOD_T, "cWarningsV", newVal);
					}
				}
				__fallthrough;

			case IDCANCEL:
				if (!m_fIsModal && (IDOK == LOWORD(wParam) || IDCANCEL == LOWORD(wParam))) // modeless dialogs can receive a IDCANCEL from destroyAll()
					::DestroyWindow(hwnd);
				else
					::EndDialog(hwnd, LOWORD(wParam));
				break;
			}
			break;

		case WM_NOTIFY:
			switch (((NMHDR *)lParam)->code) {
			case EN_LINK:
				switch (((ENLINK *)lParam)->msg) {
				case WM_LBUTTONUP:
					ENLINK *e = reinterpret_cast<ENLINK *>(lParam);

					const wchar_t *wszUrl = Utils::extractURLFromRichEdit(e, ::GetDlgItem(hwnd, IDC_WARNTEXT));
					if (wszUrl) {
						Utils_OpenUrlW(wszUrl);
						mir_free(const_cast<wchar_t *>(wszUrl));
					}
				}
			}
			break;

		case WM_DESTROY:
			::SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
			delete this;

			WindowList_Remove(hWindowList, hwnd);
			Window_FreeIcon_IcoLib(hwnd);
			break;
		}

		return FALSE;
	}
};

/////////////////////////////////////////////////////////////////////////////////////////
// implementation of the CWarningImpl class
//
// IMPORTANT note to translators for translation of the warning dialogs:
// 
//  Make sure to NOT remove the pipe character ( | ) from the strings. This separates the
//  warning title from the actual warning text.
// 
//  Also, do NOT insert multiple | characters in the translated string. Not well-formatted
//  warnings cannot be translated and the plugin will show the untranslated versions.
// 
//  strings marked with a NOT TRANSLATABLE comment cannot be translated at all. This
//  will be used for important and critical error messages only.
// 
//  some strings are empty, this is intentional and used for error messages that share
//  the message with other possible error notifications (popups, tool tips etc.)
// 
//  Entries that do not use the LPGENW() macro are NOT TRANSLATABLE, so don't bother translating them.

static wchar_t *Warnings[] = {
	nullptr,
	LPGENW("Save file|Unable to save temporary file"), // WARN_SAVEFILE 
	LPGENW("Edit user notes|You are editing the user notes. Click the button again or use the hotkey (default: Alt+N) to save the notes and return to normal messaging mode"),  /* WARN_EDITUSERNOTES */
	LPGENW("Missing component|The icon pack is missing. Please install it to the default icons folder.\n\nNo icons will be available"),		/* WARN_ICONPACKMISSING */
	LPGENW("Aero peek warning|You have enabled Aero Peek features and loaded a custom container window skin\n\nThis can result in minor visual anomalies in the live preview feature."),	/* WARN_AEROPEEKSKIN */
	LPGENW("File transfer problem|Sending the image by file transfer failed.\n\nPossible reasons: File transfers not supported, either you or the target contact is offline, or you are invisible and the target contact is not on your visibility list."), /* WARN_IMGSVC_MISSING */
	LPGENW("Settings problem|The option \\b1 History -> Imitate IEView API\\b0  is enabled and the History++ plugin is active. This can cause problems when using IEView as message log viewer.\n\nShould I correct the option (a restart is required)?"), /* WARN_HPP_APICHECK */
	LPGENW("Configuration issue|The unattended send feature is disabled. The \\b1 send later\\b0  and \\b1 send to multiple contacts\\b0  features depend on it.\n\nYou must enable it under \\b1Options -> Message sessions -> Advanced tweaks\\b0. Changing this option requires a restart."), /* WARN_NO_SENDLATER */
	LPGENW("Closing Window|You are about to close a window with multiple tabs open.\n\nProceed?"),		/* WARN_CLOSEWINDOW */
	LPGENW("Closing options dialog|To reflect the changes done by importing a theme in the options dialog, the dialog must be closed after loading a theme \\b1 and unsaved changes might be lost\\b0 .\n\nDo you want to continue?"), /* WARN_OPTION_CLOSE */
	LPGENW("Loading a theme|Loading a color and font theme can overwrite the settings defined by your skin.\n\nDo you want to continue?"), /* WARN_THEME_OVERWRITE */
};

/////////////////////////////////////////////////////////////////////////////////////////
// send cancel message to all open warning dialogs so they are destroyed
// before TabSRMM is unloaded.
// 
// called by the OkToExit handler in globals.cpp

void CWarning::destroyAll()
{
	if (hWindowList)
		WindowList_Broadcast(hWindowList, WM_COMMAND, MAKEWPARAM(IDCANCEL, 0), 0);
}

/////////////////////////////////////////////////////////////////////////////////////////
// show a warning dialog using the id value. Check whether the user has chosen to
// not show this message again. This has room for 64 different warning dialogs, which
// should be enough in the first place. Extending it should not be too hard though.

LRESULT CWarning::show(const int uId, uint32_t dwFlags, const wchar_t *tszTxt)
{
	if (hWindowList == nullptr)
		hWindowList = WindowList_Create();

	// don't open new warnings when shutdown was initiated (modal ones will otherwise
	// block the shutdown)
	if (CMimAPI::m_shutDown)
		return -1;

	wchar_t *_s = nullptr;
	if (tszTxt)
		_s = const_cast<wchar_t *>(tszTxt);
	else {
		if (uId == -1)
			return -1;

		if (dwFlags & CWF_UNTRANSLATED)
			_s = TranslateW(Warnings[uId]);
		else {
			// revert to untranslated warning when the translated message
			// is not well-formatted.
			_s = TranslateW(Warnings[uId]);

			if (mir_wstrlen(_s) < 3 || nullptr == wcschr(_s, '|'))
				_s = TranslateW(Warnings[uId]);
		}
	}

	if (mir_wstrlen(_s) > 3 && wcschr(_s, '|') != nullptr) {
		if (uId >= 0 && !(dwFlags & CWF_NOALLOWHIDE)) {
			uint32_t val = M.GetDword("cWarningsL", 0);
			uint32_t mask = ((__int64)1L) << uId;
			if (mask & val) {
				bool bResult = (M.GetDword("cWarningsV", 0) & mask) != 0;
				if (dwFlags & MB_YESNO || dwFlags & MB_YESNOCANCEL)
					return (bResult) ? IDYES : IDNO;
				return IDOK;
			}
		}

		ptrW s(mir_wstrdup(_s));
		wchar_t *separator_pos = wcschr(s, '|');

		if (separator_pos) {
			*separator_pos = 0;

			CWarningImpl *w = new CWarningImpl(s, separator_pos + 1, uId, dwFlags);
			if (dwFlags & MB_YESNO || dwFlags & MB_YESNOCANCEL)
				return w->ShowDialog();

			w->ShowDialog();
		}
	}
	return -1;
}