WxWidgets with Visual Studio and Unicode Sources - c++

Following the UTF-8 everywhere manifesto, and mostly its part: how to do text on Windows, I've created this simple example with wxWidgets. I wanted wxWidgets to interpret string literals as UTF-8 strings, but it seems, that library gets me wrong.
single source file - main.cpp, encoded as UTF-8 without signature (in msvc terminology):
#include <wx/wx.h>
class Mainw: public wxFrame
{
public:
Mainw(wxWindow * parent, wxWindowID wxId, const wxString & label)
: wxFrame(parent, wxId, label)
{
wxBoxSizer * sizer = new wxBoxSizer(wxHORIZONTAL);
sizer->Add(new wxTextCtrl(this, wxID_ANY, wxT("Кириллица")), 1, wxEXPAND | wxALL, 5);
this->SetSizer(sizer);
}
};
class MyApp: public wxApp
{
public:
bool OnInit()
{
Mainw *f = new Mainw(NULL, wxID_ANY, wxT("Frame"));
f->Show();
return true;
}
};
IMPLEMENT_APP(MyApp)
Preprocessor Definitions:
UNICODE
_UNICODE
WIN32
__WXMSW__
_WINDOWS
_DEBUG
__WXDEBUG__
wxUSE_UNICODE=1
WXUSINGDLL=1
Linked with WxWidgets Library version 3.0.2
Headers - http://sourceforge.net/projects/wxwindows/files/3.0.2/wxWidgets-3.0.2_headers.7z/download
Binaries - http://sourceforge.net/projects/wxwindows/files/3.0.2/binaries/wxMSW-3.0.2_vc90_Dev.7z/download
Being run, this example produces window with text Кириллица, instead of Кириллица (there was something similar, but it changed to this, when I tried to select it to copy here). It means, that wxWidgets fails to interpret my string literal as UTF-8, but interprets it as something else - maybe as text in system encoding which is windows-1251.
Is there any way to change this behavior of library to match utf-8 everywhere manifesto?
Conclusion:
I gave up. I managed to build library with msvc and flag wxUSE_UNICODE_UTF8 but it would not help without some complex changes in library configuration headers. It seems, that this option is POSIX only

Is there any way to change this behavior of library to match utf-8 everywhere manifesto?
No, not under Windows because Windows doesn't support UTF-8 locales (in principle, they could be emulated by the CRT, but AFAIK no compiler does it) and wxString(const char*) ctor interprets the string in the current locale encoding by default.
There are two simple solutions however:
Use wxString::FromUTF8() explicitly.
Use wxString(const wchar_t*) ctor with L"..." wide char argument.
Just for completeness, you also might force the library into accepting UTF-8 narrow text by rebuilding it with wxUSE_UTF8_LOCALE_ONLY=1, but I'm far from sure if this is going to work because the CRT locale will still be different and so using non-ASCII characters with any CRT functions will most likely not work as expected, so I definitely do not recommend doing this unless you're just curious to see what happens.

Related

Why do I need a QTranslator for localized button texts in a QMessageBox on Windows?

I'm developing a Qt application that runs on Linux and Windows. I'm on a German locale.
When building it on Linux, I get the correct translations for the standard buttons displayed by e. g. a QMessageBox::question with QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel buttons set ("Ja", "Nein" and "Abbrechen" in my case).
If I build the code on Windows however, I only get the C locale (English) texts for those buttons ("Yes", "No" and "Cancel").
After some searching, I found a globally working solution by adding
#include <QTranslator>
#include <QLibraryInfo>
and
QTranslator qtTranslator;
if (qtTranslator.load(QLocale::system(), QString::fromUtf8("qt"), QString::fromUtf8("_"),
QLibraryInfo::location(QLibraryInfo::TranslationsPath))) {
app.installTranslator(&qtTranslator);
}
to my main.cpp.
Using this, I also get the translated strings on Windows. On Linux however, the qtTranslator.load call fails and thus, nothing happens to the state I had before, so I ended up putting that code inside an #ifdef Q_OS_WIN block.
This works, but seems a bit hacky to me. Plus, I don't understand why I get the translated strings on Linux by default and not on Windows.
Is the way I do it the correct solution? And if so, why do I need that additional code on Windows?
Try to replace your code on this and tested.
static QTranslator qtTr;
qtTr.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath));
installTranslator(&qtTr);

