tagging release
[dasher.git] / Src / Win32 / Widgets / Edit.cpp
blob93bdad198707c2e4c0491bacd06dadc4e2c7d3c9
1 // Edit.cpp
2 //
3 /////////////////////////////////////////////////////////////////////////////
4 //
5 // Copyright (c) 2002-2006 David Ward
6 //
7 /////////////////////////////////////////////////////////////////////////////
9 /*
10 File I/O is very simplistic. It relies on the fact that there isn't
11 going to be loads of text in the edit box. Otherwise I'm sure performance
12 would be unacceptable.
15 #include "WinCommon.h"
17 #include "Edit.h"
18 #include "../../DasherCore/Event.h"
19 #include "FilenameGUI.h"
20 #include "../resource.h"
21 #include "../../DasherCore/DasherInterfaceBase.h"
22 #include "../ActionSpeech.h"
24 #include "../Common/DasherEncodingToCP.h"
26 using namespace Dasher;
27 using namespace std;
28 using namespace WinLocalisation;
29 using namespace WinUTF8;
32 /////////////////////////////////////////////////////////////////////////////
34 CEdit::CEdit(CAppSettings *pAppSettings) : m_FontSize(0), m_FontName(""), FileHandle(INVALID_HANDLE_VALUE),
35 m_FilenameGUI(0), threadid(0), targetwindow(0),
36 textentry(false)
39 m_pAppSettings = pAppSettings;
41 CodePage = GetUserCodePage();
42 m_Font = GetCodePageFont(CodePage, 14);
44 // FIXME - move speech into a new file
46 // Initialise speech support
47 speech.resize(0);
50 m_pActionSpeech = new CActionSpeech;
51 m_pActionSpeech->Activate();
55 ////////////////////////////////////////////////////////////////////////////
57 HWND CEdit::Create(HWND hParent, bool bNewWithDate)
59 m_hWnd = CWindowImpl<CEdit>::Create(hParent, NULL, NULL, ES_NOHIDESEL | WS_CHILD | ES_MULTILINE | WS_VSCROLL | WS_VISIBLE, WS_EX_CLIENTEDGE);
62 Tstring WindowTitle;
63 WinLocalisation::GetResourceString(IDS_APP_TITLE, &WindowTitle);
64 m_FilenameGUI = new CFilenameGUI(hParent, WindowTitle.c_str(), bNewWithDate);
66 return m_hWnd;
70 CEdit::~CEdit() {
71 DeleteObject(m_Font);
73 delete m_FilenameGUI;
74 if(FileHandle != INVALID_HANDLE_VALUE)
75 CloseHandle(FileHandle);
77 m_pActionSpeech->Deactivate();
78 delete m_pActionSpeech;
82 void CEdit::Move(int x, int y, int Width, int Height)
84 MoveWindow( x, y, Width, Height, TRUE);
87 void CEdit::New(const string &filename) {
88 Tstring newFilename;
89 UTF8string_to_wstring(filename, newFilename);
90 TNew(newFilename);
93 bool CEdit::Open(const string &filename) {
94 Tstring openFilename;
95 UTF8string_to_wstring(filename, openFilename);
96 return TOpen(openFilename);
99 bool CEdit::OpenAppendMode(const string &filename) {
100 Tstring openFilename;
101 UTF8string_to_wstring(filename, openFilename);
102 return TOpenAppendMode(openFilename);
105 bool CEdit::SaveAs(const string &filename) {
106 Tstring saveFilename;
107 UTF8string_to_wstring(filename, saveFilename);
108 return TSaveAs(saveFilename);
111 /* CEdit::Save() - Save to file: {{{
113 Write a Byte Order Mark (BOM) if writing a Unicode file.
114 (Convert to wide in ANSI version and then) convert to desired codepage.
115 Dump to file
116 }}}*/
117 bool CEdit::Save() {
118 if(FileHandle == INVALID_HANDLE_VALUE) {
119 if(m_filename == TEXT(""))
120 return false;
121 FileHandle = CreateFile(m_filename.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES) NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL);
123 if(FileHandle == INVALID_HANDLE_VALUE)
124 return false;
127 // Truncate File to 0 bytes.
128 SetFilePointer(FileHandle, NULL, NULL, FILE_BEGIN);
129 SetEndOfFile(FileHandle);
131 // Get all the text from the edit control
132 LRESULT EditLength = 1 + SendMessage( WM_GETTEXTLENGTH, 0, 0);
133 TCHAR *EditText = new TCHAR[EditLength];
134 EditLength = SendMessage( WM_GETTEXT, (WPARAM) EditLength, (LPARAM) EditText);
136 DWORD NumberOfBytesWritten; // Used by WriteFile
138 // This is Windows therefore we tag Unicode files with BOMs (Byte Order Marks) {{{
139 // Then notepad and other Windows apps can recognise the files.
140 // Do NOT write BOMs in a UNIX version, they are not welcome there.
141 // The BOM is just an encoding of U+FEFF (ZERO WIDTH NO-BREAK SPACE)
142 // This is unambiguous as U+FFFE is not a valid Unicode character.
143 // There could be a menu option for this, but most users won't know what a BOM is. }}}
144 unsigned int WideLength = 0;
145 wchar_t *WideText = 0;
146 if((m_Encoding == Opts::UTF16LE) || (m_Encoding == Opts::UTF16BE)) {
147 // These are the UTF-16 formats. If the string isn't already in UTF-16 we need
148 // it to be so.
149 #ifdef _UNICODE
150 WideLength = EditLength;
151 WideText = EditText;
152 #else
153 WideText = new wchar_t[EditLength + 1];
154 WideLength = MultiByteToWideChar(CodePage, 0, EditText, -1, WideText, EditLength + 1);
155 #endif
157 switch (m_Encoding) {
158 case Opts::UTF8:{ // there is no byte order, but BOM tags it as a UTF-8 file
159 unsigned char BOM[3] = { 0xEF, 0xBB, 0xBF };
160 WriteFile(FileHandle, &BOM, 3, &NumberOfBytesWritten, NULL);
161 Tstring Tmp = EditText;
162 string Output;
163 wstring_to_UTF8string(EditText, Output);
164 WriteFile(FileHandle, Output.c_str(), Output.size(), &NumberOfBytesWritten, NULL);
165 break;
167 case Opts::UTF16LE:{
168 // TODO I am assuming this machine is LE. Do any windows (perhaps CE) machines run on BE?
169 unsigned char BOM[2] = { 0xFF, 0xFE };
170 WriteFile(FileHandle, &BOM, 2, &NumberOfBytesWritten, NULL);
171 WriteFile(FileHandle, WideText, WideLength * 2, &NumberOfBytesWritten, NULL);
172 #ifndef _UNICODE
173 delete[]WideText;
174 #endif
175 break;
177 case Opts::UTF16BE:{ // UTF-16BE
178 // TODO I am again assuming this machine is LE.
179 unsigned char BOM[2] = { 0xFE, 0xFF };
180 WriteFile(FileHandle, &BOM, 2, &NumberOfBytesWritten, NULL);
181 // There will be a better way. Perhaps use _swab instead.
182 for(unsigned int i = 0; i < WideLength; i++) {
183 const char *Hack = (char *)&WideText[i];
184 WriteFile(FileHandle, Hack + 1, 1, &NumberOfBytesWritten, NULL);
185 WriteFile(FileHandle, Hack, 1, &NumberOfBytesWritten, NULL);
187 #ifndef _UNICODE
188 delete[]WideText;
189 #endif
190 break;
192 default:
193 #ifdef _UNICODE
194 char *MultiByteText = new char[EditLength * 4];
195 int MultiByteLength = WideCharToMultiByte(CodePage, 0, EditText, EditLength, MultiByteText, EditLength * 4, NULL, NULL);
196 WriteFile(FileHandle, MultiByteText, MultiByteLength, &NumberOfBytesWritten, NULL);
197 delete[]MultiByteText;
198 #else
199 WriteFile(FileHandle, EditText, EditLength, &NumberOfBytesWritten, NULL);
200 #endif
201 break; // do nothing
204 delete[]EditText;
205 // The file handle is not closed here. We keep a write-lock on the file to stop other programs confusing us.
207 m_FilenameGUI->SetDirty(false);
208 m_dirty = false;
209 return true;
212 //void CEdit::TimeStampNewFiles(bool Value) {
213 // m_FilenameGUI->SetNewWithDate(Value);
216 void CEdit::New() {
217 switch (m_FilenameGUI->QuerySaveFirst()) {
218 case IDYES:
219 if(!Save())
220 if(!TSaveAs(m_FilenameGUI->SaveAs()))
221 return;
222 break;
223 case IDNO:
224 break;
225 default:
226 return;
228 TNew(TEXT(""));
231 void CEdit::Open() {
232 switch (m_FilenameGUI->QuerySaveFirst()) {
233 case IDYES:
234 if(!Save())
235 if(!TSaveAs(m_FilenameGUI->SaveAs()))
236 return;
237 break;
238 case IDNO:
239 break;
240 default:
241 return;
242 break;
244 TOpen(m_FilenameGUI->Open());
247 void CEdit::OpenAppendMode() {
248 switch (m_FilenameGUI->QuerySaveFirst()) {
249 case IDYES:
250 if(!Save())
251 if(!TSaveAs(m_FilenameGUI->SaveAs()))
252 return;
253 break;
254 case IDNO:
255 break;
256 default:
257 return;
258 break;
260 TOpenAppendMode(m_FilenameGUI->Open());
263 void CEdit::SaveAs() {
264 TSaveAs(m_FilenameGUI->SaveAs());
267 std::string CEdit::Import() {
268 string filename;
269 wstring_to_UTF8string(m_FilenameGUI->Open(), filename);
270 return filename;
273 void CEdit::SetDirty() {
274 m_dirty = true;
275 m_FilenameGUI->SetDirty(true);
278 void CEdit::TNew(const Tstring &filename) {
279 if(filename == TEXT(""))
280 m_filename = m_FilenameGUI->New();
281 else
282 m_filename = filename;
283 if(FileHandle != INVALID_HANDLE_VALUE)
284 CloseHandle(FileHandle);
285 FileHandle = INVALID_HANDLE_VALUE;
286 AppendMode = false;
287 Clear();
289 // m_pDasherInterface->InvalidateContext(true);
292 bool CEdit::TOpen(const Tstring &filename) {
293 // Could try and detect unicode formats from BOMs like notepad.
294 // Could also base codepage on menu.
295 // Best thing is probably to trust any BOMs at the beginning of file, but otherwise
296 // to believe menu. Unicode files don't necessarily have BOMs, especially from Unix.
298 HANDLE TmpHandle = CreateFile(filename.c_str(), GENERIC_READ | GENERIC_WRITE,
299 FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES) NULL,
300 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
301 (HANDLE) NULL);
303 if(TmpHandle == INVALID_HANDLE_VALUE)
304 return false;
306 if(FileHandle != INVALID_HANDLE_VALUE)
307 CloseHandle(FileHandle);
308 FileHandle = TmpHandle;
309 m_filename = filename;
311 SetFilePointer(FileHandle, NULL, NULL, FILE_BEGIN);
313 DWORD filesize = GetFileSize(FileHandle, NULL);
314 unsigned long amountread;
316 char *filebuffer = new char[filesize];
318 // Just read in whole file as char* and cast later.
320 ReadFile(FileHandle, filebuffer, filesize, &amountread, NULL);
322 string text;
323 text = text + filebuffer;
324 Tstring inserttext;
325 UTF8string_to_wstring(text, inserttext);
326 InsertText(inserttext);
328 AppendMode = false;
329 m_FilenameGUI->SetFilename(m_filename);
330 m_FilenameGUI->SetDirty(false);
331 m_dirty = false;
332 return true;
335 bool CEdit::TOpenAppendMode(const Tstring &filename) {
337 AppendMode = true;
338 return true;
341 bool CEdit::TSaveAs(const Tstring &filename) {
342 HANDLE TmpHandle = CreateFile(filename.c_str(), GENERIC_READ | GENERIC_WRITE,
343 FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES) NULL,
344 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
345 (HANDLE) NULL);
347 if(TmpHandle == INVALID_HANDLE_VALUE)
348 return false;
350 if(FileHandle != INVALID_HANDLE_VALUE)
351 CloseHandle(FileHandle);
352 FileHandle = TmpHandle;
354 m_filename = filename;
355 if(Save()) {
356 m_FilenameGUI->SetFilename(m_filename);
357 return true;
359 else
360 return false;
363 void CEdit::Cut()
365 SendMessage(WM_CUT, 0, 0);
368 void CEdit::Copy()
370 SendMessage(WM_COPY, 0, 0);
372 #ifndef _UNICODE
373 HGLOBAL handle;
374 DWORD* foo;
375 handle = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, sizeof(DWORD));
376 foo = (DWORD*) GlobalLock(handle);
377 *foo = MAKELCID(MAKELANGID(LANG_JAPANESE, SUBLANG_DEFAULT), SORT_DEFAULT);
378 GlobalUnlock(handle);
379 OpenClipboard(m_hwnd);
380 SetClipboardData(CF_LOCALE, handle);
381 CloseClipboard();
382 #endif
386 void CEdit::CopyAll()
388 // One might think this would lead to flickering of selecting and
389 // unselecting. It doesn't seem to. Using the clipboard directly
390 // is fiddly, so this cheat is useful.
391 DWORD start, finish;
392 SendMessage(EM_GETSEL, (LONG) & start, (LONG) & finish);
393 SendMessage(EM_SETSEL, 0, -1);
394 SendMessage(WM_COPY, 0, 0);
395 SendMessage(EM_SETSEL, (LONG) start, (LONG) finish);
398 void CEdit::Paste()
400 SendMessage(WM_PASTE, 0, 0);
403 void CEdit::SelectAll()
405 SendMessage(EM_SETSEL, 0, -1);
408 void CEdit::Clear()
410 SendMessage(WM_SETTEXT, 0, (LPARAM) TEXT(""));
411 speech.resize(0);
414 void CEdit::SetEncoding(Dasher::Opts::FileEncodingFormats Encoding) {
415 m_Encoding = Encoding;
418 void CEdit::SetFont(string Name, long Size) {
420 #ifndef _WIN32_WCE
422 m_FontName = Name;
423 m_FontSize = Size;
425 Tstring FontName;
426 UTF8string_to_wstring(Name, FontName);
428 if(Size == 0)
429 Size = 14;
431 DeleteObject(m_Font);
432 if(Name == "")
433 m_Font = GetCodePageFont(CodePage, -Size);
434 else
435 m_Font = CreateFont(-Size, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE, FontName.c_str()); // DEFAULT_CHARSET => font made just from Size and FontName
437 SendMessage(WM_SETFONT, (WPARAM) m_Font, true);
438 #else
439 // not implemented
440 #pragma message ( "CEdit::SetFot not implemented on WinCE")
441 DASHER_ASSERT(0);
442 #endif
446 void CEdit::SetInterface(Dasher::CDasherInterfaceBase *DasherInterface) {
447 m_pDasherInterface = DasherInterface;
449 // TODO: What on Earth is this doing here?
450 SetFont(m_FontName, m_FontSize);
453 void CEdit::write_to_file() {
454 // TODO: Reimplement if necessary
456 //const string & TrainFile = m_pDasherInterface->GetTrainFile();
457 //if(TrainFile == "")
458 // return;
459 //Tstring TTrainFile;
460 //UTF8string_to_wstring(TrainFile, TTrainFile);
462 //HANDLE hFile = CreateFile(TTrainFile.c_str(),
463 // GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
465 //if(hFile == INVALID_HANDLE_VALUE) {
466 // OutputDebugString(TEXT("Can not open file\n"));
468 //else {
469 // DWORD NumberOfBytesWritten;
470 // SetFilePointer(hFile, 0, NULL, FILE_END);
471 // for(unsigned int i = 0; i < m_Output.size(); i++) {
472 // WriteFile(hFile, &m_Output[i], 1, &NumberOfBytesWritten, NULL);
473 // }
475 // m_Output = "";
476 // CloseHandle(hFile);
480 void CEdit::get_new_context(string &str, int max) {
481 // Currently all of the edit box up to the caret is copied
482 // and then a few characters taken from that.
483 // This is a bit silly, but works for now. IAM 2002/08
485 int iStart, iFinish;
486 SendMessage(EM_GETSEL, (LONG) & iStart, (LONG) & iFinish);
488 TCHAR *tString = new TCHAR[iFinish + 1];
490 SendMessage(WM_GETTEXT, (LONG) (iStart + 1), (LONG) tString);
492 string Wasteful;
493 wstring_to_UTF8string(tString, Wasteful);
495 if(Wasteful.size() > max)
496 str = Wasteful.substr(Wasteful.size() - max, max);
497 else
498 str = Wasteful;
500 delete[]tString;
503 void CEdit::output(const std::string &sText) {
504 wstring String;
505 WinUTF8::UTF8string_to_wstring(sText, String);
507 InsertText(String);
509 if(m_pAppSettings->GetLongParameter(APP_LP_STYLE) == 2) {
510 const char *DisplayText = sText.c_str();
511 #ifdef UNICODE
512 if(DisplayText[0] == 0xd && DisplayText[1] == 0xa) {
513 // Newline, so we want to fake an enter
514 fakekey[0].type = fakekey[1].type = INPUT_KEYBOARD;
515 fakekey[0].ki.wVk = fakekey[1].ki.wVk = VK_RETURN;
516 fakekey[0].ki.time = fakekey[1].ki.time = 0;
517 fakekey[1].ki.dwFlags = KEYEVENTF_KEYUP;
518 SendInput(2, fakekey, sizeof(INPUT));
520 else {
521 for(std::wstring::iterator it(String.begin()); it != String.end(); ++it) {
522 fakekey[0].type = INPUT_KEYBOARD;
523 #ifdef DASHER_WINCE
524 fakekey[0].ki.dwFlags = KEYEVENTF_KEYUP;
525 #else
526 fakekey[0].ki.dwFlags = KEYEVENTF_UNICODE;
527 #endif
528 fakekey[0].ki.wVk = 0;
529 fakekey[0].ki.time = NULL;
530 fakekey[0].ki.wScan = *it;
531 SendInput(1, fakekey, sizeof(INPUT));
534 #else
535 if(DisplayText[0] == 0xd && DisplayText[1] == 0xa) {
536 // Newline, so we want to fake an enter
537 SetFocus(targetwindow);
538 keybd_event(VK_RETURN, 0, NULL, NULL);
539 keybd_event(VK_RETURN, 0, KEYEVENTF_KEYUP, NULL);
541 Tstring character;
542 WinUTF8::UTF8string_to_wstring(DisplayText, &character, 1252);
543 TCHAR test = character[0];
544 SHORT outputvk = VkKeyScan(char (character[0]));
545 SetFocus(targetwindow);
546 if(HIBYTE(outputvk) && 6) {
547 keybd_event(VK_SHIFT, 0, NULL, NULL);
548 keybd_event(LOBYTE(outputvk), 0, NULL, NULL);
549 keybd_event(LOBYTE(outputvk), 0, KEYEVENTF_KEYUP, NULL);
550 keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, NULL);
552 else {
553 keybd_event(LOBYTE(outputvk), 0, NULL, NULL);
554 keybd_event(LOBYTE(outputvk), 0, KEYEVENTF_KEYUP, NULL);
556 #endif
558 m_Output += sText;
560 UTF8string_to_wstring(sText, newchar);
561 speech += newchar;
564 void CEdit::Move(int iDirection, int iDist) {
566 // Unfortunately there doesn't seem to be a sane way of obtaining the caret
567 // position (as opposed to the bounds of the selection), so we're just going
568 // to have to assume that the caret is at the end...
570 int iStart;
571 int iEnd;
572 SendMessage(EM_GETSEL, (WPARAM)&iStart, (LPARAM)&iEnd);
574 // int iStartLine;
575 int iEndLine;
576 // int iLineOffset;
577 // int iLineLength;
578 // int iLineStart;
579 // int iNumLines;
580 int iNumChars;
582 HLOCAL hMemHandle;
583 std::wstring strBufferText;
585 if(iDirection == EDIT_FORWARDS) {
586 switch(iDist) {
587 case EDIT_CHAR:
588 //if(iStart != iEnd)
589 ++iEnd;
590 iStart = iEnd;
591 break;
592 case EDIT_WORD:
593 // Hmm... words are hard - this is a rough and ready approximation:
595 iNumChars = SendMessage(WM_GETTEXTLENGTH, 0, 0);
596 hMemHandle = (HLOCAL)SendMessage( EM_GETHANDLE, 0, 0);
597 strBufferText = std::wstring((WCHAR*)LocalLock(hMemHandle), iNumChars);
598 LocalUnlock(hMemHandle);
600 iEnd = strBufferText.find(' ', iEnd+1);
601 if(iEnd == -1)
602 iEnd = iNumChars + 1;
603 else
604 iEnd = iEnd + 1;
605 iStart = iEnd;
606 break;
607 case EDIT_LINE:
608 /* iEndLine = SendMessage( EM_LINEFROMCHAR, (WPARAM)iEnd, 0);
609 iLineOffset = iEnd - SendMessage( EM_LINEINDEX, (WPARAM)iEndLine, 0);
610 iNumLines = SendMessage( EM_GETLINECOUNT, 0, 0);
611 if( iEndLine < iNumLines - 1) {
612 ++iEndLine;
613 iLineStart = SendMessage( EM_LINEINDEX, (WPARAM)iEndLine, 0);
614 iLineLength = SendMessage( EM_LINELENGTH, (WPARAM)iEndLine, 0);
615 if( iLineOffset < iLineLength )
616 iEnd = iLineStart+iLineOffset;
617 else
618 iEnd = iLineStart+iLineLength;
620 else if(iEndLine == iNumLines - 1) {
621 // we're on the last line so go to end of file
622 iNumChars = SendMessage(WM_GETTEXTLENGTH, 0, 0);
623 iEnd = iNumChars + 1;
626 // Make it behave like the 'End' key, unless we're at the end of the current line.
627 // Then go down a line.
628 iEndLine = SendMessage(EM_LINEFROMCHAR, (WPARAM)iEnd, 0);
629 iEnd = SendMessage(EM_LINEINDEX, (WPARAM)(iEndLine + 1), 0) - 1; // end of this line
630 if(iStart==iEnd) // we were already at the end so go down a line
631 iEnd = SendMessage(EM_LINEINDEX, (WPARAM)(iEndLine + 2), 0) - 1;
632 iStart = iEnd;
633 break;
634 case EDIT_FILE:
635 iNumChars = SendMessage(WM_GETTEXTLENGTH, 0, 0);
636 iEnd = iNumChars + 1;
637 iStart = iEnd;
638 break;
641 else {
642 switch(iDist) {
643 case EDIT_CHAR:
644 if( iStart == iEnd )
645 --iStart;
646 iEnd = iStart;
647 break;
648 case EDIT_WORD:
649 iNumChars = SendMessage(WM_GETTEXTLENGTH, 0, 0);
650 hMemHandle = (HLOCAL)SendMessage(EM_GETHANDLE, 0, 0);
651 strBufferText = std::wstring((WCHAR*)LocalLock(hMemHandle), iNumChars);
652 LocalUnlock(hMemHandle);
654 if(iEnd > 0) {
655 iEnd = strBufferText.rfind(' ', iEnd-2);
656 if(iEnd == -1)
657 iEnd = 0;
658 else
659 iEnd = iEnd + 1;
661 iStart = iEnd;
662 break;
663 case EDIT_LINE:
665 iStartLine = SendMessage(EM_LINEFROMCHAR, (WPARAM)iStart, 0);
666 iEndLine = SendMessage(EM_LINEFROMCHAR, (WPARAM)iEnd, 0);
667 iLineOffset = iEnd - SendMessage(EM_LINEINDEX, (WPARAM)iEndLine, 0);
668 if( iStartLine > 0)
669 --iStartLine;
670 else if( iStartLine == 0)
672 // we're on the first line so go to start of file...
673 iStart = iEnd = 0;
674 break;
676 iLineStart = SendMessage(EM_LINEINDEX, (WPARAM)iStartLine, 0);
677 iLineLength = SendMessage(EM_LINELENGTH, (WPARAM)iStartLine, 0);
678 if( iLineOffset < iLineLength )
679 iStart = iLineStart+iLineOffset;
680 else
681 iStart = iLineStart+iLineLength;
683 iEndLine = SendMessage(EM_LINEFROMCHAR, (WPARAM)iEnd, 0);
684 iEnd = SendMessage(EM_LINEINDEX, (WPARAM)(iEndLine), 0); // start of this line
685 if(iStart==iEnd) // we were already at the start so go up a line
686 iEnd = SendMessage(EM_LINEINDEX, (WPARAM)(iEndLine - 1), 0);
687 iStart = iEnd;
688 break;
689 case EDIT_FILE:
690 iStart = 0;
691 iEnd = 0;
692 break;
696 SendMessage(EM_SETSEL, (WPARAM)iStart, (LPARAM)iEnd);
697 SendMessage(EM_SCROLLCARET, 0, 0); //scroll the caret into view!
700 void CEdit::Delete(int iDirection, int iDist) {
701 int iStart;
702 int iEnd;
703 int iEndLine;
704 // int iLineOffset;
705 // int iLineLength;
706 // int iLineStart;
707 // int iNumLines;
708 int iNumChars;
710 HLOCAL hMemHandle;
711 std::wstring strBufferText;
713 SendMessage(EM_GETSEL, (WPARAM)&iStart, (LPARAM)&iEnd);
715 if(iDirection == EDIT_FORWARDS) {
716 switch(iDist) {
717 case EDIT_CHAR:
718 ++iEnd;
719 break;
720 case EDIT_WORD:
721 iNumChars = SendMessage(WM_GETTEXTLENGTH, 0, 0);
722 hMemHandle = (HLOCAL)SendMessage(EM_GETHANDLE, 0, 0);
723 strBufferText = std::wstring((WCHAR*)LocalLock(hMemHandle), iNumChars);
724 LocalUnlock(hMemHandle);
726 iEnd = strBufferText.find(' ', iEnd+1);
727 if(iEnd == -1)
728 iEnd = iNumChars + 1;
729 break;
730 case EDIT_LINE:
732 iEndLine = SendMessage(EM_LINEFROMCHAR, (WPARAM)iEnd, 0);
733 iLineOffset = iEnd - SendMessage(EM_LINEINDEX, (WPARAM)iEndLine, 0);
734 iNumLines = SendMessage(EM_GETLINECOUNT, 0, 0);
735 if( iEndLine < iNumLines - 1) {
736 ++iEndLine;
737 iLineStart = SendMessage(EM_LINEINDEX, (WPARAM)iEndLine, 0);
738 iLineLength = SendMessage(EM_LINELENGTH, (WPARAM)iEndLine, 0);
739 if( iLineOffset < iLineLength )
740 iEnd = iLineStart+iLineOffset;
741 else
742 iEnd = iLineStart+iLineLength;
745 iEndLine = SendMessage(EM_LINEFROMCHAR, (WPARAM)iEnd, 0);
746 iEnd = SendMessage(EM_LINEINDEX, (WPARAM)(iEndLine + 1), 0); // end of this line
747 if(iStart==iEnd) // we were already at the end so go down a line
748 iEnd = SendMessage(EM_LINEINDEX, (WPARAM)(iEndLine + 2), 0);
749 break;
750 case EDIT_FILE:
751 iNumChars = SendMessage(WM_GETTEXTLENGTH, 0, 0);
752 iEnd = iNumChars + 1;
753 break;
756 else {
757 switch(iDist) {
758 case EDIT_CHAR:
759 --iEnd;
760 break;
761 case EDIT_WORD:
762 iNumChars = SendMessage(WM_GETTEXTLENGTH, 0, 0);
763 hMemHandle = (HLOCAL)SendMessage(EM_GETHANDLE, 0, 0);
764 strBufferText = std::wstring((WCHAR*)LocalLock(hMemHandle), iNumChars);
765 LocalUnlock(hMemHandle);
767 if(iEnd > 0) {
768 iEnd = strBufferText.rfind(' ', iEnd-2);
769 if(iEnd == -1)
770 iEnd = 0;
771 else
772 iEnd = iEnd + 1;
774 break;
775 case EDIT_LINE:
776 /* iEndLine = SendMessage(EM_LINEFROMCHAR, (WPARAM)iEnd, 0);
777 iLineOffset = iEnd - SendMessage(EM_LINEINDEX, (WPARAM)iEndLine, 0);
778 iNumLines = SendMessage(EM_GETLINECOUNT, 0, 0);
779 if(iEndLine > 0) {
780 --iEndLine;
781 iLineStart = SendMessage(EM_LINEINDEX, (WPARAM)iEndLine, 0);
782 iLineLength = SendMessage(EM_LINELENGTH, (WPARAM)iEndLine, 0);
783 if( iLineOffset < iLineLength )
784 iEnd = iLineStart+iLineOffset;
785 else
786 iEnd = iLineStart+iLineLength;
789 iEndLine = SendMessage(EM_LINEFROMCHAR, (WPARAM)iEnd, 0);
790 iEnd = SendMessage(EM_LINEINDEX, (WPARAM)(iEndLine), 0); // start of this line
791 if(iStart==iEnd) // we were already at the start so go up a line
792 iEnd = SendMessage(EM_LINEINDEX, (WPARAM)(iEndLine - 1), 0);
794 break;
795 case EDIT_FILE:
796 iEnd = 0;
797 break;
801 SendMessage(EM_SETSEL, (WPARAM)iStart, (LPARAM)iEnd);
802 SendMessage(EM_REPLACESEL, (WPARAM)true, (LPARAM)"");
805 /////////////////////////////////////////////////////////////////////////////
807 void CEdit::SetKeyboardTarget(HWND hwnd)
809 m_bForwardKeyboard = true;
810 m_hTarget = hwnd;
813 /////////////////////////////////////////////////////////////////////////////
815 LRESULT CEdit::OnLButtonDown(UINT message, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
817 m_bForwardKeyboard = false;
818 bHandled = FALSE; // let the EDIT class handle it
819 return 0;
822 /////////////////////////////////////////////////////////////////////////////
824 LRESULT CEdit::OnLButtonUp(UINT message, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
826 // m_pDasherInterface->InvalidateContext(false);
828 bHandled = FALSE; // let the EDIT class handle it
829 return 0;
832 /////////////////////////////////////////////////////////////////////////////
834 HRESULT CEdit::OnChar(UINT message, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
836 if(!m_bForwardKeyboard)
837 bHandled = FALSE; // let the EDIT class handle it
838 else
839 bHandled = TRUE; // traps the message, preventing it from reaching the EDIT control
841 return 0;
844 /////////////////////////////////////////////////////////////////////////////
846 HRESULT CEdit::OnKeyDown(UINT message, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
848 if(m_bForwardKeyboard)
850 SendMessage(m_hTarget,message,wParam,lParam);
851 bHandled = TRUE; // traps the message, preventing it from reaching the EDIT control
853 else
854 bHandled = FALSE; // let the EDIT class handle it
855 return 0;
858 /////////////////////////////////////////////////////////////////////////////
860 HRESULT CEdit::OnKeyUp(UINT message, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
862 if(m_bForwardKeyboard)
864 SendMessage(m_hTarget,message,wParam,lParam);
865 bHandled = TRUE; // traps the message, preventing it from reaching the EDIT control
867 else
869 // if we enter text or move around the edit control, update the Dasher display
870 //if (Canvas->Running()==false) { // FIXME - reimplement this
871 // m_pDasherInterface->ChangeEdit();
873 InvalidateRect(NULL, FALSE);
874 bHandled = FALSE; // let the EDIT class handle it
876 return 0;
879 /////////////////////////////////////////////////////////////////////////////
881 HRESULT CEdit::OnCommand(UINT message, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
883 bHandled = TRUE;
884 return SendMessage( GetParent() , message, wParam, lParam);
887 /////////////////////////////////////////////////////////////////////////////
889 void CEdit::InsertText(Tstring InsertText) {
890 SendMessage(EM_REPLACESEL, TRUE, (LPARAM) InsertText.c_str());
893 // TODO The following were inline in DJW's code. Think about reinstating
896 void CEdit::dumpedit(int i) const
899 TCHAR deb[32];
900 wsprintf(deb,TEXT("edit %d %x\n"),i,m_hwnd);
901 OutputDebugString(deb);
905 /// Delete text from the editbox
907 void CEdit::deletetext(const std::string &sText) {
908 // Lookup the unicode string that we need to delete - we only actually
909 // need the length of the string, but this is important eg for newline
910 // characters which are actually two symbols
912 wstring String;
913 WinUTF8::UTF8string_to_wstring(sText, String);
915 int iLength(String.size());
917 // Get the start and end of the current selection, and decrement the start
918 // by the number of characters to be deleted
920 DWORD start, finish;
921 SendMessage(EM_GETSEL, (LONG) & start, (LONG) & finish);
922 start -= iLength;
923 SendMessage(EM_SETSEL, (LONG) start, (LONG) finish);
925 // Replace the selection with a null string
927 TCHAR out[2];
928 wsprintf(out, TEXT(""));
929 SendMessage(EM_REPLACESEL, TRUE, (LONG) out);
931 // FIXME - I *think* we still only want to send one keyboard event to delete a
932 // newline pair, but we're now assuming we'll never have two real characters for
933 // a single symbol
935 // if(targetwindow != NULL && textentry == true) {
936 if(m_pAppSettings->GetLongParameter(APP_LP_STYLE) == 2) {
938 #ifdef _UNICODE
939 fakekey[0].type = fakekey[1].type = INPUT_KEYBOARD;
940 fakekey[0].ki.wVk = fakekey[1].ki.wVk = VK_BACK;
941 fakekey[0].ki.time = fakekey[1].ki.time = 0;
942 fakekey[1].ki.dwFlags = KEYEVENTF_KEYUP;
944 ::SetFocus(targetwindow);
945 SendInput(2, fakekey, sizeof(INPUT));
946 #else
947 SetFocus(targetwindow);
948 keybd_event(VK_BACK, 0, NULL, NULL);
949 keybd_event(VK_BACK, 0, KEYEVENTF_KEYUP, NULL);
950 #endif
953 // Shorten the speech buffer (?)
954 if(speech.length() >= iLength) {
955 speech.resize(speech.length() - iLength);
958 // And the output buffer (?)
959 if(m_Output.length() >= iLength) {
960 m_Output.resize(m_Output.length() - iLength);
964 //void CEdit::SetWindow(HWND window)
967 //#ifndef DASHER_WINCE
969 // if(targetwindow != window) {
971 // targetwindow = window;
973 // if(threadid != NULL) {
975 // AttachThreadInput(GetCurrentThreadId(), threadid, FALSE);
977 // // SetFocus(Parent);
979 // }
981 // if(window != NULL) {
983 // threadid = GetWindowThreadProcessId(window, NULL);
985 // AttachThreadInput(GetCurrentThreadId(), GetWindowThreadProcessId(window, NULL), TRUE);
987 // // SetFocus(window);
989 // }
991 // }
993 //#endif
998 void CEdit::speak(int what) {
999 if(!m_pActionSpeech->GetActive())
1000 return;
1002 // TODO: The remainder of this function is somewhat horrible and hacky...
1004 // TODO: Horrible hack - don't speak in direct entry mode
1005 if(m_pAppSettings->GetLongParameter(APP_LP_STYLE) == 2)
1006 return;
1008 std::wstring strSpeech;
1010 if(what == 1) { // All
1011 int speechlength = GetWindowTextLength();
1012 LPTSTR allspeech = new TCHAR[speechlength + 1];
1013 GetWindowText(allspeech, speechlength + 1);
1014 strSpeech = allspeech;
1015 lastspeech = allspeech;
1016 delete allspeech;
1017 speech.resize(0);
1019 else if(what == 2) { // New
1020 strSpeech = speech;
1021 lastspeech = speech;
1022 speech.resize(0);
1024 else if(what == 3) {
1025 strSpeech = lastspeech;
1028 m_pActionSpeech->Execue(strSpeech);
1031 void CEdit::SetNewWithDate(bool bNewWithDate) {
1032 if(m_FilenameGUI)
1033 m_FilenameGUI->SetNewWithDate(bNewWithDate);
1037 void CEdit::HandleEvent(Dasher::CEvent *pEvent) {
1038 // TODO: Note the mess in the parent class which ulitmately results in this being called - sort it out sometime
1040 switch(pEvent->m_iEventType) {
1041 case EV_EDIT:
1042 HandleEditEvent(pEvent);
1043 break;
1044 case EV_STOP:
1045 HandleStop();
1046 break;
1050 void CEdit::HandleEditEvent(Dasher::CEvent *pEvent) {
1051 Dasher::CEditEvent * pEvt(static_cast< Dasher::CEditEvent * >(pEvent));
1053 switch (pEvt->m_iEditType) {
1054 case 1:
1055 output(pEvt->m_sText);
1056 break;
1057 case 2:
1058 deletetext(pEvt->m_sText);
1059 break;
1063 void CEdit::HandleStop() {
1064 if(m_pAppSettings->GetBoolParameter(APP_BP_SPEECH_MODE))
1065 speak(2);
1067 if(m_pAppSettings->GetBoolParameter(APP_BP_COPY_ALL_ON_STOP))
1068 CopyAll();