diff options
-rw-r--r-- | doc/ScintillaHistory.html | 4 | ||||
-rw-r--r-- | test/MessageNumbers.py | 1 | ||||
-rw-r--r-- | test/win32Tests.py | 162 | ||||
-rw-r--r-- | win32/ScintillaWin.cxx | 39 |
4 files changed, 185 insertions, 21 deletions
diff --git a/doc/ScintillaHistory.html b/doc/ScintillaHistory.html index 1ece8653b..659eba28a 100644 --- a/doc/ScintillaHistory.html +++ b/doc/ScintillaHistory.html @@ -565,6 +565,10 @@ Lexer added for DataFlex. <a href="https://sourceforge.net/p/scintilla/feature-requests/1295/">Feature #1295</a>. </li> + <li> + On Win32, limit text returned from WM_GETTEXT to the length specified in wParam. + <a href="https://sourceforge.net/p/scintilla/bugs/2110/">Bug #2110</a>. + </li> </ul> <h3> <a href="https://www.scintilla.org/scite417.zip">Release 4.1.7</a> diff --git a/test/MessageNumbers.py b/test/MessageNumbers.py index 782027069..440a0f668 100644 --- a/test/MessageNumbers.py +++ b/test/MessageNumbers.py @@ -14,6 +14,7 @@ msgs = { "WM_EXITSIZEMOVE":562, "WM_GETMINMAXINFO":36, "WM_GETTEXT":13, +"WM_GETTEXTLENGTH":14, "WM_IME_SETCONTEXT":0x0281, "WM_IME_NOTIFY":0x0282, "WM_KEYDOWN":256, diff --git a/test/win32Tests.py b/test/win32Tests.py new file mode 100644 index 000000000..21a89dbed --- /dev/null +++ b/test/win32Tests.py @@ -0,0 +1,162 @@ +# -*- coding: utf-8 -*- +# Requires Python 2.7 or later + +# These are tests that run only on Win32 as they use Win32 SendMessage call +# to send WM_* messages to Scintilla that are not implemented on other platforms. +# These help Scintilla behave like a Win32 text control and can help screen readers, +# for example. + +from __future__ import with_statement +from __future__ import unicode_literals + +import codecs, ctypes, os, sys, unittest + +from MessageNumbers import msgs, sgsm + +import ctypes +user32 = ctypes.windll.user32 + +import XiteWin as Xite + +class TestWins(unittest.TestCase): + + def setUp(self): + self.xite = Xite.xiteFrame + self.ed = self.xite.ed + self.sciHwnd = self.xite.sciHwnd + self.ed.ClearAll() + self.ed.EmptyUndoBuffer() + self.ed.SetCodePage(0) + self.ed.SetStatus(0) + + # Helper methods + + def Send(self, msg, w, l): + return user32.SendMessageW(self.sciHwnd, msgs[msg], w, l) + + def GetTextLength(self): + return self.Send("WM_GETTEXTLENGTH", 0, 0) + + def GetText(self, n, s): + # n = The maximum number of characters to be copied, including the terminating null character. + # returns the number of characters copied, not including the terminating null character + return self.Send("WM_GETTEXT", n, s) + + def TextValue(self): + self.assertEquals(self.ed.GetStatus(), 0) + lenValue = self.GetTextLength() + lenValueWithNUL = lenValue + 1 + value = ctypes.create_unicode_buffer(lenValueWithNUL) + lenData = self.GetText(lenValueWithNUL, value) + self.assertEquals(self.ed.GetStatus(), 0) + self.assertEquals(lenData, lenValue) + return value.value + + def SetText(self, s): + return self.Send("WM_SETTEXT", 0, s) + + # Tests + + def testSetText(self): + self.SetText(b"ab") + self.assertEquals(self.ed.Length, 2) + + def testGetTextLength(self): + self.SetText(b"ab") + self.assertEquals(self.GetTextLength(), 2) + + def testGetText(self): + self.SetText(b"ab") + data = ctypes.create_unicode_buffer(100) + lenData = self.GetText(100, data) + self.assertEquals(lenData, 2) + self.assertEquals(len(data.value), 2) + self.assertEquals(data.value, "ab") + + def testGetUTF8Text(self): + self.ed.SetCodePage(65001) + t = "å" + tu8 = t.encode("UTF-8") + self.SetText(tu8) + value = self.TextValue() + self.assertEquals(value, t) + + def testGetBadUTF8Text(self): + self.ed.SetCodePage(65001) + tu8 = b't\xc2' + t = "t\xc2" + self.SetText(tu8) + value = self.TextValue() + self.assertEquals(len(value), 2) + self.assertEquals(value, t) + + def testGetJISText(self): + self.ed.SetCodePage(932) + t = "\N{HIRAGANA LETTER KA}" + tu8 = t.encode("shift-jis") + self.SetText(tu8) + value = self.TextValue() + self.assertEquals(len(value), 1) + self.assertEquals(value, t) + + def testGetBadJISText(self): + self.ed.SetCodePage(932) + # This is invalid Shift-JIS, surrounded by [] + tu8 = b'[\x85\xff]' + # Win32 uses Katakana Middle Dot to indicate some invalid Shift-JIS text + # At other times \uF8F3 is used which is a private use area character + # See https://unicodebook.readthedocs.io/operating_systems.html + katakanaMiddleDot = '[\N{KATAKANA MIDDLE DOT}]' + privateBad = '[\uf8f3]' + self.SetText(tu8) + value = self.TextValue() + self.assertEquals(len(value), 3) + self.assertEquals(value, katakanaMiddleDot) + + # This is even less valid Shift-JIS + tu8 = b'[\xff]' + self.SetText(tu8) + value = self.TextValue() + self.assertEquals(len(value), 3) + self.assertEquals(value, privateBad) + + def testGetTextLong(self): + self.assertEquals(self.ed.GetStatus(), 0) + self.SetText(b"ab") + data = ctypes.create_unicode_buffer(100) + lenData = self.GetText(4, data) + self.assertEquals(self.ed.GetStatus(), 0) + self.assertEquals(lenData, 2) + self.assertEquals(data.value, "ab") + + def testGetTextShort(self): + self.assertEquals(self.ed.GetStatus(), 0) + self.SetText(b"ab") + data = ctypes.create_unicode_buffer(100) + lenData = self.GetText(2, data) + self.assertEquals(self.ed.GetStatus(), 0) + self.assertEquals(lenData, 1) + self.assertEquals(data.value, "a") + + def testGetTextJustNUL(self): + self.assertEquals(self.ed.GetStatus(), 0) + self.SetText(b"ab") + data = ctypes.create_unicode_buffer(100) + lenData = self.GetText(1, data) + self.assertEquals(self.ed.GetStatus(), 0) + #~ print(data) + self.assertEquals(lenData, 0) + self.assertEquals(data.value, "") + + def testGetTextZeroLength(self): + self.assertEquals(self.ed.GetStatus(), 0) + self.SetText(b"ab") + data = ctypes.create_unicode_buffer(100) + lenData = self.GetText(0, data) + self.assertEquals(self.ed.GetStatus(), 0) + #~ print(data) + self.assertEquals(lenData, 0) + self.assertEquals(data.value, "") + +if __name__ == '__main__': + uu = Xite.main("win32Tests") diff --git a/win32/ScintillaWin.cxx b/win32/ScintillaWin.cxx index a11ac94a1..31b9a59c6 100644 --- a/win32/ScintillaWin.cxx +++ b/win32/ScintillaWin.cxx @@ -1201,34 +1201,31 @@ UINT ScintillaWin::CodePageOfDocument() const { } sptr_t ScintillaWin::GetTextLength() { - if (pdoc->Length() == 0) - return 0; - std::string docBytes(pdoc->Length(), '\0'); - pdoc->GetCharRange(&docBytes[0], 0, pdoc->Length()); - if (IsUnicodeMode()) { - return UTF16Length(std::string_view(&docBytes[0], docBytes.size())); - } else { - return WideCharLenFromMultiByte(CodePageOfDocument(), docBytes); - } + return pdoc->CountUTF16(0, pdoc->Length()); } sptr_t ScintillaWin::GetText(uptr_t wParam, sptr_t lParam) { + if (lParam == 0) { + return pdoc->CountUTF16(0, pdoc->Length()); + } + if (wParam == 0) { + return 0; + } wchar_t *ptr = static_cast<wchar_t *>(PtrFromSPtr(lParam)); if (pdoc->Length() == 0) { *ptr = L'\0'; return 0; } - std::string docBytes(pdoc->Length(), '\0'); - pdoc->GetCharRange(&docBytes[0], 0, pdoc->Length()); + const Sci::Position lengthWanted = wParam - 1; + Sci::Position sizeRequestedRange = pdoc->GetRelativePositionUTF16(0, lengthWanted); + if (sizeRequestedRange < 0) { + // Requested more text than there is in the document. + sizeRequestedRange = pdoc->CountUTF16(0, pdoc->Length()); + } + std::string docBytes(sizeRequestedRange, '\0'); + pdoc->GetCharRange(&docBytes[0], 0, sizeRequestedRange); if (IsUnicodeMode()) { - const std::string_view sv(&docBytes[0], docBytes.size()); - const size_t lengthUTF16 = UTF16Length(sv); - if (lParam == 0) - return lengthUTF16; - if (wParam == 0) - return 0; - size_t uLen = UTF16FromUTF8(sv, - ptr, wParam - 1); + const size_t uLen = UTF16FromUTF8(docBytes, ptr, lengthWanted); ptr[uLen] = L'\0'; return uLen; } else { @@ -1236,8 +1233,8 @@ sptr_t ScintillaWin::GetText(uptr_t wParam, sptr_t lParam) { // Convert to Unicode using the current Scintilla code page const UINT cpSrc = CodePageOfDocument(); int lengthUTF16 = WideCharLenFromMultiByte(cpSrc, docBytes); - if (lengthUTF16 >= static_cast<int>(wParam)) - lengthUTF16 = static_cast<int>(wParam)-1; + if (lengthUTF16 > lengthWanted) + lengthUTF16 = static_cast<int>(lengthWanted); WideCharFromMultiByte(cpSrc, docBytes, ptr, lengthUTF16); ptr[lengthUTF16] = L'\0'; return lengthUTF16; |