NVDA screen reader requests wrong QAccessible::InterfaceType

I'm on Qt 5.9.1.
I'm trying to make an application accessible through screen readers. I have a custom widget (let's call it MyWidget) that contains text (a lot of it.) The text is drawn using QPainter, which is why a custom widget is used rather than something like QTextBrowser.
I implemented the QAccessibleTextInterface for the widget in a AccessibleMyWidget class that derives from QAccessibleWidget and QAccessibleTextInterface. It works fine with Orca under Linux, but when used in Windows 7 with NVDA, QAccessibleInterface::interface_cast() requests the wrong interface type. With Orca, I get requests for a QAccessible::TextInterface. In NVDA, it's always QAccessible::ValueInterface.
AccessibleMyWidget is defined as:
class AccessibleMyWidget:
public QAccessibleWidget, public QAccessibleTextInterface {
public:
explicit AccessibleMyWidget(QWidget* w)
: QAccessibleWidget(w, QAccessible::EditableText)
{
Q_ASSERT(isValid());
}
void* interface_cast(QAccessible::InterfaceType t) override
{
if (t == QAccessible::TextInterface) {
// !!! This is never requested with NVDA !!!
return static_cast<QAccessibleTextInterface*>(this);
}
return QAccessibleWidget::interface_cast(t);
}
/*
* QAccessibleTextInterface implementation below this point.
*/
void addSelection(int startOffset, int endOffset) override;
QString attributes(int offset, int* startOffset,
int* endOffset) const override;
// etc.
};
With Orca under Linux, everything seems to work as intended. I get calls to interface_cast() for a TextInterface, and after that, the various functions of QAccessibleTextInterface are called. With NVDA under Linux, I just get interface_cast() calls for ValueInterface, and none of the QAccessibleTextInterface functions are called. Which means MyWidget is completely inaccessible, unless I override QAccessibleWidget::text() and just return all text as a single string, which means no cursor navigation, no selection support... It basically becomes just a QLabel at this point, but with a ton of text and thus very hard to use.
What am I missing here?
Well, an NVDA developer helped me track down the issue. It's because the MinGW version of Qt has IAccessible2 disabled and uses MSAA (Microsoft Active Accessibility) instead, which doesn't support any of these interfaces. IA2 needs COM, and MinGW doesn't support that.
So I would have to switch from GCC/MinGW to MSVC to make this work on Windows. Too bad that's not actually an option in the foreseeable future :-/
2020 update:
This has been fixed since. Not sure when exactly, but the current mingw Qt builds now support this.

How to get different language text by GetWindowText?

How to get different language text by GetWindowText?
I have modfying a whiteboard project. When the user type in the whiteboard, the onchange function will called and will show the text in the whiteboard. However, when I type Japanese text (Non system default language) it shows "?" instead. The following is the code spinet for onchange.
void CHBEdit::OnChange()
{
static bool bChanged = true;
CDC *pDC = GetDC();
if (bChanged) {
CString str;
GetWindowText(str);
m_strText = str;
int iStartChar, iEndChar;
GetSel(iStartChar, iEndChar);
CRect rect;
GetWindowRect(&rect);
CFont *Oldfont = (CFont *)pDC->SelectObject(&m_Font);
CSize size = pDC->GetOutputTextExtent(str); //+ "a");
pDC->SelectObject(Oldfont);
SetWindowPos(NULL, 0, 0, size.cx, size.cy, SWP_NOMOVE);
bChanged = false;
SetWindowText(str);
SetSel(iStartChar, iEndChar);
}
else
bChanged = true;
ReleaseDC(pDC);
}
I am really new in C++. I did research and had tried GetwindowTextW with UTF8 unicode, but it still showing "?". Maybe the logic wrong.
I would appreciate it if anyone could give me the solution to show different language text in the whiteboard. Thank you.
GetWindowTextW is the right way. Windows uses UTF16 Unicode standard, with wchar_t wide string. UTF8 standard is commonly used with Linux and internet. You may need UTF16/UTF8 conversion only when exporting/importing data to other systems.
If you create a new MFC project it will be setup in Unicode and MFC functions will default to Unicode. Example:
//UNICODE project:
CString str;
GetWindowText(str);
MessageBox(str);
If project is Unicode, this will will work in Japanese and all other languages.
If you are working with a legacy MFC project which is stuck in in ANSI mode and you can't change it, use the following fix:
//ANSI project:
CStringW str;
GetWindowTextW(str);
MessageBoxW(str);

