support full-width/composite characters and true color palette in terminal (#1386)
[far2l.git] / far2l / src / edit.cpp
blobe58c26ee9d5e9a88c72c23b2d549e6de5d01f6e3
1 /*
2 edit.cpp
4 Реализация одиночной строки редактирования
5 */
6 /*
7 Copyright (c) 1996 Eugene Roshal
8 Copyright (c) 2000 Far Group
9 All rights reserved.
11 Redistribution and use in source and binary forms, with or without
12 modification, are permitted provided that the following conditions
13 are met:
14 1. Redistributions of source code must retain the above copyright
15 notice, this list of conditions and the following disclaimer.
16 2. Redistributions in binary form must reproduce the above copyright
17 notice, this list of conditions and the following disclaimer in the
18 documentation and/or other materials provided with the distribution.
19 3. The name of the authors may not be used to endorse or promote products
20 derived from this software without specific prior written permission.
22 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 #include "headers.hpp"
37 #include "edit.hpp"
38 #include "keyboard.hpp"
39 #include "macroopcode.hpp"
40 #include "lang.hpp"
41 #include "keys.hpp"
42 #include "editor.hpp"
43 #include "ctrlobj.hpp"
44 #include "filepanels.hpp"
45 #include "filelist.hpp"
46 #include "panel.hpp"
47 #include "scrbuf.hpp"
48 #include "interf.hpp"
49 #include "palette.hpp"
50 #include "clipboard.hpp"
51 #include "xlat.hpp"
52 #include "datetime.hpp"
53 #include "Bookmarks.hpp"
54 #include "pathmix.hpp"
55 #include "strmix.hpp"
56 #include "panelmix.hpp"
57 #include "RegExp.hpp"
58 #include "history.hpp"
59 #include "vmenu.hpp"
60 #include "chgmmode.hpp"
61 #include <cwctype>
63 static int Recurse=0;
65 enum {EOL_NONE,EOL_CR,EOL_LF,EOL_CRLF,EOL_CRCRLF};
66 static const wchar_t *EOL_TYPE_CHARS[]={L"",L"\r",L"\n",L"\r\n",L"\r\r\n"};
68 #define EDMASK_ANY L'X' // позволяет вводить в строку ввода любой символ;
69 #define EDMASK_DSS L'#' // позволяет вводить в строку ввода цифры, пробел и знак минуса;
70 #define EDMASK_DIGIT L'9' // позволяет вводить в строку ввода только цифры;
71 #define EDMASK_DIGITS L'N' // позволяет вводить в строку ввода только цифры и пробелы;
72 #define EDMASK_ALPHA L'A' // позволяет вводить в строку ввода только буквы.
73 #define EDMASK_HEX L'H' // позволяет вводить в строку ввода шестнадцатиричные символы.
75 class DisableCallback
77 bool OldState;
78 bool *CurState;
79 public:
80 DisableCallback(bool &State){OldState=State;CurState=&State;State=false;}
81 void Restore(){*CurState=OldState;}
82 ~DisableCallback(){Restore();}
85 Edit::Edit(ScreenObject *pOwner, Callback* aCallback, bool bAllocateData):
86 m_next(nullptr),
87 m_prev(nullptr),
88 Str(bAllocateData ? reinterpret_cast<wchar_t*>(malloc(sizeof(wchar_t))) : nullptr),
89 StrSize(0),
90 MaxLength(-1),
91 Mask(nullptr),
92 LeftPos(0),
93 CurPos(0),
94 PrevCurPos(0),
95 MSelStart(-1),
96 SelStart(-1),
97 SelEnd(0),
98 CursorSize(-1),
99 CursorPos(0)
101 m_Callback.Active=true;
102 m_Callback.m_Callback=nullptr;
103 m_Callback.m_Param=nullptr;
105 if (aCallback) m_Callback=*aCallback;
107 SetOwner(pOwner);
108 SetWordDiv(Opt.strWordDiv);
110 if (bAllocateData)
111 *Str=0;
113 Flags.Set(FEDITLINE_EDITBEYONDEND);
114 Color=F_LIGHTGRAY|B_BLACK;
115 SelColor=F_WHITE|B_BLACK;
116 ColorUnChanged=COL_DIALOGEDITUNCHANGED;
117 EndType=EOL_NONE;
118 ColorList=nullptr;
119 ColorCount=0;
120 TabSize=Opt.EdOpt.TabSize;
121 TabExpandMode = EXPAND_NOTABS;
122 Flags.Change(FEDITLINE_DELREMOVESBLOCKS,Opt.EdOpt.DelRemovesBlocks);
123 Flags.Change(FEDITLINE_PERSISTENTBLOCKS,Opt.EdOpt.PersistentBlocks);
124 Flags.Change(FEDITLINE_SHOWWHITESPACE,Opt.EdOpt.ShowWhiteSpace);
125 m_codepage = 0; //BUGBUG
129 Edit::~Edit()
131 if (ColorList)
132 free(ColorList);
134 if (Mask)
135 free(Mask);
137 if (Str)
138 free(Str);
141 DWORD Edit::SetCodePage(UINT codepage)
143 DWORD Ret=SETCP_NOERROR;
144 DWORD wc2mbFlags=WC_NO_BEST_FIT_CHARS;
145 BOOL UsedDefaultChar=FALSE;
146 LPBOOL lpUsedDefaultChar=&UsedDefaultChar;
148 if (m_codepage==CP_UTF7 || m_codepage==CP_UTF8 || m_codepage==CP_UTF16LE|| m_codepage==CP_UTF16BE) // BUGBUG: CP_SYMBOL, 50xxx, 57xxx too
150 wc2mbFlags=0;
151 lpUsedDefaultChar=nullptr;
154 DWORD mb2wcFlags=MB_ERR_INVALID_CHARS;
156 if (codepage==CP_UTF7) // BUGBUG: CP_SYMBOL, 50xxx, 57xxx too
158 mb2wcFlags=0;
161 if (codepage != m_codepage)
163 if (Str && *Str)
165 //m_codepage = codepage;
166 int length = WINPORT(WideCharToMultiByte)(m_codepage, wc2mbFlags, Str, StrSize, nullptr, 0, nullptr, lpUsedDefaultChar);
168 if (UsedDefaultChar)
169 Ret|=SETCP_WC2MBERROR;
171 char *decoded = (char*)malloc(length);
173 if (!decoded)
175 Ret|=SETCP_OTHERERROR;
176 return Ret;
179 WINPORT(WideCharToMultiByte)(m_codepage, 0, Str, StrSize, decoded, length, nullptr, nullptr);
180 int length2 = WINPORT(MultiByteToWideChar)(codepage, mb2wcFlags, decoded, length, nullptr, 0);
182 if (!length2 && WINPORT(GetLastError)()==ERROR_NO_UNICODE_TRANSLATION)
184 Ret|=SETCP_MB2WCERROR;
185 length2 = WINPORT(MultiByteToWideChar)(codepage, 0, decoded, length, nullptr, 0);
188 wchar_t *encoded = (wchar_t*)malloc((length2+1)*sizeof(wchar_t));
190 if (!encoded)
192 free(decoded);
193 Ret|=SETCP_OTHERERROR;
194 return Ret;
197 length2 = WINPORT(MultiByteToWideChar)(codepage, 0, decoded, length, encoded, length2);
198 encoded[length2] = L'\0';
199 free(decoded);
200 free(Str);
201 Str = encoded;
202 StrSize = length2;
205 m_codepage = codepage;
206 Changed();
209 return Ret;
212 UINT Edit::GetCodePage()
214 return m_codepage;
218 void Edit::DisplayObject()
220 if (Flags.Check(FEDITLINE_DROPDOWNBOX))
222 Flags.Clear(FEDITLINE_CLEARFLAG); // при дроп-даун нам не нужно никакого unchanged text
223 SelStart=0;
224 SelEnd=StrSize; // а также считаем что все выделено -
225 // надо же отличаться от обычных Edit
228 // Вычисление нового положения курсора в строке с учётом Mask.
229 int Value=(PrevCurPos>CurPos)?-1:1;
230 CurPos=GetNextCursorPos(CurPos,Value);
231 FastShow();
233 /* $ 26.07.2000 tran
234 при DropDownBox курсор выключаем
235 не знаю даже - попробовал но не очень красиво вышло */
236 if (Flags.Check(FEDITLINE_DROPDOWNBOX))
237 ::SetCursorType(0,10);
238 else
240 if (Flags.Check(FEDITLINE_OVERTYPE))
242 int NewCursorSize=IsFullscreen()?
243 (Opt.CursorSize[3]?Opt.CursorSize[3]:99):
244 (Opt.CursorSize[2]?Opt.CursorSize[2]:99);
245 ::SetCursorType(1,CursorSize==-1?NewCursorSize:CursorSize);
247 else
249 int NewCursorSize=IsFullscreen()?
250 (Opt.CursorSize[1]?Opt.CursorSize[1]:10):
251 (Opt.CursorSize[0]?Opt.CursorSize[0]:10);
252 ::SetCursorType(1,CursorSize==-1?NewCursorSize:CursorSize);
256 MoveCursor(X1+CursorPos-LeftPos,Y1);
260 void Edit::SetCursorType(bool Visible, DWORD Size)
262 Flags.Change(FEDITLINE_CURSORVISIBLE,Visible);
263 CursorSize=Size;
264 ::SetCursorType(Visible,Size);
267 void Edit::GetCursorType(bool& Visible, DWORD& Size)
269 Visible=Flags.Check(FEDITLINE_CURSORVISIBLE)!=FALSE;
270 Size=CursorSize;
273 // Вычисление нового положения курсора в строке с учётом Mask.
274 int Edit::GetNextCursorPos(int Position,int Where)
276 int Result=Position;
278 if (Mask && *Mask && (Where==-1 || Where==1))
280 int PosChanged=FALSE;
281 int MaskLen=StrLength(Mask);
283 for (int i=Position; i<MaskLen && i>=0; i+=Where)
285 if (CheckCharMask(Mask[i]))
287 Result=i;
288 PosChanged=TRUE;
289 break;
293 if (!PosChanged)
295 for (int i=Position; i>=0; i--)
297 if (CheckCharMask(Mask[i]))
299 Result=i;
300 PosChanged=TRUE;
301 break;
306 if (!PosChanged)
308 for (int i=Position; i<MaskLen; i++)
310 if (CheckCharMask(Mask[i]))
312 Result=i;
313 break;
319 return Result;
322 void Edit::FastShow()
324 int EditLength=ObjWidth;
326 if (!Flags.Check(FEDITLINE_EDITBEYONDEND) && CurPos>StrSize && StrSize>=0)
327 CurPos=StrSize;
329 if (MaxLength!=-1)
331 if (StrSize>MaxLength)
333 Str[MaxLength]=0;
334 StrSize=MaxLength;
337 if (CurPos>MaxLength-1)
338 CurPos=MaxLength>0 ? (MaxLength-1):0;
341 int CellCurPos=GetCellCurPos();
343 /* $ 31.07.2001 KM
344 ! Для комбобокса сделаем отображение строки
345 с первой позиции.
347 int RealLeftPos = -1;
348 if (!Flags.Check(FEDITLINE_DROPDOWNBOX))
350 if (CellCurPos - LeftPos > EditLength - 1)
352 // tricky left pos shifting to
353 // - avoid LeftPos pointing into middle of full-width char cells pair
354 // - ensure RealLeftPos really shifted in case string starts by some long character
355 for (int ShiftBy = 1; ShiftBy <= std::max(TabSize, 2); ++ShiftBy)
357 RealLeftPos = CellPosToReal(CellCurPos - EditLength + ShiftBy);
358 int NewLeftPos = RealPosToCell(RealLeftPos);
359 if (LeftPos != NewLeftPos)
361 LeftPos = NewLeftPos;
362 break;
367 if (CellCurPos < LeftPos)
368 LeftPos = CellCurPos;
371 if (RealLeftPos == -1)
372 RealLeftPos = CellPosToReal(LeftPos);
374 GotoXY(X1,Y1);
375 int CellSelStart=(SelStart==-1) ? -1:RealPosToCell(SelStart);
376 int CellSelEnd=(SelEnd<0) ? -1:RealPosToCell(SelEnd);
378 /* $ 17.08.2000 KM
379 Если есть маска, сделаем подготовку строки, то есть
380 все "постоянные" символы в маске, не являющиеся шаблонными
381 должны постоянно присутствовать в Str
383 if (Mask && *Mask)
384 RefreshStrByMask();
386 CursorPos = CellCurPos;
388 OutStr.clear();
389 size_t OutStrCells = 0;
390 for (int i = RealLeftPos; i < StrSize && int(OutStrCells) < EditLength; ++i)
392 auto wc = Str[i];
393 if (wc == L' ' && Flags.Check(FEDITLINE_SHOWWHITESPACE) && Flags.Check(FEDITLINE_EDITORMODE))
395 wc = L'\xB7';
398 if (wc == L'\t')
400 for (int j = 0, S = TabSize - ((LeftPos + OutStrCells) % TabSize);
401 j < S && int(OutStrCells) < EditLength;
402 ++j, ++OutStrCells)
404 OutStr.emplace_back(
405 (Flags.Check(FEDITLINE_SHOWWHITESPACE) && Flags.Check(FEDITLINE_EDITORMODE) && !j)
406 ? L'\x2192' : L' ');
409 else
411 if (IsCharFullWidth(wc))
413 if (int(OutStrCells + 2) > EditLength)
415 OutStr.emplace_back(L' ');
416 OutStrCells++;
417 break;
419 OutStrCells+= 2;
421 else if (!IsCharXxxfix(wc) || i == RealLeftPos)
422 OutStrCells++;
424 OutStr.emplace_back(wc ? wc : L' ');
428 if (Flags.Check(FEDITLINE_PASSWORDMODE))
430 OutStr.resize(OutStrCells);
431 std::fill(OutStr.begin(), OutStr.end(), L'*');
434 OutStr.emplace_back(0);
435 SetColor(Color);
437 if (CellSelStart==-1)
439 if (Flags.Check(FEDITLINE_CLEARFLAG))
441 SetColor(ColorUnChanged);
443 if (Mask && *Mask)
445 RemoveTrailingSpaces(OutStr.data());
446 OutStr.resize(wcslen(OutStr.data()));
447 OutStrCells = StrCellsCount(OutStr.data(), OutStr.size());
448 OutStr.emplace_back(0);
451 FS << fmt::Cells() << fmt::LeftAlign() << OutStr.data();
452 SetColor(Color);
453 int BlankLength = EditLength - (int)OutStrCells;
455 if (BlankLength > 0)
457 FS << fmt::Cells() << fmt::Expand(BlankLength) << L"";
460 else
462 FS << fmt::LeftAlign() << fmt::Cells() << fmt::Size(EditLength) << OutStr.data();
465 else
467 if ((CellSelStart-=LeftPos)<0)
468 CellSelStart=0;
470 int AllString = (CellSelEnd==-1);
472 if (AllString)
473 CellSelEnd = EditLength;
474 else if ((CellSelEnd-=LeftPos)<0)
475 CellSelEnd=0;
477 for (;int(OutStrCells) < EditLength; ++OutStrCells)
479 OutStr.emplace(OutStr.begin() + OutStr.size() - 1, L' ');
482 /* $ 24.08.2000 SVS
483 ! У DropDowList`а выделение по полной программе - на всю видимую длину
484 ДАЖЕ ЕСЛИ ПУСТАЯ СТРОКА
486 if (CellSelStart>=EditLength /*|| !AllString && CellSelStart>=StrSize*/ ||
487 CellSelEnd<CellSelStart)
489 if (Flags.Check(FEDITLINE_DROPDOWNBOX))
491 SetColor(SelColor);
492 FS << fmt::Cells() << fmt::Expand(X2 - X1 + 1) << OutStr.data();
494 else
495 Text(OutStr.data());
497 else
499 FS << fmt::Cells() << fmt::Truncate(CellSelStart) << OutStr.data();
500 SetColor(SelColor);
502 if (!Flags.Check(FEDITLINE_DROPDOWNBOX))
504 FS << fmt::Cells() << fmt::Skip(CellSelStart) << fmt::Truncate(CellSelEnd - CellSelStart) << OutStr.data();
506 if (CellSelEnd < EditLength)
508 //SetColor(Flags.Check(FEDITLINE_CLEARFLAG) ? SelColor:Color);
509 SetColor(Color);
510 FS << fmt::Cells() << fmt::Skip(CellSelEnd) << OutStr.data();
513 else
515 FS << fmt::Cells() << fmt::Expand(X2 - X1 + 1) << OutStr.data();
520 /* $ 26.07.2000 tran
521 при дроп-даун цвета нам не нужны */
522 if (!Flags.Check(FEDITLINE_DROPDOWNBOX))
523 ApplyColor();
527 int Edit::RecurseProcessKey(int Key)
529 Recurse++;
530 int RetCode=ProcessKey(Key);
531 Recurse--;
532 return(RetCode);
536 // Функция вставки всякой хреновени - от шорткатов до имен файлов
537 int Edit::ProcessInsPath(int Key,int PrevSelStart,int PrevSelEnd)
539 int RetCode=FALSE;
540 FARString strPathName;
542 if (Key>=KEY_RCTRL0 && Key<=KEY_RCTRL9) // шорткаты?
544 FARString strPluginModule, strPluginFile, strPluginData;
546 if (Bookmarks().Get(Key-KEY_RCTRL0,&strPathName,&strPluginModule,&strPluginFile,&strPluginData))
547 RetCode=TRUE;
549 else // Пути/имена?
551 RetCode=_MakePath1(Key,strPathName,L"");
554 // Если что-нить получилось, именно его и вставим (PathName)
555 if (RetCode)
557 if (Flags.Check(FEDITLINE_CLEARFLAG))
559 LeftPos=0;
560 SetString(L"");
563 if (PrevSelStart!=-1)
565 SelStart=PrevSelStart;
566 SelEnd=PrevSelEnd;
569 if (!Flags.Check(FEDITLINE_PERSISTENTBLOCKS))
570 DeleteBlock();
572 InsertString(strPathName);
573 Flags.Clear(FEDITLINE_CLEARFLAG);
576 return RetCode;
580 int64_t Edit::VMProcess(int OpCode,void *vParam,int64_t iParam)
582 switch (OpCode)
584 case MCODE_C_EMPTY:
585 return (int64_t)!GetLength();
586 case MCODE_C_SELECTED:
587 return (int64_t)(SelStart != -1 && SelStart < SelEnd);
588 case MCODE_C_EOF:
589 return (int64_t)(CurPos >= StrSize);
590 case MCODE_C_BOF:
591 return (int64_t)!CurPos;
592 case MCODE_V_ITEMCOUNT:
593 return (int64_t)StrSize;
594 case MCODE_V_CURPOS:
595 return (int64_t)(CursorPos+1);
596 case MCODE_F_EDITOR_SEL:
598 int Action=(int)((INT_PTR)vParam);
600 switch (Action)
602 case 0: // Get Param
604 switch (iParam)
606 case 0: // return FirstLine
607 case 2: // return LastLine
608 return IsSelection()?1:0;
609 case 1: // return FirstPos
610 return IsSelection()?SelStart+1:0;
611 case 3: // return LastPos
612 return IsSelection()?SelEnd:0;
613 case 4: // return block type (0=nothing 1=stream, 2=column)
614 return IsSelection()?1:0;
617 break;
619 case 1: // Set Pos
621 if (IsSelection())
623 switch (iParam)
625 case 0: // begin block (FirstLine & FirstPos)
626 case 1: // end block (LastLine & LastPos)
628 SetCellCurPos(iParam?SelEnd:SelStart);
629 Show();
630 return 1;
635 break;
637 case 2: // Set Stream Selection Edge
638 case 3: // Set Column Selection Edge
640 switch (iParam)
642 case 0: // selection start
644 MSelStart=GetCurPos();
645 return 1;
647 case 1: // selection finish
649 if (MSelStart != -1)
651 if (MSelStart != GetCurPos())
652 Select(MSelStart,GetCurPos());
653 else
654 Select(-1,0);
656 Show();
657 MSelStart=-1;
658 return 1;
661 return 0;
665 break;
667 case 4: // UnMark sel block
669 Select(-1,0);
670 MSelStart=-1;
671 Show();
672 return 1;
676 break;
680 return 0;
683 int Edit::CalcRTrimmedStrSize() const
685 int TrimmedStrSize = StrSize;
686 while (TrimmedStrSize > 0 && (IsSpace(Str[TrimmedStrSize - 1]) || IsEol(Str[TrimmedStrSize - 1])))
688 --TrimmedStrSize;
690 return TrimmedStrSize;
693 int Edit::CalcPosFwdTo(int Pos, int LimitPos) const
695 if (LimitPos != -1)
697 if (Pos < LimitPos) do
699 Pos++;
700 } while (Pos < LimitPos && Pos < StrSize && IsCharXxxfix(Str[Pos]));
702 else do
704 Pos++;
705 } while (Pos < StrSize && IsCharXxxfix(Str[Pos]));
707 return Pos;
710 int Edit::CalcPosBwdTo(int Pos) const
712 if (Pos <= 0)
713 return 0;
717 --Pos;
718 } while (Pos > 0 && IsCharXxxfix(Str[Pos]));
720 return Pos;
723 int Edit::ProcessKey(int Key)
725 switch (Key)
727 case KEY_ADD:
728 Key=L'+';
729 break;
730 case KEY_SUBTRACT:
731 Key=L'-';
732 break;
733 case KEY_MULTIPLY:
734 Key=L'*';
735 break;
736 case KEY_DIVIDE:
737 Key=L'/';
738 break;
739 case KEY_DECIMAL:
740 Key=L'.';
741 break;
742 case KEY_CTRLC:
743 Key=KEY_CTRLINS;
744 break;
745 case KEY_CTRLV:
746 Key=KEY_SHIFTINS;
747 break;
748 case KEY_CTRLX:
749 Key=KEY_SHIFTDEL;
750 break;
753 int PrevSelStart=-1,PrevSelEnd=0;
755 if (!Flags.Check(FEDITLINE_DROPDOWNBOX) && Key==KEY_CTRLL)
757 Flags.Swap(FEDITLINE_READONLY);
760 /* $ 26.07.2000 SVS
761 Bugs #??
762 В строках ввода при выделенном блоке нажимаем BS и вместо
763 ожидаемого удаления блока (как в редакторе) получаем:
764 - символ перед курсором удален
765 - выделение блока снято
767 if ((((Key==KEY_BS || Key==KEY_DEL || Key==KEY_NUMDEL) && Flags.Check(FEDITLINE_DELREMOVESBLOCKS)) || Key==KEY_CTRLD) &&
768 !Flags.Check(FEDITLINE_EDITORMODE) && SelStart!=-1 && SelStart<SelEnd)
770 DeleteBlock();
771 Show();
772 return TRUE;
775 int _Macro_IsExecuting=CtrlObject->Macro.IsExecuting();
777 // $ 04.07.2000 IG - добавлена проврерка на запуск макроса (00025.edit.cpp.txt)
778 if (!ShiftPressed && (!_Macro_IsExecuting || (IsNavKey(Key) && _Macro_IsExecuting)) &&
779 !IsShiftKey(Key) && !Recurse &&
780 Key!=KEY_SHIFT && Key!=KEY_CTRL && Key!=KEY_ALT &&
781 Key!=KEY_RCTRL && Key!=KEY_RALT && Key!=KEY_NONE &&
782 Key!=KEY_INS &&
783 Key!=KEY_KILLFOCUS && Key != KEY_GOTFOCUS &&
784 ((Key&(~KEY_CTRLMASK)) != KEY_LWIN && (Key&(~KEY_CTRLMASK)) != KEY_RWIN && (Key&(~KEY_CTRLMASK)) != KEY_APPS)
787 Flags.Clear(FEDITLINE_MARKINGBLOCK); // хмм... а это здесь должно быть?
789 if (!Flags.Check(FEDITLINE_PERSISTENTBLOCKS) && !(Key==KEY_CTRLINS || Key==KEY_CTRLNUMPAD0) &&
790 !(Key==KEY_SHIFTDEL||Key==KEY_SHIFTNUMDEL||Key==KEY_SHIFTDECIMAL) && !Flags.Check(FEDITLINE_EDITORMODE) && Key != KEY_CTRLQ &&
791 !(Key == KEY_SHIFTINS || Key == KEY_SHIFTNUMPAD0)) //Key != KEY_SHIFTINS) //??
793 /* $ 12.11.2002 DJ
794 зачем рисоваться, если ничего не изменилось?
796 if (SelStart != -1 || SelEnd )
798 PrevSelStart=SelStart;
799 PrevSelEnd=SelEnd;
800 Select(-1,0);
801 Show();
806 /* $ 11.09.2000 SVS
807 если Opt.DlgEULBsClear = 1, то BS в диалогах для UnChanged строки
808 удаляет такую строку также, как и Del
810 if (((Opt.Dialogs.EULBsClear && Key==KEY_BS) || Key==KEY_DEL || Key==KEY_NUMDEL) &&
811 Flags.Check(FEDITLINE_CLEARFLAG) && CurPos>=StrSize)
812 Key=KEY_CTRLY;
814 /* $ 15.09.2000 SVS
815 Bug - Выделяем кусочек строки -> Shift-Del удяляет всю строку
816 Так должно быть только для UnChanged состояния
818 if ((Key == KEY_SHIFTDEL || Key == KEY_SHIFTNUMDEL || Key == KEY_SHIFTDECIMAL) && Flags.Check(FEDITLINE_CLEARFLAG) && CurPos>=StrSize && SelStart==-1)
820 SelStart=0;
821 SelEnd=StrSize;
824 if (Flags.Check(FEDITLINE_CLEARFLAG) && ((Key <= 0xFFFF && Key!=KEY_BS) || Key==KEY_CTRLBRACKET ||
825 Key==KEY_CTRLBACKBRACKET || Key==KEY_CTRLSHIFTBRACKET ||
826 Key==KEY_CTRLSHIFTBACKBRACKET || Key==KEY_SHIFTENTER || Key==KEY_SHIFTNUMENTER))
828 LeftPos=0;
829 SetString(L"");
830 Show();
833 // Здесь - вызов функции вставки путей/файлов
834 if (ProcessInsPath(Key,PrevSelStart,PrevSelEnd))
836 Show();
837 return TRUE;
840 if (Key!=KEY_NONE && Key!=KEY_IDLE && Key!=KEY_SHIFTINS && Key!=KEY_SHIFTNUMPAD0 && Key!=KEY_CTRLINS &&
841 ((unsigned int)Key<KEY_F1 || (unsigned int)Key>KEY_F12) && Key!=KEY_ALT && Key!=KEY_SHIFT &&
842 Key!=KEY_CTRL && Key!=KEY_RALT && Key!=KEY_RCTRL &&
843 (Key<KEY_ALT_BASE || Key > KEY_ALT_BASE+0xFFFF) && // ???? 256 ???
844 !(((unsigned int)Key>=KEY_MACRO_BASE && (unsigned int)Key<=KEY_MACRO_ENDBASE) || ((unsigned int)Key>=KEY_OP_BASE && (unsigned int)Key <=KEY_OP_ENDBASE)) && Key!=KEY_CTRLQ)
846 Flags.Clear(FEDITLINE_CLEARFLAG);
847 Show();
850 switch (Key)
852 case KEY_SHIFTLEFT: case KEY_SHIFTNUMPAD4:
854 if (CurPos>0)
856 RecurseProcessKey(KEY_LEFT);
858 if (!Flags.Check(FEDITLINE_MARKINGBLOCK))
860 Select(-1,0);
861 Flags.Set(FEDITLINE_MARKINGBLOCK);
864 if (SelStart!=-1 && SelStart<=CurPos)
865 Select(SelStart,CurPos);
866 else
868 int EndPos = CalcPosFwd((Mask && *Mask) ? CalcRTrimmedStrSize() : -1);
869 int NewStartPos=CurPos;
871 if (EndPos>StrSize)
872 EndPos=StrSize;
874 if (NewStartPos>StrSize)
875 NewStartPos=StrSize;
877 AddSelect(NewStartPos,EndPos);
880 Show();
883 return TRUE;
885 case KEY_SHIFTRIGHT: case KEY_SHIFTNUMPAD6:
887 if (!Flags.Check(FEDITLINE_MARKINGBLOCK))
889 Select(-1,0);
890 Flags.Set(FEDITLINE_MARKINGBLOCK);
893 if ((SelStart!=-1 && SelEnd==-1) || SelEnd>CurPos)
895 if (CalcPosFwd() == SelEnd)
896 Select(-1,0);
897 else
898 Select(CalcPosFwd(), SelEnd);
900 else
901 AddSelect(CurPos, CalcPosFwd());
903 RecurseProcessKey(KEY_RIGHT);
904 return TRUE;
906 case KEY_CTRLSHIFTLEFT: case KEY_CTRLSHIFTNUMPAD4:
908 if (CurPos>StrSize)
910 PrevCurPos=CurPos;
911 CurPos=StrSize;
914 if (CurPos>0)
915 RecurseProcessKey(KEY_SHIFTLEFT);
917 while (CurPos>0 && !(!IsWordDiv(WordDiv(), Str[CurPos]) &&
918 IsWordDiv(WordDiv(),Str[CurPos-1]) && !IsSpace(Str[CurPos])))
920 if (!IsSpace(Str[CurPos]) && (IsSpace(Str[CurPos-1]) ||
921 IsWordDiv(WordDiv(), Str[CurPos-1])))
922 break;
924 RecurseProcessKey(KEY_SHIFTLEFT);
927 Show();
928 return TRUE;
930 case KEY_CTRLSHIFTRIGHT: case KEY_CTRLSHIFTNUMPAD6:
932 if (CurPos>=StrSize)
933 return FALSE;
935 RecurseProcessKey(KEY_SHIFTRIGHT);
937 while (CurPos<StrSize && !(IsWordDiv(WordDiv(), Str[CurPos]) &&
938 !IsWordDiv(WordDiv(), Str[CurPos-1])))
940 if (!IsSpace(Str[CurPos]) && (IsSpace(Str[CurPos-1]) || IsWordDiv(WordDiv(), Str[CurPos-1])))
941 break;
943 RecurseProcessKey(KEY_SHIFTRIGHT);
945 if (MaxLength!=-1 && CurPos==MaxLength-1)
946 break;
949 Show();
950 return TRUE;
952 case KEY_SHIFTHOME: case KEY_SHIFTNUMPAD7:
954 Lock();
956 while (CurPos>0)
957 RecurseProcessKey(KEY_SHIFTLEFT);
959 Unlock();
960 Show();
961 return TRUE;
963 case KEY_SHIFTEND: case KEY_SHIFTNUMPAD1:
965 Lock();
966 int Len = (Mask && *Mask) ? CalcRTrimmedStrSize() : StrSize;
968 int LastCurPos=CurPos;
970 while (CurPos<Len/*StrSize*/)
972 RecurseProcessKey(KEY_SHIFTRIGHT);
974 if (LastCurPos==CurPos)break;
976 LastCurPos=CurPos;
979 Unlock();
980 Show();
981 return TRUE;
983 case KEY_BS:
985 if (CurPos<=0)
986 return FALSE;
988 PrevCurPos = CurPos;
989 CurPos = CalcPosBwd();
991 while (LeftPos > 0 && RealPosToCell(CurPos) <= LeftPos)
993 LeftPos-= 15;
994 if (LeftPos > 0)
995 LeftPos = RealPosToCell(CellPosToReal(LeftPos));
996 else
997 LeftPos = 0;
1000 if (!RecurseProcessKey(KEY_DEL))
1001 Show();
1003 return TRUE;
1005 case KEY_CTRLSHIFTBS:
1007 DisableCallback DC(m_Callback.Active);
1009 // BUGBUG
1010 for (int i=CurPos; i>=0; i--)
1012 RecurseProcessKey(KEY_BS);
1014 DC.Restore();
1015 Changed(true);
1016 Show();
1017 return TRUE;
1019 case KEY_CTRLBS:
1021 if (CurPos>StrSize)
1023 PrevCurPos=CurPos;
1024 CurPos=StrSize;
1027 Lock();
1029 DisableCallback DC(m_Callback.Active);
1031 // BUGBUG
1032 for (;;)
1034 int StopDelete=FALSE;
1036 if (CurPos>1 && IsSpace(Str[CurPos-1])!=IsSpace(Str[CurPos-2]))
1037 StopDelete=TRUE;
1039 RecurseProcessKey(KEY_BS);
1041 if (!CurPos || StopDelete)
1042 break;
1044 if (IsWordDiv(WordDiv(),Str[CurPos-1]))
1045 break;
1048 Unlock();
1049 DC.Restore();
1050 Changed(true);
1051 Show();
1052 return TRUE;
1054 case KEY_CTRLQ:
1056 Lock();
1058 if (!Flags.Check(FEDITLINE_PERSISTENTBLOCKS) && (SelStart != -1 || Flags.Check(FEDITLINE_CLEARFLAG)))
1059 RecurseProcessKey(KEY_DEL);
1061 ProcessCtrlQ();
1062 Unlock();
1063 Show();
1064 return TRUE;
1066 case KEY_OP_SELWORD:
1068 int OldCurPos=CurPos;
1069 PrevSelStart=SelStart;
1070 PrevSelEnd=SelEnd;
1071 #if defined(MOUSEKEY)
1073 if (CurPos >= SelStart && CurPos <= SelEnd)
1074 { // выделяем ВСЮ строку при повторном двойном клике
1075 Select(0,StrSize);
1077 else
1078 #endif
1080 int SStart, SEnd;
1082 if (CalcWordFromString(Str,CurPos,&SStart,&SEnd,WordDiv()))
1083 Select(SStart,SEnd+(SEnd < StrSize?1:0));
1086 CurPos=OldCurPos; // возвращаем обратно
1087 Show();
1088 return TRUE;
1090 case KEY_OP_PLAINTEXT:
1092 if (!Flags.Check(FEDITLINE_PERSISTENTBLOCKS))
1094 if (SelStart != -1 || Flags.Check(FEDITLINE_CLEARFLAG)) // BugZ#1053 - Неточности в $Text
1095 RecurseProcessKey(KEY_DEL);
1098 const wchar_t *S = eStackAsString();
1100 ProcessInsPlainText(S);
1102 Show();
1103 return TRUE;
1105 case KEY_CTRLT:
1106 case KEY_CTRLDEL:
1107 case KEY_CTRLNUMDEL:
1108 case KEY_CTRLDECIMAL:
1110 if (CurPos>=StrSize)
1111 return FALSE;
1113 Lock();
1114 DisableCallback DC(m_Callback.Active);
1115 if (Mask && *Mask)
1117 int MaskLen=StrLength(Mask);
1118 int ptr=CurPos;
1120 while (ptr<MaskLen)
1122 ptr++;
1124 if (!CheckCharMask(Mask[ptr]) ||
1125 (IsSpace(Str[ptr]) && !IsSpace(Str[ptr+1])) ||
1126 (IsWordDiv(WordDiv(), Str[ptr])))
1127 break;
1130 // BUGBUG
1131 for (int i=0; i<ptr-CurPos; i++)
1132 RecurseProcessKey(KEY_DEL);
1134 else
1136 for (;;)
1138 int StopDelete=FALSE;
1140 if (CurPos<StrSize-1 && IsSpace(Str[CurPos]) && !IsSpace(Str[CurPos+1]))
1141 StopDelete=TRUE;
1143 RecurseProcessKey(KEY_DEL);
1145 if (CurPos>=StrSize || StopDelete)
1146 break;
1148 if (IsWordDiv(WordDiv(), Str[CurPos]))
1149 break;
1153 Unlock();
1154 DC.Restore();
1155 Changed(true);
1156 Show();
1157 return TRUE;
1159 case KEY_CTRLY:
1161 if (Flags.Check(FEDITLINE_READONLY|FEDITLINE_DROPDOWNBOX))
1162 return (TRUE);
1164 PrevCurPos=CurPos;
1165 LeftPos=CurPos=0;
1166 *Str=0;
1167 StrSize=0;
1168 Str=(wchar_t *)realloc(Str,1*sizeof(wchar_t));
1169 Select(-1,0);
1170 Changed();
1171 Show();
1172 return TRUE;
1174 case KEY_CTRLK:
1176 if (Flags.Check(FEDITLINE_READONLY|FEDITLINE_DROPDOWNBOX))
1177 return (TRUE);
1179 if (CurPos>=StrSize)
1180 return FALSE;
1182 if (!Flags.Check(FEDITLINE_EDITBEYONDEND))
1184 if (CurPos<SelEnd)
1185 SelEnd=CurPos;
1187 if (SelEnd<SelStart && SelEnd!=-1)
1189 SelEnd=0;
1190 SelStart=-1;
1194 Str[CurPos]=0;
1195 StrSize=CurPos;
1196 Str=(wchar_t *)realloc(Str,(StrSize+1)*sizeof(wchar_t));
1197 Changed();
1198 Show();
1199 return TRUE;
1201 case KEY_HOME: case KEY_NUMPAD7:
1202 case KEY_CTRLHOME: case KEY_CTRLNUMPAD7:
1204 PrevCurPos=CurPos;
1205 CurPos=0;
1206 Show();
1207 return TRUE;
1209 case KEY_END: case KEY_NUMPAD1:
1210 case KEY_CTRLEND: case KEY_CTRLNUMPAD1:
1211 case KEY_CTRLSHIFTEND: case KEY_CTRLSHIFTNUMPAD1:
1213 PrevCurPos = CurPos;
1214 CurPos = (Mask && *Mask) ? CalcRTrimmedStrSize() : StrSize;
1215 Show();
1216 return TRUE;
1218 case KEY_LEFT: case KEY_NUMPAD4: case KEY_MSWHEEL_LEFT:
1219 case KEY_CTRLS:
1221 if (CurPos>0)
1223 PrevCurPos = CurPos;
1224 CurPos = CalcPosBwd();
1225 Show();
1228 return TRUE;
1230 case KEY_RIGHT: case KEY_NUMPAD6: case KEY_MSWHEEL_RIGHT:
1231 case KEY_CTRLD:
1233 PrevCurPos = CurPos;
1234 CurPos = CalcPosFwd((Mask && *Mask) ? CalcRTrimmedStrSize() : -1);
1235 Show();
1236 return TRUE;
1238 case KEY_INS: case KEY_NUMPAD0:
1240 Flags.Swap(FEDITLINE_OVERTYPE);
1241 Show();
1242 return TRUE;
1244 case KEY_NUMDEL:
1245 case KEY_DEL:
1247 if (Flags.Check(FEDITLINE_READONLY|FEDITLINE_DROPDOWNBOX))
1248 return (TRUE);
1250 if (CurPos>=StrSize)
1251 return FALSE;
1253 if (SelStart!=-1)
1255 if (SelEnd!=-1 && CurPos<SelEnd)
1256 SelEnd--;
1258 if (CurPos<SelStart)
1259 SelStart--;
1261 if (SelEnd!=-1 && SelEnd<=SelStart)
1263 SelStart=-1;
1264 SelEnd=0;
1268 if (Mask && *Mask)
1270 Str[CurPos] = L' ';
1272 else
1274 auto NextPos = CalcPosFwd();
1275 if (NextPos > CurPos) {
1276 wmemmove(Str + CurPos, Str + NextPos, (StrSize - NextPos) + 1);
1277 StrSize-= (NextPos - CurPos);
1278 wchar_t *NewStr = (wchar_t *)realloc(Str, (StrSize + 1) * sizeof(wchar_t));
1279 if (NewStr) {
1280 Str = NewStr;
1285 Changed(true);
1286 Show();
1287 return TRUE;
1289 case KEY_CTRLLEFT: case KEY_CTRLNUMPAD4:
1291 PrevCurPos=CurPos;
1293 if (CurPos>StrSize)
1294 CurPos=StrSize;
1296 CurPos = CalcPosBwd();
1298 while (CurPos>0 && !(!IsWordDiv(WordDiv(), Str[CurPos]) &&
1299 IsWordDiv(WordDiv(), Str[CurPos-1]) && !IsSpace(Str[CurPos])))
1301 if (!IsSpace(Str[CurPos]) && IsSpace(Str[CurPos-1]))
1302 break;
1304 CurPos--;
1307 Show();
1308 return TRUE;
1310 case KEY_CTRLRIGHT: case KEY_CTRLNUMPAD6:
1312 if (CurPos>=StrSize)
1313 return FALSE;
1315 PrevCurPos=CurPos;
1316 int Len;
1318 if (Mask && *Mask)
1320 Len = CalcRTrimmedStrSize();
1321 CurPos = CalcPosFwd(Len);
1323 else
1325 Len = StrSize;
1326 CurPos = CalcPosFwd();
1329 while (CurPos<Len/*StrSize*/ && !(IsWordDiv(WordDiv(),Str[CurPos]) &&
1330 !IsWordDiv(WordDiv(), Str[CurPos-1])))
1332 if (!IsSpace(Str[CurPos]) && IsSpace(Str[CurPos-1]))
1333 break;
1335 CurPos++;
1338 Show();
1339 return TRUE;
1341 case KEY_SHIFTNUMDEL:
1342 case KEY_SHIFTDECIMAL:
1343 case KEY_SHIFTDEL:
1345 if (SelStart==-1 || SelStart>=SelEnd)
1346 return FALSE;
1348 RecurseProcessKey(KEY_CTRLINS);
1349 DeleteBlock();
1350 Show();
1351 return TRUE;
1353 case KEY_CTRLINS: case KEY_CTRLNUMPAD0:
1355 if (!Flags.Check(FEDITLINE_PASSWORDMODE))
1357 if (SelStart==-1 || SelStart>=SelEnd)
1359 if (Mask && *Mask)
1361 std::wstring TrimmedStr(Str, CalcRTrimmedStrSize());
1362 CopyToClipboard(TrimmedStr.c_str());
1364 else
1366 CopyToClipboard(Str);
1369 else if (SelEnd<=StrSize) // TODO: если в начало условия добавить "StrSize &&", то пропадет баг "Ctrl-Ins в пустой строке очищает клипборд"
1371 int Ch=Str[SelEnd];
1372 Str[SelEnd]=0;
1373 CopyToClipboard(Str+SelStart);
1374 Str[SelEnd]=Ch;
1378 return TRUE;
1380 case KEY_SHIFTINS: case KEY_SHIFTNUMPAD0:
1382 wchar_t *ClipText=PasteFromClipboardEx(MaxLength);
1384 if (!ClipText)
1385 return TRUE;
1387 if (!Flags.Check(FEDITLINE_PERSISTENTBLOCKS))
1389 DisableCallback DC(m_Callback.Active);
1390 DeleteBlock();
1393 for (int i=StrLength(Str)-1; i>=0 && IsEol(Str[i]); i--)
1394 Str[i]=0;
1396 for (int i=0; ClipText[i]; i++)
1398 if (IsEol(ClipText[i]))
1400 if (IsEol(ClipText[i+1]))
1401 wmemmove(&ClipText[i],&ClipText[i+1],StrLength(&ClipText[i+1])+1);
1403 if (!ClipText[i+1])
1404 ClipText[i]=0;
1405 else
1406 ClipText[i]=L' ';
1410 if (Flags.Check(FEDITLINE_CLEARFLAG))
1412 LeftPos=0;
1413 Flags.Clear(FEDITLINE_CLEARFLAG);
1414 SetString(ClipText);
1416 else
1418 InsertString(ClipText);
1421 if (ClipText)
1422 free(ClipText);
1424 Show();
1425 return TRUE;
1427 case KEY_SHIFTTAB:
1429 PrevCurPos=CurPos;
1430 CursorPos-=(CursorPos-1) % TabSize+1;
1432 if (CursorPos<0) CursorPos=0; //CursorPos=0,TabSize=1 case
1434 SetCellCurPos(CursorPos);
1435 Show();
1436 return TRUE;
1438 case KEY_SHIFTSPACE:
1439 Key = KEY_SPACE;
1440 default:
1442 // _D(SysLog(L"Key=0x%08X",Key));
1443 if (Key==KEY_ENTER || Key>=EXTENDED_KEY_BASE) // KEY_NUMENTER,KEY_IDLE,KEY_NONE covered by >=EXTENDED_KEY_BASE
1444 break;
1446 if (!Flags.Check(FEDITLINE_PERSISTENTBLOCKS))
1448 if (PrevSelStart!=-1)
1450 SelStart=PrevSelStart;
1451 SelEnd=PrevSelEnd;
1453 DisableCallback DC(m_Callback.Active);
1454 DeleteBlock();
1457 if (InsertKey(Key))
1458 Show();
1460 return TRUE;
1464 return FALSE;
1467 // обработка Ctrl-Q
1468 int Edit::ProcessCtrlQ()
1470 INPUT_RECORD rec;
1471 DWORD Key;
1473 for (;;)
1475 Key=GetInputRecord(&rec);
1477 if (Key!=KEY_NONE && Key!=KEY_IDLE && rec.Event.KeyEvent.uChar.AsciiChar)
1478 break;
1480 if (Key==KEY_CONSOLE_BUFFER_RESIZE)
1482 // int Dis=EditOutDisabled;
1483 // EditOutDisabled=0;
1484 Show();
1485 // EditOutDisabled=Dis;
1490 EditOutDisabled++;
1491 if (!Flags.Check(FEDITLINE_PERSISTENTBLOCKS))
1493 DeleteBlock();
1495 else
1496 Flags.Clear(FEDITLINE_CLEARFLAG);
1497 EditOutDisabled--;
1499 return InsertKey(rec.Event.KeyEvent.uChar.AsciiChar);
1502 int Edit::ProcessInsPlainText(const wchar_t *str)
1504 if (*str)
1506 InsertString(str);
1507 return TRUE;
1510 return FALSE;
1513 int Edit::InsertKey(int Key)
1515 bool changed=false;
1516 wchar_t *NewStr;
1518 if (Flags.Check(FEDITLINE_READONLY|FEDITLINE_DROPDOWNBOX))
1519 return (TRUE);
1521 if (Key==KEY_TAB && Flags.Check(FEDITLINE_OVERTYPE))
1523 PrevCurPos=CurPos;
1524 CursorPos+=TabSize - (CursorPos % TabSize);
1525 SetCellCurPos(CursorPos);
1526 return TRUE;
1529 if (Mask && *Mask)
1531 int MaskLen=StrLength(Mask);
1533 if (CurPos<MaskLen)
1535 if (KeyMatchedMask(Key))
1537 if (!Flags.Check(FEDITLINE_OVERTYPE))
1539 int i=MaskLen-1;
1541 while (!CheckCharMask(Mask[i]) && i>CurPos)
1542 i--;
1544 for (int j=i; i>CurPos; i--)
1546 if (CheckCharMask(Mask[i]))
1548 while (!CheckCharMask(Mask[j-1]))
1550 if (j<=CurPos)
1551 break;
1553 j--;
1556 Str[i]=Str[j-1];
1557 j--;
1562 PrevCurPos=CurPos;
1563 Str[CurPos++]=Key;
1564 changed=true;
1566 else
1568 // Здесь вариант для "ввели символ из маски", например для SetAttr - ввесли '.'
1569 ;// char *Ptr=strchr(Mask+CurPos,Key);
1572 else if (CurPos<StrSize)
1574 PrevCurPos=CurPos;
1575 Str[CurPos++]=Key;
1576 changed=true;
1579 else
1581 if (MaxLength == -1 || StrSize < MaxLength)
1583 if (CurPos>=StrSize)
1585 if (!(NewStr=(wchar_t *)realloc(Str,(CurPos+2)*sizeof(wchar_t))))
1586 return FALSE;
1588 Str=NewStr;
1589 swprintf(&Str[StrSize],CurPos+2,L"%*ls",CurPos-StrSize,L"");
1590 //memset(Str+StrSize,' ',CurPos-StrSize);Str[CurPos+1]=0;
1591 StrSize=CurPos+1;
1593 else if (!Flags.Check(FEDITLINE_OVERTYPE))
1594 StrSize++;
1596 if (Key==KEY_TAB && (TabExpandMode==EXPAND_NEWTABS || TabExpandMode==EXPAND_ALLTABS))
1598 StrSize--;
1599 InsertTab();
1600 return TRUE;
1603 if (!(NewStr=(wchar_t *)realloc(Str,(StrSize+1)*sizeof(wchar_t))))
1604 return TRUE;
1606 Str=NewStr;
1608 if (!Flags.Check(FEDITLINE_OVERTYPE))
1610 wmemmove(Str+CurPos+1,Str+CurPos,StrSize-CurPos);
1612 if (SelStart!=-1)
1614 if (SelEnd!=-1 && CurPos<SelEnd)
1615 SelEnd++;
1617 if (CurPos<SelStart)
1618 SelStart++;
1622 PrevCurPos=CurPos;
1623 Str[CurPos++]=Key;
1624 changed=true;
1626 else if (Flags.Check(FEDITLINE_OVERTYPE))
1628 if (CurPos < StrSize)
1630 PrevCurPos=CurPos;
1631 Str[CurPos++]=Key;
1632 changed=true;
1635 /*else
1636 MessageBeep(MB_ICONHAND);*/
1639 Str[StrSize]=0;
1641 if (changed) Changed();
1643 return TRUE;
1646 void Edit::SetObjectColor(int Color,int SelColor,int ColorUnChanged)
1648 this->Color=Color;
1649 this->SelColor=SelColor;
1650 this->ColorUnChanged=ColorUnChanged;
1654 void Edit::GetString(wchar_t *Str,int MaxSize)
1656 //far_wcsncpy(Str, this->Str,MaxSize);
1657 wmemmove(Str,this->Str,Min(StrSize,MaxSize-1));
1658 Str[Min(StrSize,MaxSize-1)]=0;
1659 Str[MaxSize-1]=0;
1662 void Edit::GetString(FARString &strStr)
1664 strStr = Str;
1668 const wchar_t* Edit::GetStringAddr()
1670 return Str;
1675 void Edit::SetHiString(const wchar_t *Str)
1677 if (Flags.Check(FEDITLINE_READONLY))
1678 return;
1680 FARString NewStr;
1681 HiText2Str(NewStr, Str);
1682 Select(-1,0);
1683 SetBinaryString(NewStr,StrLength(NewStr));
1686 void Edit::SetString(const wchar_t *Str, int Length)
1688 if (Flags.Check(FEDITLINE_READONLY))
1689 return;
1691 Select(-1,0);
1692 SetBinaryString(Str,Length==-1?(int)StrLength(Str):Length);
1695 void Edit::SetEOL(const wchar_t *EOL)
1697 EndType=EOL_NONE;
1699 if (EOL && *EOL)
1701 if (EOL[0]==L'\r')
1702 if (EOL[1]==L'\n')
1703 EndType=EOL_CRLF;
1704 else if (EOL[1]==L'\r' && EOL[2]==L'\n')
1705 EndType=EOL_CRCRLF;
1706 else
1707 EndType=EOL_CR;
1708 else if (EOL[0]==L'\n')
1709 EndType=EOL_LF;
1713 const wchar_t *Edit::GetEOL()
1715 return EOL_TYPE_CHARS[EndType];
1718 /* $ 25.07.2000 tran
1719 примечание:
1720 в этом методе DropDownBox не обрабатывается
1721 ибо он вызывается только из SetString и из класса Editor
1722 в Dialog он нигде не вызывается */
1723 void Edit::SetBinaryString(const wchar_t *Str,int Length)
1725 if (Flags.Check(FEDITLINE_READONLY))
1726 return;
1728 // коррекция вставляемого размера, если определен MaxLength
1729 if (MaxLength != -1 && Length > MaxLength)
1731 Length=MaxLength; // ??
1734 if (Length>0 && !Flags.Check(FEDITLINE_PARENT_SINGLELINE))
1736 if (Str[Length-1]==L'\r')
1738 EndType=EOL_CR;
1739 Length--;
1741 else
1743 if (Str[Length-1]==L'\n')
1745 Length--;
1747 if (Length > 0 && Str[Length-1]==L'\r')
1749 Length--;
1751 if (Length > 0 && Str[Length-1]==L'\r')
1753 Length--;
1754 EndType=EOL_CRCRLF;
1756 else
1757 EndType=EOL_CRLF;
1759 else
1760 EndType=EOL_LF;
1762 else
1763 EndType=EOL_NONE;
1767 CurPos=0;
1769 if (Mask && *Mask)
1771 RefreshStrByMask(TRUE);
1772 int maskLen=StrLength(Mask);
1774 for (int i=0,j=0; j<maskLen && j<Length;)
1776 if (CheckCharMask(Mask[i]))
1778 int goLoop=FALSE;
1780 if (KeyMatchedMask(Str[j]))
1781 InsertKey(Str[j]);
1782 else
1783 goLoop=TRUE;
1785 j++;
1787 if (goLoop) continue;
1789 else
1791 PrevCurPos=CurPos;
1792 CurPos++;
1795 i++;
1798 /* Здесь необходимо условие (!*Str), т.к. для очистки строки
1799 обычно вводится нечто вроде SetBinaryString("",0)
1800 Т.е. таким образом мы добиваемся "инициализации" строки с маской
1802 RefreshStrByMask(!*Str);
1804 else
1806 wchar_t *NewStr=(wchar_t *)malloc((Length+1)*sizeof(wchar_t));
1808 if (!NewStr)
1809 return;
1811 free(this->Str);
1813 this->Str=NewStr;
1814 StrSize=Length;
1815 wmemcpy(this->Str,Str,Length);
1816 this->Str[Length]=0;
1818 if (TabExpandMode == EXPAND_ALLTABS)
1819 ExpandTabs();
1821 PrevCurPos=CurPos;
1822 CurPos=StrSize;
1825 Changed();
1828 void Edit::GetBinaryString(const wchar_t **Str,const wchar_t **EOL,int &Length)
1830 *Str=this->Str;
1832 if (EOL)
1833 *EOL=EOL_TYPE_CHARS[EndType];
1835 Length=StrSize; //???
1838 int Edit::GetSelString(wchar_t *Str, int MaxSize)
1840 if (SelStart==-1 || (SelEnd!=-1 && SelEnd<=SelStart) ||
1841 SelStart>=StrSize)
1843 *Str=0;
1844 return FALSE;
1847 int CopyLength;
1849 if (SelEnd==-1)
1850 CopyLength=MaxSize;
1851 else
1852 CopyLength=Min(MaxSize,SelEnd-SelStart+1);
1854 far_wcsncpy(Str,this->Str+SelStart,CopyLength);
1855 return TRUE;
1858 int Edit::GetSelString(FARString &strStr)
1860 if (SelStart==-1 || (SelEnd!=-1 && SelEnd<=SelStart) ||
1861 SelStart>=StrSize)
1863 strStr.Clear();
1864 return FALSE;
1867 int CopyLength;
1868 CopyLength=SelEnd-SelStart+1;
1869 wchar_t *lpwszStr = strStr.GetBuffer(CopyLength+1);
1870 far_wcsncpy(lpwszStr,this->Str+SelStart,CopyLength);
1871 strStr.ReleaseBuffer();
1872 return TRUE;
1877 void Edit::InsertString(const wchar_t *Str)
1879 if (Flags.Check(FEDITLINE_READONLY|FEDITLINE_DROPDOWNBOX))
1880 return;
1882 if (!Flags.Check(FEDITLINE_PERSISTENTBLOCKS))
1883 DeleteBlock();
1885 InsertBinaryString(Str,StrLength(Str));
1889 void Edit::InsertBinaryString(const wchar_t *Str,int Length)
1891 wchar_t *NewStr;
1893 if (Flags.Check(FEDITLINE_READONLY|FEDITLINE_DROPDOWNBOX))
1894 return;
1896 Flags.Clear(FEDITLINE_CLEARFLAG);
1898 if (Mask && *Mask)
1900 int Pos=CurPos;
1901 int MaskLen=StrLength(Mask);
1903 if (Pos<MaskLen)
1905 //_SVS(SysLog(L"InsertBinaryString ==> Str='%ls' (Length=%d) Mask='%ls'",Str,Length,Mask+Pos));
1906 int StrLen=(MaskLen-Pos>Length)?Length:MaskLen-Pos;
1908 /* $ 15.11.2000 KM
1909 Внесены исправления для правильной работы PasteFromClipboard
1910 в строке с маской
1912 for (int i=Pos,j=0; j<StrLen+Pos;)
1914 if (CheckCharMask(Mask[i]))
1916 int goLoop=FALSE;
1918 if (j < Length && KeyMatchedMask(Str[j]))
1920 InsertKey(Str[j]);
1921 //_SVS(SysLog(L"InsertBinaryString ==> InsertKey(Str[%d]='%c');",j,Str[j]));
1923 else
1924 goLoop=TRUE;
1926 j++;
1928 if (goLoop) continue;
1930 else
1932 if(Mask[j] == Str[j])
1934 j++;
1936 PrevCurPos=CurPos;
1937 CurPos++;
1940 i++;
1944 RefreshStrByMask();
1945 //_SVS(SysLog(L"InsertBinaryString ==> this->Str='%ls'",this->Str));
1947 else
1949 if (MaxLength != -1 && StrSize+Length > MaxLength)
1951 // коррекция вставляемого размера, если определен MaxLength
1952 if (StrSize < MaxLength)
1954 Length=MaxLength-StrSize;
1958 if (MaxLength == -1 || StrSize+Length <= MaxLength)
1960 if (CurPos>StrSize)
1962 if (!(NewStr=(wchar_t *)realloc(this->Str,(CurPos+1)*sizeof(wchar_t))))
1963 return;
1965 this->Str=NewStr;
1966 swprintf(&this->Str[StrSize],CurPos+1,L"%*ls",CurPos-StrSize,L"");
1967 //memset(this->Str+StrSize,' ',CurPos-StrSize);this->Str[CurPos+1]=0;
1968 StrSize=CurPos;
1971 int TmpSize=StrSize-CurPos;
1972 wchar_t *TmpStr=new(std::nothrow) wchar_t[TmpSize+16];
1974 if (!TmpStr)
1975 return;
1977 wmemcpy(TmpStr,&this->Str[CurPos],TmpSize);
1978 StrSize+=Length;
1980 if (!(NewStr=(wchar_t *)realloc(this->Str,(StrSize+1)*sizeof(wchar_t))))
1982 delete[] TmpStr;
1983 return;
1986 this->Str=NewStr;
1987 wmemcpy(&this->Str[CurPos],Str,Length);
1988 PrevCurPos=CurPos;
1989 CurPos+=Length;
1990 wmemcpy(this->Str+CurPos,TmpStr,TmpSize);
1991 this->Str[StrSize]=0;
1992 delete[] TmpStr;
1994 if (TabExpandMode == EXPAND_ALLTABS)
1995 ExpandTabs();
1997 Changed();
1999 /*else
2000 MessageBeep(MB_ICONHAND);*/
2005 int Edit::GetLength()
2007 return(StrSize);
2011 // Функция установки маски ввода в объект Edit
2012 void Edit::SetInputMask(const wchar_t *InputMask)
2014 if (Mask)
2015 free(Mask);
2017 if (InputMask && *InputMask)
2019 if (!(Mask=wcsdup(InputMask)))
2020 return;
2022 RefreshStrByMask(TRUE);
2024 else
2025 Mask=nullptr;
2029 // Функция обновления состояния строки ввода по содержимому Mask
2030 void Edit::RefreshStrByMask(int InitMode)
2032 if (Mask && *Mask)
2034 int MaskLen=StrLength(Mask);
2036 if (StrSize!=MaskLen)
2038 wchar_t *NewStr=(wchar_t *)realloc(Str,(MaskLen+1)*sizeof(wchar_t));
2040 if (!NewStr)
2041 return;
2043 Str=NewStr;
2045 for (int i=StrSize; i<MaskLen; i++)
2046 Str[i]=L' ';
2048 StrSize=MaxLength=MaskLen;
2049 Str[StrSize]=0;
2052 for (int i=0; i<MaskLen; i++)
2054 if (InitMode)
2055 Str[i]=L' ';
2057 if (!CheckCharMask(Mask[i]))
2058 Str[i]=Mask[i];
2064 int Edit::ProcessMouse(MOUSE_EVENT_RECORD *MouseEvent)
2066 if (!(MouseEvent->dwButtonState & 3))
2067 return FALSE;
2069 if (MouseEvent->dwMousePosition.X<X1 || MouseEvent->dwMousePosition.X>X2 ||
2070 MouseEvent->dwMousePosition.Y!=Y1)
2071 return FALSE;
2073 //SetClearFlag(0); // пусть едитор сам заботится о снятии клеар-текста?
2074 SetCellCurPos(MouseEvent->dwMousePosition.X - X1 + LeftPos);
2076 if (!Flags.Check(FEDITLINE_PERSISTENTBLOCKS))
2077 Select(-1,0);
2079 if (MouseEvent->dwButtonState&FROM_LEFT_1ST_BUTTON_PRESSED)
2081 static int PrevDoubleClick=0;
2082 static COORD PrevPosition={0,0};
2084 if (WINPORT(GetTickCount)()-PrevDoubleClick<=WINPORT(GetDoubleClickTime)() && MouseEvent->dwEventFlags!=MOUSE_MOVED &&
2085 PrevPosition.X == MouseEvent->dwMousePosition.X && PrevPosition.Y == MouseEvent->dwMousePosition.Y)
2087 Select(0,StrSize);
2088 PrevDoubleClick=0;
2089 PrevPosition.X=0;
2090 PrevPosition.Y=0;
2093 if (MouseEvent->dwEventFlags==DOUBLE_CLICK)
2095 ProcessKey(KEY_OP_SELWORD);
2096 PrevDoubleClick=WINPORT(GetTickCount)();
2097 PrevPosition=MouseEvent->dwMousePosition;
2099 else
2101 PrevDoubleClick=0;
2102 PrevPosition.X=0;
2103 PrevPosition.Y=0;
2107 Show();
2108 return TRUE;
2112 /* $ 03.08.2000 KM
2113 Немного изменён алгоритм из-за необходимости
2114 добавления поиска целых слов.
2116 int Edit::Search(const FARString& Str,FARString& ReplaceStr,int Position,int Case,int WholeWords,int Reverse,int Regexp, int *SearchLength)
2118 return SearchString(this->Str,this->StrSize,Str,ReplaceStr,CurPos,Position,Case,WholeWords,Reverse,Regexp,SearchLength,WordDiv());
2121 void Edit::InsertTab()
2123 wchar_t *TabPtr;
2124 int Pos,S;
2126 if (Flags.Check(FEDITLINE_READONLY))
2127 return;
2129 Pos=CurPos;
2130 S=TabSize-(Pos % TabSize);
2132 if (SelStart!=-1)
2134 if (Pos<=SelStart)
2136 SelStart+=S-(Pos==SelStart?0:1);
2139 if (SelEnd!=-1 && Pos<SelEnd)
2141 SelEnd+=S;
2145 int PrevStrSize=StrSize;
2146 StrSize+=S;
2147 CurPos+=S;
2148 Str=(wchar_t *)realloc(Str,(StrSize+1)*sizeof(wchar_t));
2149 TabPtr=Str+Pos;
2150 wmemmove(TabPtr+S,TabPtr,PrevStrSize-Pos);
2151 wmemset(TabPtr,L' ',S);
2152 Str[StrSize]=0;
2153 Changed();
2157 void Edit::ExpandTabs()
2159 wchar_t *TabPtr;
2160 int Pos=0,S;
2162 if (Flags.Check(FEDITLINE_READONLY))
2163 return;
2165 bool changed=false;
2167 while ((TabPtr=(wchar_t *)wmemchr(Str+Pos,L'\t',StrSize-Pos)))
2169 changed=true;
2170 Pos=(int)(TabPtr-Str);
2171 S=TabSize-((int)(TabPtr-Str) % TabSize);
2173 if (SelStart!=-1)
2175 if (Pos<=SelStart)
2177 SelStart+=S-(Pos==SelStart?0:1);
2180 if (SelEnd!=-1 && Pos<SelEnd)
2182 SelEnd+=S-1;
2186 int PrevStrSize=StrSize;
2187 StrSize+=S-1;
2189 if (CurPos>Pos)
2190 CurPos+=S-1;
2192 Str=(wchar_t *)realloc(Str,(StrSize+1)*sizeof(wchar_t));
2193 TabPtr=Str+Pos;
2194 wmemmove(TabPtr+S,TabPtr+1,PrevStrSize-Pos);
2195 wmemset(TabPtr,L' ',S);
2196 Str[StrSize]=0;
2199 if (changed) Changed();
2203 int Edit::GetCellCurPos()
2205 return(RealPosToCell(CurPos));
2209 void Edit::SetCellCurPos(int NewPos)
2211 if (Mask && *Mask)
2213 int NewPosLimit = CalcRTrimmedStrSize();
2214 if (NewPos > NewPosLimit)
2215 NewPos = NewPosLimit;
2218 CurPos = CellPosToReal(NewPos);
2222 int Edit::RealPosToCell(int Pos)
2224 return RealPosToCell(0, 0, Pos, nullptr);
2228 int Edit::RealPosToCell(int PrevLength, int PrevPos, int Pos, int* CorrectPos)
2230 // Корректировка табов
2231 bool bCorrectPos = CorrectPos && *CorrectPos;
2233 if (CorrectPos)
2234 *CorrectPos = 0;
2236 // Инциализируем результирующую длину предыдущим значением
2237 int TabPos = PrevLength;
2239 // Если предыдущая позиция за концом строки, то табов там точно нет и
2240 // вычислять особо ничего не надо, иначе производим вычисление
2241 if (PrevPos >= StrSize)
2242 TabPos += Pos-PrevPos;
2243 else
2245 // Начинаем вычисление с предыдущей позиции
2246 int Index = PrevPos;
2248 // Проходим по всем символам до позиции поиска, если она ещё в пределах строки,
2249 // либо до конца строки, если позиция поиска за пределами строки
2250 for (; Index < Min(Pos, StrSize); Index++)
2252 // Обрабатываем табы
2253 if (Str[Index] == L'\t' && TabExpandMode != EXPAND_ALLTABS)
2255 // Если есть необходимость делать корректировку табов и эта коректировка
2256 // ещё не проводилась, то увеличиваем длину обрабатываемой строки на еденицу
2257 if (bCorrectPos)
2259 ++Pos;
2260 *CorrectPos = 1;
2261 bCorrectPos = false;
2264 // Расчитываем длину таба с учётом настроек и текущей позиции в строке
2265 TabPos += TabSize-(TabPos%TabSize);
2267 // Обрабатываем все остальные символы
2268 else if (IsCharFullWidth(Str[Index]))
2270 TabPos+= 2;
2272 else if (!IsCharXxxfix(Str[Index]))
2274 TabPos++;
2277 // Если позиция находится за пределами строки, то там точно нет табов и всё просто
2278 if (Pos >= StrSize)
2279 TabPos += Pos-Index;
2282 return TabPos;
2286 int Edit::CellPosToReal(int Pos)
2288 int Index = 0;
2289 for (int CellPos = 0; CellPos < Pos; Index++)
2291 if (Index >= StrSize)
2293 Index += Pos-CellPos;
2294 break;
2297 if (Str[Index] == L'\t' && TabExpandMode != EXPAND_ALLTABS)
2299 int NewCellPos = CellPos + TabSize - (CellPos%TabSize);
2301 if (NewCellPos > Pos)
2302 break;
2304 CellPos = NewCellPos;
2306 else
2308 CellPos+= IsCharFullWidth(Str[Index]) ? 2 : 1;
2309 while (Index + 1 < StrSize && IsCharXxxfix(Str[Index + 1]))
2311 Index++;
2316 return Index;
2319 void Edit::SanitizeSelectionRange()
2321 if (SelEnd >= SelStart && SelStart >= 0)
2323 while (SelStart > 0 && IsCharXxxfix(Str[SelStart]))
2324 --SelStart;
2326 while (SelEnd < StrSize && IsCharXxxfix(Str[SelEnd]))
2327 ++SelEnd;
2330 /* $ 24.06.2002 SKV
2331 Если начало выделения за концом строки, надо выделение снять.
2332 17.09.2002 возвращаю обратно. Глюкодром.
2334 if (SelEnd < SelStart && SelEnd != -1)
2336 SelStart = -1;
2337 SelEnd = 0;
2340 if (SelStart == -1 && SelEnd == -1)
2342 SelStart = -1;
2343 SelEnd = 0;
2347 void Edit::Select(int Start,int End)
2349 SelStart = Start;
2350 SelEnd = End;
2352 SanitizeSelectionRange();
2355 void Edit::AddSelect(int Start,int End)
2357 if (Start < SelStart || SelStart == -1)
2358 SelStart = Start;
2360 if (End == -1 || (End > SelEnd && SelEnd != -1))
2361 SelEnd = End;
2363 if (SelEnd > StrSize)
2364 SelEnd = StrSize;
2366 SanitizeSelectionRange();
2370 void Edit::GetSelection(int &Start,int &End)
2372 /* $ 17.09.2002 SKV
2373 Мало того, что это нарушение правил OO design'а,
2374 так это еще и источние багов.
2376 /* if (SelEnd>StrSize+1)
2377 SelEnd=StrSize+1;
2378 if (SelStart>StrSize+1)
2379 SelStart=StrSize+1;*/
2380 /* SKV $ */
2381 Start=SelStart;
2382 End=SelEnd;
2384 if (End>StrSize)
2385 End=-1;//StrSize;
2387 if (Start>StrSize)
2388 Start=StrSize;
2392 void Edit::GetRealSelection(int &Start,int &End)
2394 Start=SelStart;
2395 End=SelEnd;
2399 void Edit::DeleteBlock()
2401 if (Flags.Check(FEDITLINE_READONLY|FEDITLINE_DROPDOWNBOX))
2402 return;
2404 if (SelStart==-1 || SelStart>=SelEnd)
2405 return;
2407 PrevCurPos=CurPos;
2409 if (Mask && *Mask)
2411 for (int i=SelStart; i<SelEnd; i++)
2413 if (CheckCharMask(Mask[i]))
2414 Str[i]=L' ';
2417 CurPos=SelStart;
2419 else
2421 int From=SelStart,To=SelEnd;
2423 if (From>StrSize)From=StrSize;
2425 if (To>StrSize)To=StrSize;
2427 wmemmove(Str+From,Str+To,StrSize-To+1);
2428 StrSize-=To-From;
2430 if (CurPos>From)
2432 if (CurPos<To)
2433 CurPos=From;
2434 else
2435 CurPos-=To-From;
2438 Str=(wchar_t *)realloc(Str,(StrSize+1)*sizeof(wchar_t));
2441 SelStart=-1;
2442 SelEnd=0;
2443 Flags.Clear(FEDITLINE_MARKINGBLOCK);
2445 // OT: Проверка на корректность поведени строки при удалении и вставки
2446 if (Flags.Check((FEDITLINE_PARENT_SINGLELINE|FEDITLINE_PARENT_MULTILINE)))
2448 if (LeftPos>CurPos)
2449 LeftPos=CurPos;
2452 Changed(true);
2456 void Edit::AddColor(ColorItem *col)
2458 if (!(ColorCount & 15))
2459 ColorList=(ColorItem *)realloc(ColorList,(ColorCount+16)*sizeof(*ColorList));
2461 ColorList[ColorCount++]=*col;
2465 int Edit::DeleteColor(int ColorPos)
2467 int Src;
2469 if (!ColorCount)
2470 return FALSE;
2472 int Dest=0;
2474 for (Src=0; Src<ColorCount; Src++)
2475 if (ColorPos!=-1 && ColorList[Src].StartPos!=ColorPos)
2477 if (Dest!=Src)
2478 ColorList[Dest]=ColorList[Src];
2480 Dest++;
2483 int DelCount=ColorCount-Dest;
2484 ColorCount=Dest;
2486 if (!ColorCount)
2488 free(ColorList);
2489 ColorList=nullptr;
2492 return(DelCount);
2496 int Edit::GetColor(ColorItem *col,int Item)
2498 if (Item >= ColorCount)
2499 return FALSE;
2501 *col=ColorList[Item];
2502 return TRUE;
2506 void Edit::ApplyColor()
2508 // Для оптимизации сохраняем вычисленные позиции между итерациями цикла
2509 int Pos = INT_MIN, TabPos = INT_MIN, TabEditorPos = INT_MIN;
2511 // Обрабатываем элементы ракраски
2512 for (int Col = 0; Col < ColorCount; Col++)
2514 ColorItem *CurItem = ColorList+Col;
2516 // Пропускаем элементы у которых начало больше конца
2517 if (CurItem->StartPos > CurItem->EndPos)
2518 continue;
2520 // Отсекаем элементы заведомо не попадающие на экран
2521 if (CurItem->StartPos-LeftPos > X2 && CurItem->EndPos-LeftPos < X1)
2522 continue;
2524 int Attr = CurItem->Color;
2525 int Length = CurItem->EndPos-CurItem->StartPos+1;
2527 if (CurItem->StartPos+Length >= StrSize)
2528 Length = StrSize-CurItem->StartPos;
2530 // Получаем начальную позицию
2531 int RealStart, Start;
2533 // Если предыдущая позиция равна текущей, то ничего не вычисляем
2534 // и сразу берём ранее вычисленное значение
2535 if (Pos == CurItem->StartPos)
2537 RealStart = TabPos;
2538 Start = TabEditorPos;
2540 // Если вычисление идёт первый раз или предыдущая позиция больше текущей,
2541 // то производим вычисление с начала строки
2542 else if (Pos == INT_MIN || CurItem->StartPos < Pos)
2544 RealStart = RealPosToCell(CurItem->StartPos);
2545 Start = RealStart-LeftPos;
2547 // Для отптимизации делаем вычисление относительно предыдущей позиции
2548 else
2550 RealStart = RealPosToCell(TabPos, Pos, CurItem->StartPos, nullptr);
2551 Start = RealStart-LeftPos;
2554 // Запоминаем вычисленные значения для их дальнейшего повторного использования
2555 Pos = CurItem->StartPos;
2556 TabPos = RealStart;
2557 TabEditorPos = Start;
2559 // Пропускаем элементы раскраски у которых начальная позиция за экраном
2560 if (Start > X2)
2561 continue;
2563 // Корректировка относительно табов (отключается, если присутвует флаг ECF_TAB1)
2564 int CorrectPos = Attr & ECF_TAB1 ? 0 : 1;
2566 if (!CorrectPos)
2567 Attr &= ~ECF_TAB1;
2569 // Получаем конечную позицию
2570 int EndPos = CurItem->EndPos;
2571 int RealEnd, End;
2573 // Обрабатываем случай, когда предыдущая позиция равна текущей, то есть
2574 // длина раскрашиваемой строкии равна 1
2575 if (Pos == EndPos)
2577 // Если необходимо делать корректироку относительно табов и единственный
2578 // символ строки -- это таб, то делаем расчёт с учтом корректировки,
2579 // иначе ничего не вычисялем и берём старые значения
2580 if (CorrectPos && EndPos < StrSize && Str[EndPos] == L'\t')
2582 RealEnd = RealPosToCell(TabPos, Pos, ++EndPos, nullptr);
2583 End = RealEnd-LeftPos;
2585 else
2587 RealEnd = TabPos;
2588 CorrectPos = 0;
2589 End = TabEditorPos;
2592 // Если предыдущая позиция больше текущей, то производим вычисление
2593 // с начала строки (с учётом корректировки относительно табов)
2594 else if (EndPos < Pos)
2596 RealEnd = RealPosToCell(0, 0, EndPos, &CorrectPos);
2597 EndPos += CorrectPos;
2598 End = RealEnd-LeftPos;
2600 // Для отптимизации делаем вычисление относительно предыдущей позиции (с учётом
2601 // корректировки относительно табов)
2602 else
2604 RealEnd = RealPosToCell(TabPos, Pos, EndPos, &CorrectPos);
2605 EndPos += CorrectPos;
2606 End = RealEnd-LeftPos;
2609 // Запоминаем вычисленные значения для их дальнейшего повторного использования
2610 Pos = EndPos;
2611 TabPos = RealEnd;
2612 TabEditorPos = End;
2614 // Пропускаем элементы раскраски у которых конечная позиция меньше левой границы экрана
2615 if (End < X1)
2616 continue;
2618 // Обрезаем раскраску элемента по экрану
2619 if (Start < X1)
2620 Start = X1;
2622 if (End > X2)
2623 End = X2;
2625 // Устанавливаем длину раскрашиваемого элемента
2626 Length = End-Start+1;
2628 if (Length < X2)
2629 Length -= CorrectPos;
2631 // Раскрашиваем элемент, если есть что раскрашивать
2632 if (Length > 0)
2634 ScrBuf.ApplyColor(
2635 Start,
2637 Start+Length-1,
2639 Attr,
2640 // Не раскрашиваем выделение
2641 SelColor >= COL_FIRSTPALETTECOLOR ? Palette[SelColor-COL_FIRSTPALETTECOLOR] : SelColor
2647 /* $ 24.09.2000 SVS $
2648 Функция Xlat - перекодировка по принципу QWERTY <-> ЙЦУКЕН
2650 void Edit::Xlat(bool All)
2652 // Для CmdLine - если нет выделения, преобразуем всю строку
2653 if (All && SelStart == -1 && !SelEnd)
2655 ::Xlat(Str,0,StrLength(Str),Opt.XLat.Flags);
2656 Changed();
2657 Show();
2658 return;
2661 if (SelStart != -1 && SelStart != SelEnd)
2663 if (SelEnd == -1)
2664 SelEnd=StrLength(Str);
2666 ::Xlat(Str,SelStart,SelEnd,Opt.XLat.Flags);
2667 Changed();
2668 Show();
2670 /* $ 25.11.2000 IS
2671 Если нет выделения, то обработаем текущее слово. Слово определяется на
2672 основе специальной группы разделителей.
2674 else
2676 /* $ 10.12.2000 IS
2677 Обрабатываем только то слово, на котором стоит курсор, или то слово, что
2678 находится левее позиции курсора на 1 символ
2680 int start=CurPos, end, StrSize=StrLength(Str);
2681 bool DoXlat=true;
2683 if (IsWordDiv(Opt.XLat.strWordDivForXlat,Str[start]))
2685 if (start) start--;
2687 DoXlat=(!IsWordDiv(Opt.XLat.strWordDivForXlat,Str[start]));
2690 if (DoXlat)
2692 while (start>=0 && !IsWordDiv(Opt.XLat.strWordDivForXlat,Str[start]))
2693 start--;
2695 start++;
2696 end=start+1;
2698 while (end<StrSize && !IsWordDiv(Opt.XLat.strWordDivForXlat,Str[end]))
2699 end++;
2701 ::Xlat(Str,start,end,Opt.XLat.Flags);
2702 Changed();
2703 Show();
2709 /* $ 15.11.2000 KM
2710 Проверяет: попадает ли символ в разрешённый
2711 диапазон символов, пропускаемых маской
2713 int Edit::KeyMatchedMask(int Key)
2715 int Inserted=FALSE;
2717 if (Mask[CurPos]==EDMASK_ANY)
2718 Inserted=TRUE;
2719 else if (Mask[CurPos] == EDMASK_DSS && (std::iswdigit(Key) || Key == L' ' || Key == L'-'))
2720 Inserted=TRUE;
2721 else if (Mask[CurPos] == EDMASK_DIGITS && (std::iswdigit(Key) || Key == L' '))
2722 Inserted=TRUE;
2723 else if (Mask[CurPos] == EDMASK_DIGIT && (std::iswdigit(Key)))
2724 Inserted=TRUE;
2725 else if (Mask[CurPos] == EDMASK_ALPHA && IsAlpha(Key))
2726 Inserted=TRUE;
2727 else if (Mask[CurPos] == EDMASK_HEX && std::iswxdigit(Key))
2728 Inserted=TRUE;
2730 return Inserted;
2733 int Edit::CheckCharMask(wchar_t Chr)
2735 return (Chr==EDMASK_ANY || Chr==EDMASK_DIGIT || Chr==EDMASK_DIGITS || Chr==EDMASK_DSS || Chr==EDMASK_ALPHA || Chr==EDMASK_HEX)?TRUE:FALSE;
2738 void Edit::SetDialogParent(DWORD Sets)
2740 if ((Sets&(FEDITLINE_PARENT_SINGLELINE|FEDITLINE_PARENT_MULTILINE)) == (FEDITLINE_PARENT_SINGLELINE|FEDITLINE_PARENT_MULTILINE) ||
2741 !(Sets&(FEDITLINE_PARENT_SINGLELINE|FEDITLINE_PARENT_MULTILINE)))
2742 Flags.Clear(FEDITLINE_PARENT_SINGLELINE|FEDITLINE_PARENT_MULTILINE);
2743 else if (Sets&FEDITLINE_PARENT_SINGLELINE)
2745 Flags.Clear(FEDITLINE_PARENT_MULTILINE);
2746 Flags.Set(FEDITLINE_PARENT_SINGLELINE);
2748 else if (Sets&FEDITLINE_PARENT_MULTILINE)
2750 Flags.Clear(FEDITLINE_PARENT_SINGLELINE);
2751 Flags.Set(FEDITLINE_PARENT_MULTILINE);
2755 void Edit::Changed(bool DelBlock)
2757 if(m_Callback.Active && m_Callback.m_Callback)
2759 m_Callback.m_Callback(m_Callback.m_Param);
2764 SystemCPEncoder::SystemCPEncoder(int nCodePage)
2766 m_nCodePage = nCodePage;
2767 m_nRefCount = 1;
2768 m_strName.Format(L"codepage - %d", m_nCodePage);
2771 SystemCPEncoder::~SystemCPEncoder()
2775 int __stdcall SystemCPEncoder::AddRef()
2777 return ++m_nRefCount;
2780 int __stdcall SystemCPEncoder::Release()
2782 if (!(--m_nRefCount))
2784 delete this;
2785 return 0;
2788 return m_nRefCount;
2791 const wchar_t* __stdcall SystemCPEncoder::GetName()
2793 return (const wchar_t*)m_strName;
2796 int __stdcall SystemCPEncoder::Encode(
2797 const char *lpString,
2798 int nLength,
2799 wchar_t *lpwszResult,
2800 int nResultLength
2803 int length = MultiByteToWideChar(m_nCodePage, 0, lpString, nLength, nullptr, 0);
2805 if (lpwszResult)
2806 length = MultiByteToWideChar(m_nCodePage, 0, lpString, nLength, lpwszResult, nResultLength);
2808 return length;
2811 int __stdcall SystemCPEncoder::Decode(
2812 const wchar_t *lpwszString,
2813 int nLength,
2814 char *lpResult,
2815 int nResultLength
2818 int length = WideCharToMultiByte(m_nCodePage, 0, lpwszString, nLength, nullptr, 0, nullptr, nullptr);
2820 if (lpResult)
2821 length = WideCharToMultiByte(m_nCodePage, 0, lpwszString, nLength, lpResult, nResultLength, nullptr, nullptr);
2823 return length;
2826 int __stdcall SystemCPEncoder::Transcode(
2827 const wchar_t *lpwszString,
2828 int nLength,
2829 ICPEncoder *pFrom,
2830 wchar_t *lpwszResult,
2831 int nResultLength
2834 int length = pFrom->Decode(lpwszString, nLength, nullptr, 0);
2835 char *lpDecoded = (char *)malloc(length);
2837 if (lpDecoded)
2839 pFrom->Decode(lpwszString, nLength, lpDecoded, length);
2840 length = Encode(lpDecoded, length, nullptr, 0);
2842 if (lpwszResult)
2843 length = Encode(lpDecoded, length, lpwszResult, nResultLength);
2845 free(lpDecoded);
2846 return length;
2849 return -1;
2853 EditControl::EditControl(ScreenObject *pOwner,Callback* aCallback,bool bAllocateData,History* iHistory,FarList* iList,DWORD iFlags)
2854 : Edit(pOwner,aCallback,bAllocateData),
2855 pCustomCompletionList(nullptr), pHistory(iHistory), pList(iList),
2856 Selection(false), SelectionStart(-1), ECFlags(iFlags)
2859 ACState=ECFlags.Check(EC_ENABLEAUTOCOMPLETE)!=FALSE;
2862 void EditControl::Show()
2864 if(X2-X1+1>StrSize)
2866 SetLeftPos(0);
2868 Edit::Show();
2871 void EditControl::Changed(bool DelBlock)
2873 if(m_Callback.Active)
2875 Edit::Changed();
2876 AutoComplete(false,DelBlock);
2880 void EditControl::SetMenuPos(VMenu& menu)
2882 if(ScrY-Y1<Min(Opt.Dialogs.CBoxMaxHeight,menu.GetItemCount())+2 && Y1>ScrY/2)
2884 menu.SetPosition(X1,Max(0,Y1-1-Min(Opt.Dialogs.CBoxMaxHeight,menu.GetItemCount())-1),Min(ScrX-2,X2),Y1-1);
2886 else
2888 menu.SetPosition(X1,Y1+1,X2,0);
2893 static void FilteredAddToMenu(VMenu &menu, const FARString &filter, const FARString &text)
2895 if (!StrCmpNI(text, filter, static_cast<int>(filter.GetLength())) && StrCmp(text, filter)) {
2896 menu.AddItem(text);
2900 void EditControl::PopulateCompletionMenu(VMenu &ComplMenu, const FARString &strFilter)
2902 SudoSilentQueryRegion ssqr;
2903 if (pCustomCompletionList)
2905 for (const auto &possibility : *pCustomCompletionList)
2906 FilteredAddToMenu(ComplMenu, strFilter, FARString(possibility));
2908 if (ComplMenu.GetItemCount() < 10)
2909 ComplMenu.AssignHighlights(0);
2911 else
2913 if(pHistory)
2915 pHistory->GetAllSimilar(ComplMenu, strFilter);
2917 else if(pList)
2919 for(int i=0;i<pList->ItemsNumber;i++)
2920 FilteredAddToMenu(ComplMenu, strFilter, pList->Items[i].Text);
2922 if(ECFlags.Check(EC_ENABLEFNCOMPLETE))
2924 if (!m_pSuggestor)
2925 m_pSuggestor.reset(new MenuFilesSuggestor);
2927 m_pSuggestor->Suggest(strFilter, ComplMenu);
2932 void EditControl::RemoveSelectedCompletionMenuItem(VMenu &ComplMenu)
2934 int CurPos = ComplMenu.GetSelectPos();
2935 if (CurPos >= 0 && !pCustomCompletionList && pHistory)
2937 FARString strName = ComplMenu.GetItemPtr(CurPos)->strName;
2938 if (pHistory->DeleteMatching(strName))
2940 ComplMenu.DeleteItem(CurPos, 1);
2941 ComplMenu.FastShow();
2946 void EditControl::AutoCompleteProcMenu(int &Result,bool Manual,bool DelBlock,int& BackKey)
2948 VMenu ComplMenu(nullptr,nullptr,0,0);
2949 FARString strTemp = Str;
2950 PopulateCompletionMenu(ComplMenu, strTemp);
2951 ComplMenu.SetBottomTitle(((!pCustomCompletionList && pHistory)
2952 ? Msg::EditControlHistoryFooter : Msg::EditControlHistoryFooterNoDel));
2954 if(ComplMenu.GetItemCount()>1 || (ComplMenu.GetItemCount()==1 && StrCmpI(strTemp,ComplMenu.GetItemPtr(0)->strName)))
2956 ComplMenu.SetFlags(VMENU_WRAPMODE|VMENU_NOTCENTER|VMENU_SHOWAMPERSAND);
2958 if(!DelBlock && Opt.AutoComplete.AppendCompletion && (!Flags.Check(FEDITLINE_PERSISTENTBLOCKS) || Opt.AutoComplete.ShowList))
2960 int SelStart=GetLength();
2962 // magic
2963 if(IsSlash(Str[SelStart-1]) && Str[SelStart-2] == L'"' && IsSlash(ComplMenu.GetItemPtr(0)->strName.At(SelStart-2)))
2965 Str[SelStart-2] = Str[SelStart-1];
2966 StrSize--;
2967 SelStart--;
2968 CurPos--;
2971 InsertString(ComplMenu.GetItemPtr(0)->strName.SubStr(SelStart));
2972 Select(SelStart, GetLength());
2973 Show();
2975 if(Opt.AutoComplete.ShowList)
2977 ChangeMacroMode MacroMode(MACRO_AUTOCOMPLETION);
2978 MenuItemEx EmptyItem;
2979 ComplMenu.AddItem(&EmptyItem,0);
2980 SetMenuPos(ComplMenu);
2981 ComplMenu.SetSelectPos(0,0);
2982 ComplMenu.SetBoxType(SHORT_SINGLE_BOX);
2983 ComplMenu.ClearDone();
2984 ComplMenu.Show();
2985 Show();
2986 int PrevPos=0;
2988 while (!ComplMenu.Done())
2990 INPUT_RECORD ir;
2991 ComplMenu.ReadInput(&ir);
2992 if(!Opt.AutoComplete.ModalList)
2994 int CurPos=ComplMenu.GetSelectPos();
2995 if(CurPos>=0 && PrevPos!=CurPos)
2997 PrevPos=CurPos;
2998 SetString(CurPos?ComplMenu.GetItemPtr(CurPos)->strName:strTemp);
2999 Show();
3002 if(ir.EventType==WINDOW_BUFFER_SIZE_EVENT)
3004 SetMenuPos(ComplMenu);
3005 ComplMenu.Show();
3007 else if(ir.EventType==KEY_EVENT || ir.EventType==FARMACRO_KEY_EVENT)
3009 int MenuKey=InputRecordToKey(&ir);
3011 // ввод
3012 if((MenuKey>=int(L' ') && MenuKey<=MAX_VKEY_CODE) || MenuKey==KEY_BS || MenuKey==KEY_DEL || MenuKey==KEY_NUMDEL)
3014 FARString strPrev;
3015 GetString(strPrev);
3016 DeleteBlock();
3017 ProcessKey(MenuKey);
3018 GetString(strTemp);
3019 if(StrCmp(strPrev,strTemp))
3021 ComplMenu.DeleteItems();
3022 PrevPos=0;
3023 if(!strTemp.IsEmpty())
3025 PopulateCompletionMenu(ComplMenu, strTemp);
3027 if(ComplMenu.GetItemCount()>1 || (ComplMenu.GetItemCount()==1 && StrCmpI(strTemp,ComplMenu.GetItemPtr(0)->strName)))
3029 if(MenuKey!=KEY_BS && MenuKey!=KEY_DEL && MenuKey!=KEY_NUMDEL && Opt.AutoComplete.AppendCompletion)
3031 int SelStart=GetLength();
3033 // magic
3034 if(IsSlash(Str[SelStart-1]) && Str[SelStart-2] == L'"' && IsSlash(ComplMenu.GetItemPtr(0)->strName.At(SelStart-2)))
3036 Str[SelStart-2] = Str[SelStart-1];
3037 StrSize--;
3038 SelStart--;
3039 CurPos--;
3042 DisableCallback DC(m_Callback.Active);
3043 InsertString(ComplMenu.GetItemPtr(0)->strName.SubStr(SelStart));
3044 if(X2-X1>GetLength())
3045 SetLeftPos(0);
3046 Select(SelStart, GetLength());
3048 ComplMenu.AddItem(&EmptyItem,0);
3049 SetMenuPos(ComplMenu);
3050 ComplMenu.SetSelectPos(0,0);
3051 ComplMenu.Redraw();
3053 else
3055 ComplMenu.SetExitCode(-1);
3057 Show();
3060 else
3062 switch(MenuKey)
3064 // "классический" перебор
3065 case KEY_CTRLEND:
3067 ComplMenu.ProcessKey(KEY_DOWN);
3068 break;
3071 // навигация по строке ввода
3072 case KEY_LEFT:
3073 case KEY_NUMPAD4:
3074 case KEY_CTRLS:
3075 case KEY_RIGHT:
3076 case KEY_NUMPAD6:
3077 case KEY_CTRLD:
3078 case KEY_CTRLLEFT:
3079 case KEY_CTRLRIGHT:
3080 case KEY_CTRLHOME:
3082 if(MenuKey == KEY_LEFT || MenuKey == KEY_NUMPAD4)
3084 MenuKey = KEY_CTRLS;
3086 else if(MenuKey == KEY_RIGHT || MenuKey == KEY_NUMPAD6)
3088 MenuKey = KEY_CTRLD;
3090 pOwner->ProcessKey(MenuKey);
3091 break;
3094 // навигация по списку
3095 case KEY_HOME:
3096 case KEY_NUMPAD7:
3097 case KEY_END:
3098 case KEY_NUMPAD1:
3099 case KEY_IDLE:
3100 case KEY_NONE:
3101 case KEY_ESC:
3102 case KEY_F10:
3103 case KEY_ALTF9:
3104 case KEY_UP:
3105 case KEY_NUMPAD8:
3106 case KEY_DOWN:
3107 case KEY_NUMPAD2:
3108 case KEY_PGUP:
3109 case KEY_NUMPAD9:
3110 case KEY_PGDN:
3111 case KEY_NUMPAD3:
3112 case KEY_ALTLEFT:
3113 case KEY_ALTRIGHT:
3114 case KEY_ALTHOME:
3115 case KEY_ALTEND:
3116 case KEY_MSWHEEL_UP:
3117 case KEY_MSWHEEL_DOWN:
3118 case KEY_MSWHEEL_LEFT:
3119 case KEY_MSWHEEL_RIGHT:
3121 ComplMenu.ProcessInput();
3122 break;
3125 case KEY_SHIFTNUMDEL:
3126 case KEY_SHIFTDEL:
3128 RemoveSelectedCompletionMenuItem(ComplMenu);
3129 break;
3132 case KEY_ENTER:
3133 case KEY_NUMENTER:
3135 if(Opt.AutoComplete.ModalList)
3137 ComplMenu.ProcessInput();
3138 break;
3142 // всё остальное закрывает список и идёт владельцу
3143 default:
3145 ComplMenu.Hide();
3146 ComplMenu.SetExitCode(-1);
3147 BackKey=MenuKey;
3148 Result=1;
3153 else
3155 ComplMenu.ProcessInput();
3158 if(Opt.AutoComplete.ModalList)
3160 int ExitCode=ComplMenu.GetExitCode();
3161 if(ExitCode>0)
3163 SetString(ComplMenu.GetItemPtr(ExitCode)->strName);
3170 int EditControl::AutoCompleteProc(bool Manual,bool DelBlock,int& BackKey)
3172 int Result=0;
3173 static int Reenter=0;
3175 if(ECFlags.Check(EC_ENABLEAUTOCOMPLETE) && *Str && !Reenter && (CtrlObject->Macro.GetCurRecord(nullptr,nullptr) == MACROMODE_NOMACRO || Manual))
3177 Reenter++;
3178 AutoCompleteProcMenu(Result,Manual,DelBlock,BackKey);
3179 Reenter--;
3181 return Result;
3184 void EditControl::AutoComplete(bool Manual,bool DelBlock)
3186 int Key=0;
3187 if(AutoCompleteProc(Manual,DelBlock,Key))
3189 // BUGBUG, hack
3190 int Wait=WaitInMainLoop;
3191 WaitInMainLoop=1;
3192 if(!CtrlObject->Macro.ProcessKey(Key))
3193 pOwner->ProcessKey(Key);
3194 WaitInMainLoop=Wait;
3195 Show();
3199 int EditControl::ProcessMouse(MOUSE_EVENT_RECORD *MouseEvent)
3201 if(Edit::ProcessMouse(MouseEvent))
3203 while(IsMouseButtonPressed()==FROM_LEFT_1ST_BUTTON_PRESSED)
3205 Flags.Clear(FEDITLINE_CLEARFLAG);
3206 SetCellCurPos(MouseX - X1 + LeftPos);
3207 if(MouseEventFlags&MOUSE_MOVED)
3209 if(!Selection)
3211 Selection=true;
3212 SelectionStart=-1;
3213 Select(SelectionStart,0);
3215 else
3217 if(SelectionStart==-1)
3219 SelectionStart=CurPos;
3221 Select(Min(SelectionStart,CurPos),Min(StrSize,Max(SelectionStart,CurPos)));
3222 Show();
3226 Selection=false;
3227 return TRUE;
3229 return FALSE;
3232 void EditControl::EnableAC(bool Permanent)
3234 ACState=Permanent?true:ECFlags.Check(EC_ENABLEAUTOCOMPLETE)!=FALSE;
3235 ECFlags.Set(EC_ENABLEAUTOCOMPLETE);
3238 void EditControl::DisableAC(bool Permanent)
3240 ACState=Permanent?false:ECFlags.Check(EC_ENABLEAUTOCOMPLETE)!=FALSE;
3241 ECFlags.Clear(EC_ENABLEAUTOCOMPLETE);
3244 void EditControl::ShowCustomCompletionList(const std::vector<std::string> &list)
3246 pCustomCompletionList = &list;
3247 AutoComplete(true, false);
3248 pCustomCompletionList = nullptr;