Skip to content

Commit 1053089

Browse files
committed
Console Unicode fixes
Console Scintilla now set to UTF8, and UTF8 text used, stored and set in the input editbox. Printing unicode text to the console still doesn't work properly, so it's not perfect, but it's much better. Included some instructions in the docs about working with unicode text. Upped version to 0.9
1 parent b607a1a commit 1053089

File tree

6 files changed

+87
-29
lines changed

6 files changed

+87
-29
lines changed

PythonScript/project/PythonScript2010.vcxproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@
182182
<IntrinsicFunctions>true</IntrinsicFunctions>
183183
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;PYTHONSCRIPT2010_EXPORTS;BOOST_PYTHON_STATIC_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
184184
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
185-
<AdditionalIncludeDirectories>../include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
185+
<AdditionalIncludeDirectories>../include;../res;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
186186
<MultiProcessorCompilation>true</MultiProcessorCompilation>
187187
</ClCompile>
188188
<Link>

PythonScript/src/ConsoleDialog.cpp

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "Notepad_plus_msgs.h"
88
#include "PluginInterface.h"
99
#include "Docking.h"
10+
#include "WcharMbcsConverter.h"
1011

1112
ConsoleDialog::ConsoleDialog() :
1213
DockingDlgInterface(IDD_CONSOLE),
@@ -235,60 +236,65 @@ void ConsoleDialog::historyPrevious()
235236
{
236237
if (m_currentHistory > 0)
237238
{
238-
char buffer[1000];
239-
GetWindowTextA(m_hInput, buffer, 1000);
239+
int length = GetWindowTextLength(m_hInput);
240+
TCHAR *buffer = new TCHAR[length + 1];
241+
GetWindowText(m_hInput, buffer, length + 1);
240242

241243
// Not an empty string and different from orig
242244
if (buffer[0] && (m_historyIter == m_history.end() || *m_historyIter != buffer))
243245
{
244246
if (m_changes.find(m_currentHistory) == m_changes.end())
245247
{
246-
m_changes.insert(std::pair<int, std::string>(m_currentHistory, std::string(buffer)));
248+
m_changes.insert(std::pair<int, tstring>(m_currentHistory, tstring(buffer)));
247249
}
248250
else
249251
{
250-
m_changes[m_currentHistory] = std::string(buffer);
252+
m_changes[m_currentHistory] = tstring(buffer);
251253
}
252254
}
255+
delete [] buffer;
253256

254257
--m_currentHistory;
255258
--m_historyIter;
256259

257260
// If there's no changes to the line, just copy the original
258261
if (m_changes.find(m_currentHistory) == m_changes.end())
259262
{
260-
::SetWindowTextA(m_hInput, m_historyIter->c_str());
263+
::SetWindowText(m_hInput, m_historyIter->c_str());
261264
::SendMessage(m_hInput, EM_SETSEL, m_historyIter->size(), (LPARAM)m_historyIter->size());
262265
}
263266
else
264267
{
265268
// Set it as the changed string
266-
::SetWindowTextA(m_hInput, m_changes[m_currentHistory].c_str());
269+
::SetWindowText(m_hInput, m_changes[m_currentHistory].c_str());
267270
::SendMessage(m_hInput, EM_SETSEL, m_changes[m_currentHistory].size(), (LPARAM)m_changes[m_currentHistory].size());
268271
}
272+
269273
}
270274
}
271275

