I was in need to use Unicode in Delphi, so I tried to use CLX. At first, all was good - then I discovered that a StringGrid couldn't work correctly in Unicode. So I solved the problem, and wrote an article on this story.
When I attempted for the first time to write a multilingual application with Delphi, I found a tricky solution to get a translations for the GUI. The GUI is written in plain english, and at runtime caption and/or text of controls get updated basing on the chosen language and a support file containing all the translations. All is editable in place, using a stringgrid containing the original texts and the relevant translations. After that, I faced the problem that all the VCL only works with ansistring and local codepage. The bigger problem was not in the GUI, assuming that the host computer had the correct codepage for the desired language. The problem was in the interactive translation system, which was using a stringgrid which could not display together all the different languages. And even if I could have solved that problem, I would not have been able to test the application in the final language.
I tried to find some way to get around this, until I understood that I should have used Unicode. VCL doesn not understand Widestrings, but CLX does. So I started a test application using CLX. In five minutes I got euphoric - after setting fmMain.Font.CharSet to fcsUnicode, all the controls on the form, menus included, were able to display foreign languages. Even the look was prettier, with colored menus and buttons. Then I tried a TStringGrid. Bleah! The in-place editor was working correctly, but as soon its contents were copied to the underlying grid, the unicode string was getting converted to AnsiString, loosing special and foreign characters. Moreover, assigning directly Cells[x,y] presented the same problem. But I didn't desist; I took the CLX source from Delphi 7 source directory, and included QGrids.pas in my project to be able to debug it and see what was going wrong. The problem was that some parts of the unit were declared as Ansistring, and other parts as Widestring, and the diligent compiler was happily converting from the two types.
Now I am not a Delphi expert, and it was the first time I was using Widestrings. Anyway, I made some weird modification to QGrids.pas and the stringgrid started to work better - assignment and back-reading of Cells[Row,Col] went well. The correction was simple (and dirty): instead of changing a lot of things I didn't know, I leaved all the ansistring declarations in place, and only copied widestrings to ansistrings without letting the compiler do any conversion. The original TStringGrid.SetCells does:
TStringGridStrings(EnsureDataRow(ARow))[ACol] := Value;
where "Value" is correctly declared as Widestring, but TStringGridStrings only copes with Ansistring; in this assignment the compiler converts Value to its Ansistring representation, loosing special characters (loosing or changing those characters which don't exist in the target codepage). Even the FreeCLX I downloaded from SourceForge was made in this way. I changed that assignment with the following:
move(value, s, length(s));
TStringGridStrings(EnsureDataRow(ARow))[ACol] := s;
where "s" is an Ansistring, but it never gets explicitly assigned; so the compiler does not care to convert it. An Ansistring can happily contain any character, included NUL, and so it can contain a Widestring representation.
Once the assignment was right, I modified its reading counterpart. The original source for TStringGrid.GetCells(ACol, ARow: Integer) has:
... Result := ssl[ACol];
and I simply replaced that statement with:
s := ssl[ACol];
setlength(result, length(s) div 2-1);
move(s, result, 2*length(result))
The only remaining problem was the in-place editor, FInPlaceEditor, which was not working. I presumed, correctly, that it was the same problem as before. The Text property of TMaskedEdit, used by a grid to obtain the edited text, is declared as:
property Text: TMaskedText read GetMaskedText write SetMaskedText;
Now, in all the CLX source, the only file referring to this TMaskedText type is QMask.pas, and this file does not declares this type. So? So I found its declaration in another file, and it was:
TMaskedText = type string;
I simply put in QMask.pas this different declaration:
TMaskedText = type widestring;
and everything started to work.
I didn't check too much - it simply seemed to work. I copied QMask.pas and QGrids.pas back to the source directory and, I don't even know how, managed to recompile the CLX package. The funny thing is that if, instead of using fcsUnicode in the CharSet property of Font, I use fcsLatin1 or ANSI_CHARSET, things keep to work. I thought that they would stop. Anyway, who cares?