diff --git a/.gitignore b/.gitignore index b8373ae1b..2e7fd5e8e 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,99 @@ Thumbs.db /src/plugins/automation/generated/salamander_h.h /src/plugins/automation/generated/salamander_i.c /src/plugins/automation/generated/salamander_p.c +/src/plugins/7zip/vcxproj/7ZA/salamander/Debug_x64/plugins/7zip +/src/plugins/7zip/vcxproj/7ZA/salamander/Release_x64/plugins/7zip +/src/plugins/7zip/vcxproj/7zwrapper/salamander/Debug_x64/plugins/7zip +/src/plugins/7zip/vcxproj/7zwrapper/salamander/Release_x64/plugins/7zip +/src/plugins/7zip/vcxproj/salamander/Debug_x64/plugins/7zip +/src/plugins/7zip/vcxproj/salamander/Release_x64/plugins/7zip +/src/plugins/automation/vcxproj/salamander/Debug_x64/plugins/automation +/src/plugins/automation/vcxproj/salamander/Release_x64/plugins/automation +/src/plugins/checksum/vcxproj/salamander/Debug_x64/plugins/checksum +/src/plugins/checksum/vcxproj/salamander/Release_x64/plugins/checksum +/src/plugins/checkver/vcxproj/salamander/Debug_x64/plugins/checkver +/src/plugins/checkver/vcxproj/salamander/Release_x64/plugins/checkver +/src/plugins/dbviewer/vcxproj/salamander/Debug_x64/plugins/dbviewer +/src/plugins/dbviewer/vcxproj/salamander/Release_x64/plugins/dbviewer +/src/plugins/demomenu/vcxproj/salamander/Debug_x64/plugins/demomenu +/src/plugins/demomenu/vcxproj/salamander/Release_x64/plugins/demomenu +/src/plugins/demoplug/vcxproj/salamander/Debug_x64/plugins/demoplug +/src/plugins/demoplug/vcxproj/salamander/Release_x64/plugins/demoplug +/src/plugins/demoview/vcxproj/salamander/Debug_x64/plugins/demoview +/src/plugins/demoview/vcxproj/salamander/Release_x64/plugins/demoview +/src/plugins/diskmap/vcxproj/salamander/Debug_x64/plugins/diskmap +/src/plugins/diskmap/vcxproj/salamander/Release_x64/plugins/diskmap +/src/plugins/filecomp/vcxproj/fcremote/salamander/Debug_x64/plugins/filecomp +/src/plugins/filecomp/vcxproj/fcremote/salamander/Release_x64/plugins/filecomp +/src/plugins/filecomp/vcxproj/salamander/Debug_x64/plugins/filecomp +/src/plugins/filecomp/vcxproj/salamander/Release_x64/plugins/filecomp +/src/plugins/folders/vcxproj/salamander/Debug_x64/plugins/folders +/src/plugins/folders/vcxproj/salamander/Release_x64/plugins/folders +/src/plugins/ftp/vcxproj/salamander/Debug_x64/plugins/ftp +/src/plugins/ftp/vcxproj/salamander/Release_x64/plugins/ftp +/src/plugins/ieviewer/vcxproj/salamander/Debug_x64/plugins/ieviewer +/src/plugins/ieviewer/vcxproj/salamander/Release_x64/plugins/ieviewer +/src/plugins/mmviewer/vcxproj/salamander/Debug_x64/plugins/mmviewer +/src/plugins/mmviewer/vcxproj/salamander/Release_x64/plugins/mmviewer +/src/plugins/nethood/vcxproj/salamander/Debug_x64/plugins/nethood +/src/plugins/nethood/vcxproj/salamander/Release_x64/plugins/nethood +/src/plugins/pak/vcxproj/salamander/Debug_x64/plugins/pak +/src/plugins/pak/vcxproj/salamander/Release_x64/plugins/pak +/src/plugins/peviewer/vcxproj/salamander/Debug_x64/plugins/peviewer +/src/plugins/peviewer/vcxproj/salamander/Release_x64/plugins/peviewer +/src/plugins/pictview/vcxproj/exif/salamander/Debug_x64/plugins/pictview +/src/plugins/pictview/vcxproj/exif/salamander/Release_x64/plugins/pictview +/src/plugins/pictview/vcxproj/salamander/Debug_x64/plugins/pictview +/src/plugins/pictview/vcxproj/salamander/Release_x64/plugins/pictview +/src/plugins/portables/vcxproj/salamander/Debug_x64/plugins/portables +/src/plugins/portables/vcxproj/salamander/Release_x64/plugins/portables +/src/plugins/regedt/vcxproj/salamander/Debug_x64/plugins/regedt +/src/plugins/regedt/vcxproj/salamander/Release_x64/plugins/regedt +/src/plugins/renamer/vcxproj/salamander/Debug_x64/plugins/renamer +/src/plugins/renamer/vcxproj/salamander/Release_x64/plugins/renamer +/src/plugins/splitcbn/vcxproj/salamander/Debug_x64/plugins/splitcbn +/src/plugins/splitcbn/vcxproj/salamander/Release_x64/plugins/splitcbn +/src/plugins/tar/vcxproj/salamander/Debug_x64/plugins/tar +/src/plugins/tar/vcxproj/salamander/Release_x64/plugins/tar +/src/plugins/unarj/vcxproj/salamander/Debug_x64/plugins/unarj +/src/plugins/unarj/vcxproj/salamander/Release_x64/plugins/unarj +/src/plugins/uncab/vcxproj/salamander/Debug_x64/plugins/uncab +/src/plugins/uncab/vcxproj/salamander/Release_x64/plugins/uncab +/src/plugins/unchm/vcxproj/chmlib/salamander/Debug_x64/plugins/unchm +/src/plugins/unchm/vcxproj/chmlib/salamander/Release_x64/plugins/unchm +/src/plugins/unchm/vcxproj/salamander/Debug_x64/plugins/unchm +/src/plugins/unchm/vcxproj/salamander/Release_x64/plugins/unchm +/src/plugins/undelete/vcxproj/salamander/Debug_x64/plugins/undelete +/src/plugins/undelete/vcxproj/salamander/Release_x64/plugins/undelete +/src/plugins/unfat/vcxproj/salamander/Debug_x64/plugins/unfat +/src/plugins/unfat/vcxproj/salamander/Release_x64/plugins/unfat +/src/plugins/uniso/vcxproj/salamander/Debug_x64/plugins/uniso +/src/plugins/uniso/vcxproj/salamander/Release_x64/plugins/uniso +/src/plugins/unlha/vcxproj/salamander/Debug_x64/plugins/unlha +/src/plugins/unlha/vcxproj/salamander/Release_x64/plugins/unlha +/src/plugins/unmime/vcxproj/salamander/Debug_x64/plugins/unmime +/src/plugins/unmime/vcxproj/salamander/Release_x64/plugins/unmime +/src/plugins/unole/vcxproj/salamander/Debug_x64/plugins/unole +/src/plugins/unole/vcxproj/salamander/Release_x64/plugins/unole +/src/plugins/unrar/vcxproj/salamander/Debug_x64/plugins/unrar +/src/plugins/unrar/vcxproj/salamander/Release_x64/plugins/unrar +/src/plugins/wmobile/vcxproj/salamander/Debug_x64/plugins/wmobile +/src/plugins/wmobile/vcxproj/salamander/Release_x64/plugins/wmobile +/src/plugins/zip/vcxproj/salamander/Debug_x64/plugins/zip +/src/plugins/zip/vcxproj/salamander/Release_x64/plugins/zip +/src/plugins/zip/vcxproj/zip2sfx/salamander/Debug_x86/plugins/zip/zip2sfx/Intermediate/microsoft/STL +/src/plugins/zip/vcxproj/zip2sfx/salamander/Release_x86/plugins/zip/zip2sfx/Intermediate/microsoft/STL +/src/vcxproj/salamander/Debug_x64 +/src/vcxproj/salamander/Release_x64 +/src/vcxproj/salmon/salamander/Debug_x64 +/src/vcxproj/salmon/salamander/Release_x64 +/src/vcxproj/salopen/salamander/Debug_x64/plugins/Intermediate/salopen/Intermediate/microsoft/STL +/src/vcxproj/salopen/salamander/Release_x64/plugins/Intermediate/salopen/Intermediate/microsoft/STL +/src/vcxproj/salspawn/salamander/Debug_x64/plugins/Intermediate/salspawn/Intermediate/microsoft/STL +/src/vcxproj/salspawn/salamander/Release_x64/plugins/Intermediate/salspawn/Intermediate/microsoft/STL +/src/vcxproj/shellext/salamander/Debug_x64/plugins/Intermediate/salextx64/Intermediate/microsoft/STL +/src/vcxproj/shellext/salamander/Debug_x86/plugins/Intermediate/salextx86/Intermediate/microsoft/STL +/src/vcxproj/shellext/salamander/Release_x64/plugins/Intermediate/salextx64/Intermediate/microsoft/STL +/src/vcxproj/shellext/salamander/Release_x86/plugins/Intermediate/salextx86/Intermediate/microsoft/STL +/src/vcxproj/sqlite/salamander/Debug_x64 +/src/vcxproj/sqlite/salamander/Release_x64 diff --git a/src/common/str.cpp b/src/common/str.cpp index 88b762b3b..09a34dcda 100644 --- a/src/common/str.cpp +++ b/src/common/str.cpp @@ -110,6 +110,23 @@ char* StrNCat(char* dst, const char* src, int dstSize) void InitializeCase() { int i; + if (GetACP() == CP_UTF8) + { + for (i = 0; i < 256; i++) + { + LowerCase[i] = (BYTE)i; + UpperCase[i] = (BYTE)i; + } + for (i = 'A'; i <= 'Z'; i++) + { + LowerCase[i] = (BYTE)(i - 'A' + 'a'); + } + for (i = 'a'; i <= 'z'; i++) + { + UpperCase[i] = (BYTE)(i - 'a' + 'A'); + } + return; + } for (i = 0; i < 256; i++) LowerCase[i] = (char)(UINT_PTR)CharLowerA((LPSTR)(UINT_PTR)i); for (i = 0; i < 256; i++) diff --git a/src/common/utf8wrap.cpp b/src/common/utf8wrap.cpp new file mode 100644 index 000000000..16985f7f3 --- /dev/null +++ b/src/common/utf8wrap.cpp @@ -0,0 +1,350 @@ +#include "precomp.h" + +#define UTF8WRAPPERS_IMPLEMENTATION +#include "utf8wrap.h" + +#include +#include +#include + +namespace +{ +bool UsingUtf8ACP() +{ + static int cached = -1; + if (cached == -1) + cached = (GetACP() == CP_UTF8) ? 1 : 0; + return cached == 1; +} + +struct Utf8Decoded +{ + std::wstring wide; + std::vector codepointByteEnds; + std::vector codepointWideEnds; + std::vector byteToCodepoint; +}; + +void DecodeUtf8(const char* str, int length, Utf8Decoded& out) +{ + out.wide.clear(); + out.codepointByteEnds.clear(); + out.codepointWideEnds.clear(); + if (str == NULL) + { + out.byteToCodepoint.clear(); + return; + } + if (length < 0) + length = (int)strlen(str); + out.byteToCodepoint.assign(length, -1); + + int index = 0; + int codepointIndex = 0; + while (index < length) + { + int start = index; + unsigned char c = static_cast(str[index]); + unsigned int code = 0xFFFD; + int advance = 1; + + if (c < 0x80) + { + code = c; + } + else if ((c & 0xE0) == 0xC0 && index + 1 < length) + { + unsigned char b1 = static_cast(str[index + 1]); + if ((b1 & 0xC0) == 0x80) + { + unsigned int candidate = ((c & 0x1F) << 6) | (b1 & 0x3F); + if (candidate >= 0x80) + { + code = candidate; + advance = 2; + } + } + } + else if ((c & 0xF0) == 0xE0 && index + 2 < length) + { + unsigned char b1 = static_cast(str[index + 1]); + unsigned char b2 = static_cast(str[index + 2]); + if ((b1 & 0xC0) == 0x80 && (b2 & 0xC0) == 0x80) + { + unsigned int candidate = ((c & 0x0F) << 12) | ((b1 & 0x3F) << 6) | (b2 & 0x3F); + if (candidate >= 0x800 && !(candidate >= 0xD800 && candidate <= 0xDFFF)) + { + code = candidate; + advance = 3; + } + } + } + else if ((c & 0xF8) == 0xF0 && index + 3 < length) + { + unsigned char b1 = static_cast(str[index + 1]); + unsigned char b2 = static_cast(str[index + 2]); + unsigned char b3 = static_cast(str[index + 3]); + if ((b1 & 0xC0) == 0x80 && (b2 & 0xC0) == 0x80 && (b3 & 0xC0) == 0x80) + { + unsigned int candidate = ((c & 0x07) << 18) | ((b1 & 0x3F) << 12) | ((b2 & 0x3F) << 6) | (b3 & 0x3F); + if (candidate >= 0x10000 && candidate <= 0x10FFFF) + { + code = candidate; + advance = 4; + } + } + } + + for (int b = 0; b < advance && start + b < length; ++b) + { + out.byteToCodepoint[start + b] = codepointIndex; + } + + index += advance; + out.codepointByteEnds.push_back(index); + + if (code <= 0xFFFF) + { + out.wide.push_back(static_cast(code)); + out.codepointWideEnds.push_back(static_cast(out.wide.size()) - 1); + } + else + { + code -= 0x10000; + WCHAR high = static_cast((code >> 10) + 0xD800); + WCHAR low = static_cast((code & 0x3FF) + 0xDC00); + out.wide.push_back(high); + out.wide.push_back(low); + out.codepointWideEnds.push_back(static_cast(out.wide.size()) - 1); + } + ++codepointIndex; + } +} + +bool Utf8ToWide(const char* str, int count, std::wstring& wide) +{ + if (str == NULL) + { + wide.clear(); + return true; + } + if (count == 0) + { + wide.clear(); + return true; + } + if (count == -1) + { + int required = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str, -1, NULL, 0); + if (required == 0) + required = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0); + if (required == 0) + return false; + wide.resize(required); + int converted = MultiByteToWideChar(CP_UTF8, 0, str, -1, wide.data(), required); + if (converted == 0) + return false; + if (wide[converted - 1] == L'\0') + --converted; + wide.resize(converted); + return true; + } + if (count < 0) + count = (int)strlen(str); + if (count == 0) + { + wide.clear(); + return true; + } + int required = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str, count, NULL, 0); + if (required == 0) + required = MultiByteToWideChar(CP_UTF8, 0, str, count, NULL, 0); + if (required == 0) + return false; + wide.resize(required); + int converted = MultiByteToWideChar(CP_UTF8, 0, str, count, wide.data(), required); + if (converted == 0) + return false; + if (converted < required) + wide.resize(converted); + return true; +} + +} // namespace + +BOOL WINAPI SalExtTextOutA(HDC hdc, int x, int y, UINT options, const RECT* rect, LPCSTR str, UINT count, const INT* dx) +{ + if (!UsingUtf8ACP()) + return ::ExtTextOutA(hdc, x, y, options, rect, str, count, dx); + + std::wstring wide; + if (!Utf8ToWide(str, static_cast(count), wide)) + return ::ExtTextOutA(hdc, x, y, options, rect, str, count, dx); + + return ::ExtTextOutW(hdc, x, y, options, rect, wide.empty() ? L"" : wide.c_str(), static_cast(wide.size()), dx); +} + +BOOL WINAPI SalTextOutA(HDC hdc, int x, int y, LPCSTR str, int count) +{ + if (!UsingUtf8ACP()) + return ::TextOutA(hdc, x, y, str, count); + + std::wstring wide; + if (!Utf8ToWide(str, count, wide)) + return ::TextOutA(hdc, x, y, str, count); + + return ::TextOutW(hdc, x, y, wide.empty() ? L"" : wide.c_str(), static_cast(wide.size())); +} + +BOOL WINAPI SalGetTextExtentPoint32A(HDC hdc, LPCSTR str, int count, LPSIZE size) +{ + if (!UsingUtf8ACP()) + return ::GetTextExtentPoint32A(hdc, str, count, size); + + std::wstring wide; + if (!Utf8ToWide(str, count, wide)) + return ::GetTextExtentPoint32A(hdc, str, count, size); + + return ::GetTextExtentPoint32W(hdc, wide.empty() ? L"" : wide.c_str(), static_cast(wide.size()), size); +} + +BOOL WINAPI SalGetTextExtentExPointA(HDC hdc, LPCSTR str, int count, int maxExtent, LPINT fit, LPINT dx, LPSIZE size) +{ + if (!UsingUtf8ACP()) + return ::GetTextExtentExPointA(hdc, str, count, maxExtent, fit, dx, size); + + Utf8Decoded decoded; + DecodeUtf8(str, count, decoded); + + SIZE measured = {0, 0}; + if (decoded.wide.empty()) + { + if (size) + *size = measured; + if (fit) + *fit = 0; + if (dx && count > 0) + ZeroMemory(dx, sizeof(int) * count); + return TRUE; + } + + std::vector wideDx(decoded.wide.size()); + BOOL ok = ::GetTextExtentExPointW(hdc, decoded.wide.data(), static_cast(decoded.wide.size()), maxExtent, NULL, wideDx.data(), &measured); + if (!ok) + return FALSE; + + if (size) + *size = measured; + + std::vector codepointWidths(decoded.codepointWideEnds.size()); + for (size_t i = 0; i < decoded.codepointWideEnds.size(); ++i) + { + codepointWidths[i] = wideDx[decoded.codepointWideEnds[i]]; + } + + if (fit) + { + int byteFit = 0; + if (maxExtent > 0) + { + int wideFit = 0; + for (size_t i = 0; i < wideDx.size(); ++i) + { + if (wideDx[i] <= maxExtent) + wideFit = static_cast(i) + 1; + else + break; + } + int cpFit = 0; + for (size_t i = 0; i < decoded.codepointWideEnds.size(); ++i) + { + if (decoded.codepointWideEnds[i] < wideFit) + cpFit = static_cast(i) + 1; + else + break; + } + if (cpFit > 0) + byteFit = decoded.codepointByteEnds[cpFit - 1]; + } + else if (maxExtent == 0) + { + byteFit = 0; + } + else + { + if (!decoded.codepointByteEnds.empty()) + byteFit = decoded.codepointByteEnds.back(); + } + if (count >= 0 && byteFit > count) + byteFit = count; + *fit = byteFit; + } + + if (dx) + { + if (count < 0) + count = static_cast(decoded.byteToCodepoint.size()); + for (int i = 0; i < count; ++i) + { + int cpIndex = (i >= 0 && i < static_cast(decoded.byteToCodepoint.size())) ? decoded.byteToCodepoint[i] : -1; + if (cpIndex >= 0 && cpIndex < static_cast(codepointWidths.size())) + dx[i] = codepointWidths[cpIndex]; + else + dx[i] = 0; + } + } + + return TRUE; +} + +int WINAPI SalDrawTextA(HDC hdc, LPCSTR str, int count, LPRECT rect, UINT format) +{ + if (!UsingUtf8ACP()) + return ::DrawTextA(hdc, str, count, rect, format); + + std::wstring wide; + if (!Utf8ToWide(str, count, wide)) + return ::DrawTextA(hdc, str, count, rect, format); + + return ::DrawTextW(hdc, wide.empty() ? L"" : wide.c_str(), static_cast(wide.size()), rect, format); +} + +int WINAPI SalDrawTextExA(HDC hdc, LPTSTR str, int count, LPRECT rect, UINT format, LPDRAWTEXTPARAMS params) +{ + if (!UsingUtf8ACP()) + return ::DrawTextExA(hdc, str, count, rect, format, params); + + std::wstring wide; + if (!Utf8ToWide(str, count, wide)) + return ::DrawTextExA(hdc, str, count, rect, format, params); + + std::wstring mutableWide = wide; + LPWSTR buffer = mutableWide.empty() ? nullptr : &mutableWide[0]; + if (buffer == nullptr) + { + static WCHAR zero = 0; + buffer = &zero; + } + + int res = ::DrawTextExW(hdc, buffer, static_cast(mutableWide.size()), rect, format, params); + + if ((format & DT_MODIFYSTRING) != 0 && str != NULL) + { + // attempt to propagate modifications back to the ANSI buffer + const WCHAR* wideSrc = buffer; + int bytesAvailable = count; + if (bytesAvailable < 0 && str != NULL) + bytesAvailable = (int)strlen(str) + 1; + if (bytesAvailable <= 0) + bytesAvailable = 0; + if (bytesAvailable > 0) + { + int converted = WideCharToMultiByte(CP_UTF8, 0, wideSrc, -1, str, bytesAvailable, NULL, NULL); + if (converted == 0 && bytesAvailable > 0) + str[bytesAvailable - 1] = '\0'; + } + } + + return res; +} + diff --git a/src/common/utf8wrap.h b/src/common/utf8wrap.h new file mode 100644 index 000000000..43d5b6edd --- /dev/null +++ b/src/common/utf8wrap.h @@ -0,0 +1,47 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +BOOL WINAPI SalExtTextOutA(HDC hdc, int x, int y, UINT options, const RECT* rect, LPCSTR str, UINT count, const INT* dx); +BOOL WINAPI SalTextOutA(HDC hdc, int x, int y, LPCSTR str, int count); +BOOL WINAPI SalGetTextExtentPoint32A(HDC hdc, LPCSTR str, int count, LPSIZE size); +BOOL WINAPI SalGetTextExtentExPointA(HDC hdc, LPCSTR str, int count, int maxExtent, LPINT fit, LPINT dx, LPSIZE size); +int WINAPI SalDrawTextA(HDC hdc, LPCSTR str, int count, LPRECT rect, UINT format); +int WINAPI SalDrawTextExA(HDC hdc, LPTSTR str, int count, LPRECT rect, UINT format, LPDRAWTEXTPARAMS params); + +#ifdef __cplusplus +} +#endif + +#ifndef UTF8WRAPPERS_IMPLEMENTATION + +#define ExtTextOutA SalExtTextOutA +#undef ExtTextOut +#define ExtTextOut SalExtTextOutA + +#define TextOutA SalTextOutA +#undef TextOut +#define TextOut SalTextOutA + +#define GetTextExtentPoint32A SalGetTextExtentPoint32A +#undef GetTextExtentPoint32 +#define GetTextExtentPoint32 SalGetTextExtentPoint32A + +#define GetTextExtentExPointA SalGetTextExtentExPointA +#undef GetTextExtentExPoint +#define GetTextExtentExPoint SalGetTextExtentExPointA + +#define DrawTextA SalDrawTextA +#undef DrawText +#define DrawText SalDrawTextA + +#define DrawTextExA SalDrawTextExA +#undef DrawTextEx +#define DrawTextEx SalDrawTextExA + +#endif // UTF8WRAPPERS_IMPLEMENTATION + diff --git a/src/common/winlib.h b/src/common/winlib.h index fd7266822..48a58216f 100644 --- a/src/common/winlib.h +++ b/src/common/winlib.h @@ -226,8 +226,8 @@ class CWindow : public CWindowsObject int nHeight, // window height HWND hwndParent, // handle of parent or owner window HMENU hmenu, // handle of menu or child-window identifier - HINSTANCE hinst, // handle of application instance - LPVOID lpvParam); // ukazatel na objekt vytvareneho okna + HINSTANCE hinst, // handle of application instance + LPVOID lpvParam); // ukazatel na objekt vytvareneho okna HWND CreateExW(DWORD dwExStyle, // extended window style LPCWSTR lpszClassName, // address of registered class name @@ -241,6 +241,8 @@ class CWindow : public CWindowsObject HMENU hmenu, // handle of menu or child-window identifier HINSTANCE hinst, // handle of application instance LPVOID lpvParam); // ukazatel na objekt vytvareneho okna + + void SetUnicodeWindow(BOOL unicode) { UnicodeWnd = unicode; } #endif // _UNICODE void AttachToWindow(HWND hWnd); diff --git a/src/dialogs.h b/src/dialogs.h index ac9fb6234..09e205201 100644 --- a/src/dialogs.h +++ b/src/dialogs.h @@ -37,6 +37,7 @@ class CCopyMoveDialog : public CCommonDialog int HistoryCount; BOOL DirectoryHelper; int SelectionEnd; + int SelectionEndChars; public: // 'history' urcuje, jestli dialog bude obsahovat combbox(TRUE), nebo editline (FALSE) diff --git a/src/dialogs3.cpp b/src/dialogs3.cpp index 12e28214d..8b5a69cb6 100644 --- a/src/dialogs3.cpp +++ b/src/dialogs3.cpp @@ -3,6 +3,10 @@ #include "precomp.h" +#include +#include +#include + #include "mainwnd.h" #include "plugins.h" #include "fileswnd.h" @@ -16,6 +20,174 @@ #include "worker.h" #include "menu.h" +namespace +{ +HWND ResolveComboEditControl(HWND ctrl) +{ + char className[16]; + if (GetClassName(ctrl, className, (int)ARRAYSIZE(className)) > 0) + { + if (_stricmp(className, "ComboBox") == 0) + { + COMBOBOXINFO info; + info.cbSize = sizeof(info); + if (GetComboBoxInfo(ctrl, &info) && info.hwndItem != NULL) + return info.hwndItem; + } + } + return ctrl; +} + +bool GetControlTextUtf8(HWND ctrl, char* buffer, int bufferSize) +{ + if (buffer == NULL || bufferSize <= 0) + return false; + + buffer[0] = '\0'; + + if (GetACP() != CP_UTF8) + { + SendMessage(ctrl, WM_GETTEXT, bufferSize, (LPARAM)buffer); + return true; + } + + HWND source = ResolveComboEditControl(ctrl); + + int length = GetWindowTextLengthW(source); + if (length < 0) + length = 0; + + std::vector wide(length + 1); + int copied = GetWindowTextW(source, wide.data(), length + 1); + if (copied < 0) + copied = 0; + if ((size_t)copied >= wide.size()) + wide.push_back(L'\0'); + wide[copied] = L'\0'; + + int required = WideCharToMultiByte(CP_UTF8, 0, wide.data(), copied, NULL, 0, NULL, NULL); + if (required < 0) + required = 0; + + if (required >= bufferSize) + { + if (bufferSize > 1) + { + int written = WideCharToMultiByte(CP_UTF8, 0, wide.data(), copied, buffer, bufferSize - 1, NULL, NULL); + if (written < 0) + written = 0; + buffer[written] = '\0'; + } + else + buffer[0] = '\0'; + return false; + } + + int written = WideCharToMultiByte(CP_UTF8, 0, wide.data(), copied, buffer, bufferSize, NULL, NULL); + if (written < 0) + written = 0; + if (written >= bufferSize) + written = bufferSize - 1; + buffer[written] = '\0'; + return true; +} + +bool Utf8ToWideString(const char* text, int count, std::wstring& wide); + +void SetControlTextUtf8(HWND ctrl, const char* text) +{ + if (text == NULL) + text = ""; + + if (GetACP() != CP_UTF8) + { + SendMessage(ctrl, WM_SETTEXT, 0, (LPARAM)text); + return; + } + + HWND target = ResolveComboEditControl(ctrl); + if (IsWindowUnicode(target)) + { + std::wstring wide; + if (Utf8ToWideString(text, -1, wide)) + { + SetWindowTextW(target, wide.empty() ? L"" : wide.c_str()); + return; + } + } + + SendMessage(target, WM_SETTEXT, 0, (LPARAM)text); +} + +bool Utf8ToWideString(const char* text, int count, std::wstring& wide) +{ + if (text == NULL) + { + wide.clear(); + return true; + } + + if (count == 0) + { + wide.clear(); + return true; + } + + if (count < 0) + { + int required = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text, -1, NULL, 0); + if (required == 0) + required = MultiByteToWideChar(CP_UTF8, 0, text, -1, NULL, 0); + if (required == 0) + { + wide.clear(); + return false; + } + + wide.resize(required); + int converted = MultiByteToWideChar(CP_UTF8, 0, text, -1, &wide[0], required); + if (converted == 0) + { + wide.clear(); + return false; + } + + if (!wide.empty() && wide.back() == L'\0') + wide.pop_back(); + + return true; + } + + if (count == 0) + { + wide.clear(); + return true; + } + + int required = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text, count, NULL, 0); + if (required == 0) + required = MultiByteToWideChar(CP_UTF8, 0, text, count, NULL, 0); + if (required == 0) + { + wide.clear(); + return false; + } + + wide.resize(required); + int converted = MultiByteToWideChar(CP_UTF8, 0, text, count, &wide[0], required); + if (converted == 0) + { + wide.clear(); + return false; + } + + if (converted < required) + wide.resize(converted); + + return true; +} +} // namespace + // // **************************************************************************** // CChangeCaseDlg @@ -73,7 +245,7 @@ void CConvertFilesDlg::Validate(CTransferInfo& ti) if (ti.Type == ttDataFromWindow) { char buf[MAX_PATH]; - SendMessage(hWnd, WM_GETTEXT, MAX_PATH, (LPARAM)buf); + GetControlTextUtf8(hWnd, buf, MAX_PATH); CMaskGroup mask(buf); int errorPos; if (!mask.PrepareMasks(errorPos)) @@ -109,11 +281,11 @@ void CConvertFilesDlg::Transfer(CTransferInfo& ti) { LoadComboFromStdHistoryValues(hWnd, history, CONVERT_HISTORY_SIZE); SendMessage(hWnd, CB_LIMITTEXT, MAX_PATH - 1, 0); - SendMessage(hWnd, WM_SETTEXT, 0, (LPARAM)Mask); + SetControlTextUtf8(hWnd, Mask); } else { - SendMessage(hWnd, WM_GETTEXT, MAX_PATH, (LPARAM)Mask); + GetControlTextUtf8(hWnd, Mask, MAX_PATH); AddValueToStdHistoryValues(history, CONVERT_HISTORY_SIZE, Mask, FALSE); } } @@ -312,11 +484,11 @@ void CFilterDialog::Transfer(CTransferInfo& ti) { LoadComboFromStdHistoryValues(hWnd, history, FILTER_HISTORY_SIZE); SendMessage(hWnd, CB_LIMITTEXT, MAX_PATH - 1, 0); - SendMessage(hWnd, WM_SETTEXT, 0, (LPARAM)Filter->GetMasksString()); + SetControlTextUtf8(hWnd, Filter->GetMasksString()); } else { - SendMessage(hWnd, WM_GETTEXT, MAX_PATH, (LPARAM)Filter->GetWritableMasksString()); + GetControlTextUtf8(hWnd, Filter->GetWritableMasksString(), MAX_PATH); AddValueToStdHistoryValues(history, FILTER_HISTORY_SIZE, Filter->GetMasksString(), FALSE); } } @@ -408,11 +580,19 @@ CCopyMoveDialog::CCopyMoveDialog(HWND parent, char* path, int pathBufSize, char* HistoryCount = historyCount; SetHelpID(helpID); // dialog se pouziva k vice ucelum - nastavime spravne helpID SelectionEnd = -1; // -1 = select all + SelectionEndChars = -1; } void CCopyMoveDialog::SetSelectionEnd(int selectionEnd) { SelectionEnd = selectionEnd; + SelectionEndChars = -1; + if (selectionEnd >= 0 && GetACP() == CP_UTF8 && Path != NULL) + { + std::wstring wide; + if (Utf8ToWideString(Path, selectionEnd, wide)) + SelectionEndChars = (int)wide.length(); + } } void CCopyMoveDialog::Transfer(CTransferInfo& ti) @@ -427,11 +607,11 @@ void CCopyMoveDialog::Transfer(CTransferInfo& ti) { LoadComboFromStdHistoryValues(hWnd, History, HistoryCount); SendMessage(hWnd, CB_LIMITTEXT, PathBufSize - 1, 0); - SendMessage(hWnd, WM_SETTEXT, 0, (LPARAM)Path); + SetControlTextUtf8(hWnd, Path); } else { - SendMessage(hWnd, WM_GETTEXT, PathBufSize, (LPARAM)Path); + GetControlTextUtf8(hWnd, Path, PathBufSize); AddValueToStdHistoryValues(History, HistoryCount, Path, FALSE); } } @@ -465,8 +645,29 @@ CCopyMoveDialog::DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam) INT_PTR ret = CCommonDialog::DialogProc(uMsg, wParam, lParam); // umime vybirat pouze nazev bez tecky a pripony + int selectionEnd = SelectionEnd; + if (SelectionEndChars >= 0) + { + HWND combo = GetDlgItem(HWindow, IDE_PATH); + if (combo != NULL) + { + HWND child = GetWindow(combo, GW_CHILD); + while (child != NULL) + { + char className[16]; + if (GetClassName(child, className, (int)ARRAYSIZE(className)) > 0) + { + if (_stricmp(className, "Edit") == 0) + break; + } + child = GetWindow(child, GW_HWNDNEXT); + } + if (child != NULL && IsWindowUnicode(child)) + selectionEnd = SelectionEndChars; + } + } PostMessage(GetDlgItem(HWindow, IDE_PATH), CB_SETEDITSEL, 0, - MAKELPARAM(0, SelectionEnd)); + MAKELPARAM(0, selectionEnd)); return FALSE; } @@ -542,7 +743,9 @@ MENU_TEMPLATE_ITEM EditNewFileDialogMenu[] = if (cmd == 1) { Configuration.UseEditNewFileDefault = TRUE; - SendDlgItemMessage(HWindow, IDE_PATH, WM_GETTEXT, MAX_PATH, (LPARAM)Configuration.EditNewFileDefault); + HWND edit = GetDlgItem(HWindow, IDE_PATH); + if (edit != NULL) + GetControlTextUtf8(edit, Configuration.EditNewFileDefault, MAX_PATH); } if (cmd == 2) { @@ -607,11 +810,11 @@ void CCopyMoveMoreDialog::Transfer(CTransferInfo& ti) { LoadComboFromStdHistoryValues(hWnd, History, HistoryCount); SendMessage(hWnd, CB_LIMITTEXT, PathBufSize - 1, 0); - SendMessage(hWnd, WM_SETTEXT, 0, (LPARAM)Path); + SetControlTextUtf8(hWnd, Path); } else { - SendMessage(hWnd, WM_GETTEXT, PathBufSize, (LPARAM)Path); + GetControlTextUtf8(hWnd, Path, PathBufSize); AddValueToStdHistoryValues(History, HistoryCount, Path, FALSE); } } @@ -1167,11 +1370,11 @@ void CChangeDirDlg::Transfer(CTransferInfo& ti) { LoadComboFromStdHistoryValues(hWnd, history, CHANGEDIR_HISTORY_SIZE); SendMessage(hWnd, CB_LIMITTEXT, 2 * MAX_PATH - 1, 0); - SendMessage(hWnd, WM_SETTEXT, 0, (LPARAM)Path); + SetControlTextUtf8(hWnd, Path); } else { - SendMessage(hWnd, WM_GETTEXT, 2 * MAX_PATH, (LPARAM)Path); + GetControlTextUtf8(hWnd, Path, 2 * MAX_PATH); AddValueToStdHistoryValues(history, CHANGEDIR_HISTORY_SIZE, Path, FALSE); } } @@ -1238,7 +1441,7 @@ void CDriveInfo::Validate(CTransferInfo& ti) if (ti.GetControl(edit, IDE_VOLNAME) && ti.Type == ttDataFromWindow) { char newName[MAX_PATH]; - SendMessage(edit, WM_GETTEXT, MAX_PATH, (LPARAM)newName); + GetControlTextUtf8(edit, newName, MAX_PATH); if (strcmp(OldVolumeName, newName) != 0) { diff --git a/src/fileswn5.cpp b/src/fileswn5.cpp index 56b996d85..c2820c340 100644 --- a/src/fileswn5.cpp +++ b/src/fileswn5.cpp @@ -4,6 +4,9 @@ #include "precomp.h" +#include +#include + #include "cfgdlg.h" #include "mainwnd.h" #include "plugins.h" @@ -2636,7 +2639,8 @@ void CFilesWindow::QuickRenameBegin(int index, const RECT* labelRect) // Since Windows Vista, Microsoft introduced a demanded feature: quick rename selects only the name without the dot and extension // the same code appears here four times - int selectionEnd = -1; + int selectionEndBytes = -1; + int selectionEndChars = -1; if (!Configuration.QuickRenameSelectAll) { if (!isDir) @@ -2644,7 +2648,31 @@ void CFilesWindow::QuickRenameBegin(int index, const RECT* labelRect) const char* dot = strrchr(formatedFileName, '.'); if (dot != NULL && dot > formatedFileName) // although ".cvspass" is an extension in Windows, Explorer selects the entire name, so we do the same // if (dot != NULL) - selectionEnd = (int)(dot - formatedFileName); + { + selectionEndBytes = (int)(dot - formatedFileName); + if (GetACP() == CP_UTF8) + { + int converted = MultiByteToWideChar(CP_UTF8, + MB_ERR_INVALID_CHARS, + formatedFileName, + selectionEndBytes, + NULL, + 0); + if (converted == 0) + converted = MultiByteToWideChar(CP_UTF8, + 0, + formatedFileName, + selectionEndBytes, + NULL, + 0); + if (converted > 0) + selectionEndChars = converted; + else + selectionEndChars = selectionEndBytes; + } + else + selectionEndChars = selectionEndBytes; + } } } @@ -2689,15 +2717,57 @@ void CFilesWindow::QuickRenameBegin(int index, const RECT* labelRect) RECT r = *labelRect; AdjustQuickRenameRect(formatedFileName, &r); - HWND hWnd = QuickRenameWindow.CreateEx(0, - "edit", - formatedFileName, - WS_BORDER | WS_CHILD | WS_CLIPSIBLINGS | ES_AUTOHSCROLL | ES_LEFT, - r.left, r.top, r.right - r.left, r.bottom - r.top, - GetListBoxHWND(), - NULL, - HInstance, - &QuickRenameWindow); + HWND hWnd = NULL; + std::wstring wideName; + if (GetACP() == CP_UTF8) + { + int required = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, formatedFileName, -1, NULL, 0); + if (required == 0) + required = MultiByteToWideChar(CP_UTF8, 0, formatedFileName, -1, NULL, 0); + if (required > 0) + { + wideName.resize(required); + int converted = MultiByteToWideChar(CP_UTF8, 0, formatedFileName, -1, &wideName[0], required); + if (converted == 0) + { + wideName.clear(); + } + else + { + if (wideName[converted - 1] == L'\0') + --converted; + if (converted < (int)wideName.size()) + wideName.resize(converted); + + QuickRenameWindow.SetUnicodeWindow(TRUE); + hWnd = QuickRenameWindow.CreateExW(0, + L"edit", + L"", + WS_BORDER | WS_CHILD | WS_CLIPSIBLINGS | ES_AUTOHSCROLL | ES_LEFT, + r.left, r.top, r.right - r.left, r.bottom - r.top, + GetListBoxHWND(), + NULL, + HInstance, + &QuickRenameWindow); + if (hWnd != NULL) + SendMessageW(hWnd, WM_SETTEXT, 0, (LPARAM)(wideName.empty() ? L"" : wideName.c_str())); + } + } + } + BOOL unicodeEdit = hWnd != NULL; + if (hWnd == NULL) + { + QuickRenameWindow.SetUnicodeWindow(FALSE); + hWnd = QuickRenameWindow.CreateEx(0, + "edit", + formatedFileName, + WS_BORDER | WS_CHILD | WS_CLIPSIBLINGS | ES_AUTOHSCROLL | ES_LEFT, + r.left, r.top, r.right - r.left, r.bottom - r.top, + GetListBoxHWND(), + NULL, + HInstance, + &QuickRenameWindow); + } if (hWnd == NULL) { TRACE_E("Cannot create QuickRenameWindow"); @@ -2714,7 +2784,24 @@ void CFilesWindow::QuickRenameBegin(int index, const RECT* labelRect) //SendMessage(hWnd, EM_SETSEL, 0, -1); // select all // we can select only the name without dot and extension - SendMessage(hWnd, EM_SETSEL, 0, selectionEnd); + int selectionEndForControl = selectionEndBytes; + if (unicodeEdit) + { + if (selectionEndChars >= 0) + selectionEndForControl = selectionEndChars; + } + else + { + if (selectionEndBytes >= 0) + selectionEndForControl = selectionEndBytes; + } + if (selectionEndForControl >= 0) + { + SendMessage(hWnd, EM_SETSEL, selectionEndForControl, (LPARAM)-1); + SendMessage(hWnd, EM_SETSEL, 0, selectionEndForControl); + } + else + SendMessage(hWnd, EM_SETSEL, 0, (LPARAM)-1); ShowWindow(hWnd, SW_SHOW); SetFocus(hWnd); @@ -2760,7 +2847,27 @@ BOOL CFilesWindow::HandeQuickRenameWindowKey(WPARAM wParam) HWND hWnd = QuickRenameWindow.HWindow; char newName[MAX_PATH]; - GetWindowText(hWnd, newName, MAX_PATH); + if (GetACP() == CP_UTF8 && IsWindowUnicode(hWnd)) + { + int length = GetWindowTextLengthW(hWnd); + if (length < 0) + length = 0; + std::vector wide(length + 1); + int copied = GetWindowTextW(hWnd, wide.data(), length + 1); + if (copied < 0) + copied = 0; + if ((size_t)copied >= wide.size()) + wide.push_back(L'\0'); + wide[copied] = L'\0'; + int written = WideCharToMultiByte(CP_UTF8, 0, wide.data(), copied, newName, MAX_PATH - 1, NULL, NULL); + if (written < 0) + written = 0; + if (written >= MAX_PATH) + written = MAX_PATH - 1; + newName[written] = '\0'; + } + else + GetWindowText(hWnd, newName, MAX_PATH); // lower the thread priority to "normal" (so operations don't overload the machine) SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); diff --git a/src/manifest.xml b/src/manifest.xml index 432a92f48..3b2da6b64 100644 --- a/src/manifest.xml +++ b/src/manifest.xml @@ -39,8 +39,9 @@ - - true + + true + UTF-8 - \ No newline at end of file + diff --git a/src/plugins/shared/lukas/str.cpp b/src/plugins/shared/lukas/str.cpp index e12b0a0b5..aeb03a171 100644 --- a/src/plugins/shared/lukas/str.cpp +++ b/src/plugins/shared/lukas/str.cpp @@ -23,12 +23,26 @@ CSTR::CSTR() CALL_STACK_MESSAGE_NONE uintptr_t i; char charTable[256]; - for (i = 0; i < 256; i++) + if (GetACP() == CP_UTF8) { - LowerCase[i] = (char)(UINT_PTR)CharLowerA((LPSTR)i); - UpperCase[i] = (char)(UINT_PTR)CharUpperA((LPSTR)i); - charTable[i] = (unsigned char)i; + for (i = 0; i < 256; i++) + { + LowerCase[i] = (char)i; + UpperCase[i] = (char)i; + charTable[i] = (unsigned char)i; + } + for (i = 'A'; i <= 'Z'; i++) + LowerCase[i] = (char)(i - 'A' + 'a'); + for (i = 'a'; i <= 'z'; i++) + UpperCase[i] = (char)(i - 'a' + 'A'); } + else + for (i = 0; i < 256; i++) + { + LowerCase[i] = (char)(UINT_PTR)CharLowerA((LPSTR)i); + UpperCase[i] = (char)(UINT_PTR)CharUpperA((LPSTR)i); + charTable[i] = (unsigned char)i; + } if (!GetStringTypeA(LOCALE_USER_DEFAULT, CT_CTYPE1, charTable, 256, CType)) { //TRACE_E("GetStringTypeA failed. Last Error = " << GetLastError()); diff --git a/src/precomp.h b/src/precomp.h index 21dac6648..3ae433738 100644 --- a/src/precomp.h +++ b/src/precomp.h @@ -58,6 +58,7 @@ #include "multimon.h" #include "sheets.h" #include "strutils.h" +#include "utf8wrap.h" #include "spl_com.h" #include "spl_base.h" #include "spl_crypt.h" diff --git a/src/salamdr1.cpp b/src/salamdr1.cpp index 7a7e8af2a..d184eda5a 100644 --- a/src/salamdr1.cpp +++ b/src/salamdr1.cpp @@ -877,7 +877,22 @@ void InitLocales() IsAlpha[i] = IsCharAlpha((char)i); } - if ((DecimalSeparatorLen = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, DecimalSeparator, 5)) == 0 || + auto LoadLocaleString = [](LCTYPE type, char* buffer, int bufferSize) -> int { + if (GetACP() == CP_UTF8) + { + WCHAR wide[8]; + int wideLen = GetLocaleInfoW(LOCALE_USER_DEFAULT, type, wide, _countof(wide)); + if (wideLen == 0 || wideLen > (int)_countof(wide)) + return 0; + int bytes = WideCharToMultiByte(CP_UTF8, 0, wide, wideLen, buffer, bufferSize, NULL, NULL); + if (bytes == 0 || bytes > bufferSize) + return 0; + return bytes; + } + return GetLocaleInfo(LOCALE_USER_DEFAULT, type, buffer, bufferSize); + }; + + if ((DecimalSeparatorLen = LoadLocaleString(LOCALE_SDECIMAL, DecimalSeparator, 5)) == 0 || DecimalSeparatorLen > 5) { strcpy(DecimalSeparator, "."); @@ -889,7 +904,7 @@ void InitLocales() DecimalSeparator[DecimalSeparatorLen] = 0; // posychrujeme nulu na konci } - if ((ThousandsSeparatorLen = GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, ThousandsSeparator, 5)) == 0 || + if ((ThousandsSeparatorLen = LoadLocaleString(LOCALE_STHOUSAND, ThousandsSeparator, 5)) == 0 || ThousandsSeparatorLen > 5) { strcpy(ThousandsSeparator, " "); @@ -3613,13 +3628,23 @@ int WinMainBody(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR cmdLine, CHARSETINFO ci; memset(&ci, 0, sizeof(ci)); char bufANSI[10]; - if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE, bufANSI, 10)) + UINT activeCodePage = GetACP(); + if (activeCodePage == CP_UTF8) + { + UserCharset = DEFAULT_CHARSET; + } + else if (GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDEFAULTANSICODEPAGE, bufANSI, 10)) { if (TranslateCharsetInfo((DWORD*)(DWORD_PTR)MAKELONG(atoi(bufANSI), 0), &ci, TCI_SRCCODEPAGE)) { UserCharset = ci.ciCharset; } } + else if (activeCodePage != 0) + { + if (TranslateCharsetInfo((DWORD*)(DWORD_PTR)MAKELONG(activeCodePage, 0), &ci, TCI_SRCCODEPAGE)) + UserCharset = ci.ciCharset; + } // kvuli pouzivani souboru mapovanych do pameti je nutne ziskat granularitu alokaci SYSTEM_INFO si; diff --git a/src/salopen/salopen.cpp b/src/salopen/salopen.cpp index 772b59a36..98fcd2c83 100644 --- a/src/salopen/salopen.cpp +++ b/src/salopen/salopen.cpp @@ -661,8 +661,16 @@ void WinMainCRTStartup() ExitProcess(2); int i; - for (i = 0; i < 256; i++) - LowerCase[i] = (char)(UINT_PTR)CharLower((LPTSTR)(UINT_PTR)i); + if (GetACP() == CP_UTF8) + { + for (i = 0; i < 256; i++) + LowerCase[i] = (char)i; + for (i = 'A'; i <= 'Z'; i++) + LowerCase[i] = (char)(i - 'A' + 'a'); + } + else + for (i = 0; i < 256; i++) + LowerCase[i] = (char)(UINT_PTR)CharLower((LPTSTR)(UINT_PTR)i); char* cmdline = GetCommandLine(); // skip leading spaces diff --git a/src/setup/infinst.c b/src/setup/infinst.c index f97364bc6..7b775dc4c 100644 --- a/src/setup/infinst.c +++ b/src/setup/infinst.c @@ -609,8 +609,16 @@ WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdS // nechceme zadne kriticke chyby jako "no disk in drive A:" SetErrorMode(SetErrorMode(0) | SEM_FAILCRITICALERRORS); - for (int i = 0; i < 256; i++) - LowerCase[i] = (char)CharLower((LPTSTR)(DWORD_PTR)i); + if (GetACP() == CP_UTF8) + { + for (int i = 0; i < 256; i++) + LowerCase[i] = (char)i; + for (int i = 'A'; i <= 'Z'; i++) + LowerCase[i] = (char)(i - 'A' + 'a'); + } + else + for (int i = 0; i < 256; i++) + LowerCase[i] = (char)CharLower((LPTSTR)(DWORD_PTR)i); lstrcpy(MAINWINDOW_TITLE, LoadStr(IDS_MAINWINDOWTITLE)); diff --git a/src/setup/remove/utils.c b/src/setup/remove/utils.c index b8ed8a1e0..d6392fe58 100644 --- a/src/setup/remove/utils.c +++ b/src/setup/remove/utils.c @@ -90,6 +90,14 @@ BOOL Is64BitWindows() void InitUtils() { int i; - for (i = 0; i < 256; i++) - LowerCase[i] = (char)CharLower((LPTSTR)(DWORD_PTR)i); + if (GetACP() == CP_UTF8) + { + for (i = 0; i < 256; i++) + LowerCase[i] = (char)i; + for (i = 'A'; i <= 'Z'; i++) + LowerCase[i] = (char)(i - 'A' + 'a'); + } + else + for (i = 0; i < 256; i++) + LowerCase[i] = (char)CharLower((LPTSTR)(DWORD_PTR)i); } diff --git a/src/vcxproj/salamand.vcxproj b/src/vcxproj/salamand.vcxproj index 84a2ad313..5c578c5b8 100644 --- a/src/vcxproj/salamand.vcxproj +++ b/src/vcxproj/salamand.vcxproj @@ -265,6 +265,8 @@ + + diff --git a/src/vcxproj/salamand.vcxproj.filters b/src/vcxproj/salamand.vcxproj.filters index 3f3c5f368..3300da235 100644 --- a/src/vcxproj/salamand.vcxproj.filters +++ b/src/vcxproj/salamand.vcxproj.filters @@ -405,6 +405,9 @@ common + + common + common diff --git a/src/versinfo.rh2 b/src/versinfo.rh2 index 9d521716b..99aa9c763 100644 --- a/src/versinfo.rh2 +++ b/src/versinfo.rh2 @@ -16,7 +16,7 @@ #define VERSINFO_MINORB VERSINFO_SALAMANDER_MINORB #define VERSINFO_VERSION VERSINFO_SALAMANDER_VERSION -#define VERSINFO_COPYRIGHT "Copyright © 1997-2023 Open Salamander Authors" +#define VERSINFO_COPYRIGHT "Copyright \xC2\xA9 1997-2023 Open Salamander Authors" #define VERSINFO_COMPANY "Open Salamander" #define VERSINFO_DESCRIPTION "Open Salamander, File Manager"