272276
void ConsoleDialog::historyNext()
273277
{
274278
if (static_cast<size_t>(m_currentHistory) < m_history.size())
275279
{
276-
char buffer[1000];
277-
GetWindowTextA(m_hInput, buffer, 1000);
280+
int length = GetWindowTextLength(m_hInput);
281+
TCHAR *buffer = new TCHAR[length + 1];
282+
GetWindowText(m_hInput, buffer, length + 1);
278283

279284

280285
// Not an empty string and different from orig
281286
if (buffer[0] && *m_historyIter != buffer)
282287
{
283288
if (m_changes.find(m_currentHistory) == m_changes.end())
284289
{
285-
m_changes.insert(std::pair<int, std::string>(m_currentHistory, std::string(buffer)));
290+
m_changes.insert(std::pair<int, tstring>(m_currentHistory, tstring(buffer)));
286291
}
287292
else
288293
{
289-
m_changes[m_currentHistory] = std::string(buffer);
294+
m_changes[m_currentHistory] = tstring(buffer);
290295
}
291296
}
297+
delete [] buffer;
292298

293299
++m_currentHistory;
294300
++m_historyIter;
@@ -298,7 +304,7 @@ void ConsoleDialog::historyNext()
298304
{
299305
if (m_historyIter != m_history.end())
300306
{
301-
::SetWindowTextA(m_hInput, m_historyIter->c_str());
307+
::SetWindowText(m_hInput, m_historyIter->c_str());
302308
::SendMessage(m_hInput, EM_SETSEL, m_historyIter->size(), (LPARAM)m_historyIter->size());
303309
}
304310
else
@@ -309,18 +315,18 @@ void ConsoleDialog::historyNext()
309315
else
310316
{
311317
// Set it as the changed string
312-
::SetWindowTextA(m_hInput, m_changes[m_currentHistory].c_str());
318+
::SetWindowText(m_hInput, m_changes[m_currentHistory].c_str());
313319
::SendMessage(m_hInput, EM_SETSEL, m_changes[m_currentHistory].size(), (LPARAM)m_changes[m_currentHistory].size());
314320
}
315321
}
316322
}
317323

318324

319-
void ConsoleDialog::historyAdd(const char *line)
325+
void ConsoleDialog::historyAdd(const TCHAR *line)
320326
{
321327
if (line && line[0])
322328
{
323-
m_history.push_back(std::string(line));
329+
m_history.push_back(tstring(line));
324330
m_currentHistory = m_history.size();
325331
}
326332

@@ -332,7 +338,7 @@ void ConsoleDialog::historyEnd()
332338
{
333339
m_currentHistory = m_history.size();
334340
m_historyIter = m_history.end();
335-
::SetWindowTextA(m_hInput, "");
341+
::SetWindowText(m_hInput, _T(""));
336342
}
337343

338344

@@ -387,14 +393,20 @@ void ConsoleDialog::runStatement()
387393
assert(m_console != NULL);
388394
if (m_console)
389395
{
390-
char buffer[1000];
391-
GetWindowTextA(::GetDlgItem(_hSelf, IDC_INPUT), buffer, 1000);
396+
397+
HWND hText = ::GetDlgItem(_hSelf, IDC_INPUT);
398+
int length = GetWindowTextLengthW(hText);
399+
TCHAR *buffer = new TCHAR[length + 1];
400+
GetWindowTextW(hText, buffer, length + 1);
392401
historyAdd(buffer);
402+
std::shared_ptr<char> charBuffer = WcharMbcsConverter::tchar2char(buffer);
403+
delete [] buffer;
404+
393405
writeText(m_prompt.size(), m_prompt.c_str());
394-
writeText(strlen(buffer), buffer);
406+
writeText(strlen(charBuffer.get()), charBuffer.get());
395407
writeText(1, "\n");
396-
SetWindowTextA(::GetDlgItem(_hSelf, IDC_INPUT), "");
397-
m_console->runStatement(buffer);
408+
SetWindowText(hText, _T(""));
409+
m_console->runStatement(charBuffer.get());
398410
}
399411
}
400412

@@ -428,6 +440,8 @@ void ConsoleDialog::createOutputWindow(HWND hParentWindow)
428440
* ... to be continued
429441
*/
430442

443+
// Set the codepage to UTF-8
444+
callScintilla(SCI_SETCODEPAGE, 65001);
431445

432446
// 0 is stdout, black text
433447
callScintilla(SCI_STYLESETSIZE, 0 /* = style number */, 8 /* = size in points */);

PythonScript/src/ConsoleDialog.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class ConsoleDialog : public DockingDlgInterface
4545
static LRESULT inputWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
4646
void historyNext();
4747
void historyPrevious();
48-
void historyAdd(const char *line);
48+
void historyAdd(const TCHAR *line);
4949
void historyEnd();
5050

5151
LRESULT callScintilla(UINT message, WPARAM wParam = 0, LPARAM lParam = 0)
@@ -72,9 +72,9 @@ class ConsoleDialog : public DockingDlgInterface
7272
WNDPROC m_originalInputWndProc;
7373
HICON m_hTabIcon;
7474

75-
std::list<std::string> m_history;
76-
std::list<std::string>::iterator m_historyIter;
77-
std::map<idx_t, std::string> m_changes;
75+
std::list<tstring> m_history;
76+
std::list<tstring>::iterator m_historyIter;
77+
std::map<idx_t, tstring> m_changes;
7878
idx_t m_currentHistory;
7979
bool m_runButtonIsRun;
8080

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#ifndef _PYTHONSCRIPTVERSION_H
22
#define _PYTHONSCRIPTVERSION_H
33

4-
#define PYSCR_VERSION_NUMERIC 0,8,0,1
5-
#define PYSCR_VERSION_STRING "0.8.0.1"
4+
#define PYSCR_VERSION_NUMERIC 0,9,0,0
5+
#define PYSCR_VERSION_STRING "0.9.0.0"
66

77
#endif

docs/source/intro.rst

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,40 @@ If you create a new module (i.e. a new file), and want to use the functions defi
6262
As the startup script runs in the same namespace as the scripts (__main__), you don't need to import the Npp module in the scripts.
6363

6464

65+
Working with Unicode Text
66+
=========================
67+
68+
Python 2.x works with multi-byte strings, as does Scintilla. That means in most cases, you don't need to do anything special to deal with unicode data,
69+
as both sides are talking the same language. However, there are a few things to observe, and occassionaly you'll need to do something special to achieve
70+
what you want to do. One important point is to make sure your script is saved in the same encoding as your target file(s) - this helps unicode strings
71+
be interpreted the same way.
72+
73+
If you need to work with the string (for instance chance the case), you need to convert the string to a Python Unicode string. To convert the string
74+
append ``.decode('utf8')`` to the string. Obviously if your string is in a different format, use the name of the correct encoding.
75+
76+
To put text back to Scintilla (so editor.something()), use .encode('utf8') from a unicode string.
77+
78+
For example::
79+
80+
# define a unicode variable
81+
someUnicodeString = u'This häs fünny ünicode chäractêrs in it'
82+
83+
# append the text to the current buffer - assuming the current buffer is set to utf8
84+
editor.addText(someUnicodeString.encode('utf8')
85+
86+
# grab the first line
87+
firstLine = editor.getLine(0)
88+
89+
# convert it to unicode
90+
firstLineUnicode = firstLine.decode('utf8')
91+
92+
# make it upper case
93+
firstLineUnicode = firstLineUnicode.upper()
94+
95+
# and put the line back
96+
editor.replaceWholeLine(firstLineUnicode.encode('utf8')
97+
98+
6599
.. _Notifications:
66100

67101
Handling Notifications
@@ -174,7 +208,9 @@ automatically, you don't need to do anything different) - the only difference is
174208
you might receive the event some time after it has actually occurred. In normal circumstances the time delay is so small it
175209
doesn't matter, but you may need to be aware of it if you're doing something time-sensitive.
176210

177-
If this causes anyone a problem, please let me know, and I'll certainly see what I can do - but no promises!
211+
This does mean that cancelling the SCN_AUTOCSELECTION notification is not possible (as the SCI_AUTOCCANCEL message must be sent
212+
before the notification returns). I'm open to suggestions for this, but processing the notifications sychronously would mean
213+
(slightly) reduced performance of "normal" code, added complication and more risk of obscure threading bugs.
178214

179215

180216
.. _Python: http://www.python.org/

todo.txt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11

22
TODO: Stuff
3-
3+
4+
DONE EnsureDirectoryExists for user scripts on New Script
5+
6+
7+
8+
9+
10+
11+
--------------
412
DONE - Console buffer.write writes '0' at the end?
513

614
DONE console stdout for console commands.

0 commit comments

Comments
 (0)