Can I localize AFX_IDP_ASK_TO_SAVE?

In my application, I have a 'Do you want to save your changes?' message box. I'm getting the text to display from MFC:
CString prompt;
AfxFormatString1(prompt, AFX_IDP_ASK_TO_SAVE, strFileName);
UINT nResult = AfxMessageBox(prompt, MB_YESNOCANCEL, AFX_IDP_ASK_TO_SAVE)
Now I'm localizing the application to Japanese. I'm guessing that standard texts such as this are already translated to most of the major languages. But I have no idea how to set MFC to use the Japanese versions of the resource identifiers of these standard texts. Is it possible to do this?
It turned out I needed to change a few include files in my .rc file:
#include "afxres.rc" // Standard components
#include "afxprint.rc" // printing/print preview resources
#include "afxribbon.rc" // MFC ribbon and control bar resources
needed to become:
#include "l.jpn/afxres.rc" // Standard components
#include "l.jpn/afxprint.rc" // printing/print preview resources
#include "l.jpn/afxribbon.rc" // MFC ribbon and control bar resources
In VS2008 AFX_IDP_ASK_TO_SAVE and other AFX_... strings are kept in MFC localization DLLs: MFC90CHS.dll, MFC90JPN.dll. MFC90KOR.dll etc. To use them on Windows Vista and later you should call:
SetThreadUILanguage (MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US));
or
SetThreadUILanguage (MAKELANGID(LANG_JAPANESE,SUBLANG_JAPANESE_JAPAN));
on Windows XP call:
SetThreadLocale(lcid)
For constants refer to https://msdn.microsoft.com/en-us/library/windows/desktop/dd318693%28v=vs.85%29.aspx
Use GetSystemDefaultLangID to retrieve right locale i.e. on Chinese OS.
Important: don't use GetUserDefaultLangID() or GetSystemDefaultUILanguage(), they return different things, i.e. in case you have English OS, having Chinese locale selected.
lcid = MAKELCID(GetSystemDefaultLangID(), SORT_DEFAULT);//With Chinese locale, returns 0x804, zh-CN

How can I use MBCS to support a language without codepage?

An MFC application need an edit ctrl in dialog for edit Tamil language.
But I found the Tamil language does not have code page in Windows (Yes, there isn't Tamil system locale), and the Unicode option is not in my situation.
Saw someone idea, Embedding HWND into external process using SetParent
I wanna to create another application built with UNICODE option, embed its window to the dialog, but failed.
Check the MSDN, SetParent need the parent and child window in one application.
So, how can I implement it?
#MSalters
I solved it by override virtual BOOL CWinThread::PumpMessage(), force the message loop use W version API.
BOOL CtamildlgApp::PumpMessage()
{
_AFX_THREAD_STATE *pState = AfxGetThreadState();
if ( !::GetMessageW( &( pState->m_msgCur ), NULL, NULL, NULL ) )
{
// Note: prevents calling message loop things in 'ExitInstance'
// will never be decremented
return FALSE;
}
// process this message
if ( pState->m_msgCur.message != WM_KICKIDLE )
{
::TranslateMessage( &( pState->m_msgCur ) );
::DispatchMessageW( &( pState->m_msgCur ) );
}
return TRUE;
}
Then CreateWindowExW(... MSFTEDIT_CLASS ...)
Tamil is indeed harder than usual. But still there's one thing obvious: use Unicode, not MBCS. Internally, all Windows functions are Unicode. The MBCS wrappers use the current code page to translate from multi-byte encodings to UTF-16. E.g. when you call MessageBoxA("Some String"), the current code page is used to translate that into a call of MessageBoxW(L"Some String").
Now, this directly means that you cannot call MessageBoxA("Narrow Tamil String") since there's no code page to convert that into the appropriate wide string. You MUST call the Unicode function MessageBoxW("Narrow Tamil String") directly.
MFC cannot magically make this problem go away. It may provide a nicer syntax for some calls, wrapping Win32 idioms in classes, constructors and destuctors. But it can't create missing code pages out of thin air.