1 /*
2   FXiTe - The Free eXtensIble Text Editor
3   Copyright (c) 2009-2013 Jeffrey Pohlmeyer <yetanothergeek@gmail.com>
4 
5   This program is free software; you can redistribute it and/or modify it
6   under the terms of the GNU General Public License version 3 as
7   published by the Free Software Foundation.
8 
9   This software is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   GNU General Public License for more details.
13 
14   You should have received a copy of the GNU General Public License
15   along with this program; if not, write to the Free Software
16   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17 */
18 
19 
20 #include <fx.h>
21 #include <Scintilla.h>
22 
23 #include "compat.h"
24 #include "macro.h"
25 
26 #include "intl.h"
27 #include "recorder.h"
28 
29 
30 #define _LUAMOD_ LUA_MODULE_NAME
31 
32 struct MacroMessage {
33     int message;
34     uptr_t wParam;
35     sptr_t lParam;
36     unsigned char allocd;
37 };
38 
MacroRecorder()39 MacroRecorder::MacroRecorder():FXObject() {
40 
41 }
42 
43 
44 
45 #define NewMessage() \
46 MacroMessage* mm=new MacroMessage; \
47 mm->message=message; \
48 mm->wParam=wParam; \
49 mm->lParam=lParam; \
50 mm->allocd=0; \
51 list.prepend((FXObject*)mm);
52 
53 
54 
55 #define AddString() { \
56 NewMessage(); \
57 mm->lParam=reinterpret_cast<long>(strdup((char*)lParam)); \
58 mm->allocd=1; \
59 }
60 
61 
62 
63 #define AppendString() { \
64   char *s=(char*)calloc(strlen((char*)prev->lParam)+strlen((char*)lParam)+1,1); \
65   strcpy(s,(char*)prev->lParam); \
66   strcat(s,(char*)lParam); \
67   free((void*)prev->lParam); \
68   prev->lParam=reinterpret_cast<long>(s); \
69 }
70 
71 
record(int message,uptr_t wParam,sptr_t lParam)72 void MacroRecorder::record(int message, uptr_t wParam, sptr_t lParam)
73 {
74 FXString msgstring;
75   MacroMessage*prev=list[0];
76   switch (message) {
77     case SCI_CUT:
78     case SCI_COPY:
79     case SCI_CLEARALL:
80     case SCI_SELECTALL:
81     case SCI_HOME:
82     case SCI_HOMEEXTEND:
83     case SCI_LINEEND:
84     case SCI_LINEENDEXTEND:
85     case SCI_HOMEWRAP:
86     case SCI_HOMEWRAPEXTEND:
87     case SCI_LINEENDWRAP:
88     case SCI_LINEENDWRAPEXTEND:
89     case SCI_DOCUMENTSTART:
90     case SCI_DOCUMENTSTARTEXTEND:
91     case SCI_DOCUMENTEND:
92     case SCI_DOCUMENTENDEXTEND:
93     case SCI_LINECOPY:
94     case SCI_LINECUT:
95     case SCI_LINEDELETE:
96     case SCI_LOWERCASE:
97     case SCI_UPPERCASE:
98     case SCI_COPYALLOWLINE:
99     case SCI_HOMERECTEXTEND:
100     case SCI_LINEENDRECTEXTEND:
101     case SCI_SEARCHANCHOR:
102     {
103       if ((!prev)||(prev->message!=message)) {
104         NewMessage();
105       }
106       return;
107     }
108 
109     case SCI_GOTOLINE:
110     case SCI_GOTOPOS:
111     case SCI_SETSELECTIONMODE:
112     {
113       if ((!prev)||(prev->message!=message)||(prev->wParam!=wParam)||(prev->lParam!=lParam)) {
114         NewMessage();
115       }
116       return;
117     }
118 
119     case SCI_REPLACESEL:
120     case SCI_ADDTEXT:
121     case SCI_INSERTTEXT:
122     case SCI_APPENDTEXT:
123     {
124       if ((prev) && (prev->message==message)) {
125         AppendString();
126       } else {
127         AddString();
128       }
129       return;
130     }
131 
132     case SCI_SEARCHNEXT:
133     case SCI_SEARCHPREV:
134     {
135       if (!prev) {
136         AddString();
137       } else {
138         if ((prev->message==SCI_SEARCHNEXT)||(prev->message==SCI_SEARCHPREV)) {
139           NewMessage();
140           if (strcmp((char*)prev->lParam,(char*)lParam)==0) {
141             mm->lParam=prev->lParam;
142           } else {
143             mm->lParam=reinterpret_cast<long>(strdup((char*)lParam));
144             mm->allocd=1;
145           }
146         } else {
147           AddString();
148         }
149       }
150       return;
151     }
152     case SCI_REPLACETARGET: { // Hijacked message, see SciSearch::NotifyRecorder()
153       AddString();
154       return;
155     }
156     case SCI_PASTE:
157     case SCI_LINEDOWN:
158     case SCI_LINEDOWNEXTEND:
159     case SCI_PARADOWN:
160     case SCI_PARADOWNEXTEND:
161     case SCI_LINEUP:
162     case SCI_LINEUPEXTEND:
163     case SCI_PARAUP:
164     case SCI_PARAUPEXTEND:
165     case SCI_CHARLEFT:
166     case SCI_CHARLEFTEXTEND:
167     case SCI_CHARRIGHT:
168     case SCI_CHARRIGHTEXTEND:
169     case SCI_WORDLEFT:
170     case SCI_WORDLEFTEXTEND:
171     case SCI_WORDRIGHT:
172     case SCI_WORDRIGHTEXTEND:
173     case SCI_WORDPARTLEFT:
174     case SCI_WORDPARTLEFTEXTEND:
175     case SCI_WORDPARTRIGHT:
176     case SCI_WORDPARTRIGHTEXTEND:
177     case SCI_WORDLEFTEND:
178     case SCI_WORDLEFTENDEXTEND:
179     case SCI_WORDRIGHTEND:
180     case SCI_WORDRIGHTENDEXTEND:
181     case SCI_STUTTEREDPAGEUP:
182     case SCI_STUTTEREDPAGEUPEXTEND:
183     case SCI_STUTTEREDPAGEDOWN:
184     case SCI_STUTTEREDPAGEDOWNEXTEND:
185     case SCI_PAGEUP:
186     case SCI_PAGEUPEXTEND:
187     case SCI_PAGEDOWN:
188     case SCI_PAGEDOWNEXTEND:
189     case SCI_DELETEBACK:
190     case SCI_TAB:
191     case SCI_BACKTAB:
192     case SCI_FORMFEED:
193     case SCI_DELWORDLEFT:
194     case SCI_DELWORDRIGHT:
195     case SCI_EDITTOGGLEOVERTYPE:
196     case SCI_CANCEL:
197     case SCI_VCHOME:
198     case SCI_VCHOMEEXTEND:
199     case SCI_VCHOMEWRAP:
200     case SCI_VCHOMEWRAPEXTEND:
201     case SCI_DELWORDRIGHTEND:
202     case SCI_DELLINELEFT:
203     case SCI_DELLINERIGHT:
204     case SCI_LINETRANSPOSE:
205     case SCI_LINEDUPLICATE:
206     case SCI_HOMEDISPLAY:
207     case SCI_HOMEDISPLAYEXTEND:
208     case SCI_LINEENDDISPLAY:
209     case SCI_LINEENDDISPLAYEXTEND:
210     case SCI_LINEDOWNRECTEXTEND:
211     case SCI_LINEUPRECTEXTEND:
212     case SCI_CHARLEFTRECTEXTEND:
213     case SCI_CHARRIGHTRECTEXTEND:
214     case SCI_VCHOMERECTEXTEND:
215     case SCI_PAGEUPRECTEXTEND:
216     case SCI_PAGEDOWNRECTEXTEND:
217     case SCI_SELECTIONDUPLICATE:
218     case SCI_LINESCROLLDOWN:
219     case SCI_LINESCROLLUP:
220     case SCI_DELETEBACKNOTLINE:
221     case SCI_CLEAR:
222     {
223       if ( (!prev) || (prev->message!=message) ) {
224         NewMessage();
225       } else {
226         prev->lParam++;
227       }
228       return;
229     }
230   }
231 }
232 
233 
playback(PlaybackFunc callback,void * user_data)234 void MacroRecorder::playback(PlaybackFunc callback, void*user_data)
235 {
236 
237   for (FXint i=list.no()-1; i>=0; i--) {
238     MacroMessage* mm=list[i];
239     callback(mm->message, mm->wParam, mm->lParam, user_data);
240   }
241 }
242 
243 
244 
clear()245 void MacroRecorder::clear()
246 {
247 
248   for (FXint i=list.no()-1; i>=0; i--) {
249     MacroMessage* mm=list[i];
250     if (mm->allocd) { free((void*)mm->lParam); }
251     delete mm;
252   }
253   list.clear();
254 }
255 
256 
257 
~MacroRecorder()258 MacroRecorder::~MacroRecorder() {
259   clear();
260 }
261 
requote(FXString & s)262 static void requote(FXString&s)
263 {
264   s.substitute("\\", "\\\\", true);
265   s.substitute("\n", "\\n", true);
266   s.substitute("\r", "\\r", true);
267   s.substitute("\"", "\\\"", true);
268 }
269 
270 #define noimpl(msg) { text="-- message " msg " not implemented"; break; }
271 
translate(TranslateFunc callback,void * user_data)272 void MacroRecorder::translate(TranslateFunc callback, void* user_data)
273 {
274 
275   for (FXint i=list.no()-1; i>=0; i--) {
276     FXString text;
277     MacroMessage* mm=list[i];
278     switch (mm->message) {
279       case SCI_CUT: {
280         text=_LUAMOD_".cut()";
281         break;
282       }
283       case SCI_COPY: {
284         text=_LUAMOD_".copy()";
285         break;
286       }
287       case SCI_CLEARALL: {
288         text=_LUAMOD_".text(\"\")";
289         break;
290       }
291       case SCI_SELECTALL: {
292         text=_LUAMOD_".select(0," _LUAMOD_ ".nchars())";
293         break;
294       }
295 
296 
297       case SCI_HOME:
298       case SCI_VCHOME:{
299         text=_LUAMOD_".go(\"edge\", -1)";
300         break;
301       }
302 
303       case SCI_HOMEEXTEND:
304       case SCI_VCHOMEEXTEND:{
305         text=_LUAMOD_".go(\"edge\", -1, true)";
306         break;
307       }
308 
309 
310       case SCI_LINEEND: {
311         text=_LUAMOD_".go(\"edge\", 1)";
312         break;
313       }
314       case SCI_LINEENDEXTEND: {
315         text=_LUAMOD_".go(\"edge\", 1, true)";
316         break;
317       }
318       case SCI_DOCUMENTSTART: {
319         text=_LUAMOD_".go(\"body\", -1)";
320         break;
321       }
322       case SCI_DOCUMENTSTARTEXTEND: {
323         text=_LUAMOD_".go(\"body\", -1, true)";
324         break;
325       }
326       case SCI_DOCUMENTEND: {
327         text=_LUAMOD_".go(\"body\", 1)";
328         break;
329       }
330       case SCI_DOCUMENTENDEXTEND: {
331         text=_LUAMOD_".go(\"body\", 1, true)";
332         break;
333       }
334       case SCI_HOMERECTEXTEND:
335       case SCI_VCHOMERECTEXTEND: {
336         text=_LUAMOD_".go(\"edge\", -1, true, true)";
337         break;
338       }
339       case SCI_LINEENDRECTEXTEND: {
340         text=_LUAMOD_".go(\"edge\", 1, true, true)";
341         break;
342       }
343       case SCI_GOTOLINE: {
344         text.format(_LUAMOD_".caret(%ld,%ld)", mm->wParam+1, mm->lParam);
345         break;
346       }
347       case SCI_GOTOPOS: {
348         if ((i>1)&&(list[i-1]->message==SCI_SEARCHNEXT)&&(list[i-2]->message==SCI_REPLACETARGET)&&(list[i-2]->wParam==2)) {
349             // If message was generated inside SciSearch::ReplaceAllInDoc() just ignore it.
350         } else {
351           text.format(_LUAMOD_".caret(%ld)", mm->wParam);
352         }
353         break;
354       }
355       case SCI_REPLACESEL: {
356         FXString s=(char*)mm->lParam;
357         FXint n=s.contains('\n');
358         if ( (n>0) && (s.length()>1) ) { // If multiple lines,  split into lines for playback...
359           int p;
360           for (p=0; p<n; p++) {
361             FXString part=s.section('\n', p);
362             requote(part);
363             text.format(_LUAMOD_".insert(\"%s\\n\",true)", part.text());
364             callback(text.text(), user_data);
365           }
366           if (!s.section('\n', n).empty()) { // there may still be text beyond the last LF...
367             FXString part=s.section('\n', n);
368             requote(part);
369             text.format(_LUAMOD_".insert(\"%s\",true)", part.text());
370             callback(text.text(), user_data);
371           }
372           continue;
373         }
374         requote(s);
375         text.format(_LUAMOD_".insert(\"%s\",true)", s.text());
376         break;
377       }
378 
379       case SCI_SEARCHNEXT:
380       case SCI_SEARCHPREV: {
381         FXString what=(char*)mm->lParam;
382         requote(what);
383         FXString flags="";
384         if (mm->wParam & SCFIND_MATCHCASE) { flags.append("\"matchcase\""); }
385         if (mm->wParam & SCFIND_WHOLEWORD) { flags.append(",\"wholeword\""); }
386         if (mm->wParam & SCFIND_REGEXP)    { flags.append(",\"regexp\""); }
387         if (flags[0]==',') { flags.erase(0,1); }
388         if ((i>0)&&(list[i-1]->message==SCI_REPLACETARGET)) {
389           FXString with=(char*)list[i-1]->lParam;
390           requote(with);
391           const char*how=NULL;
392           switch (list[i-1]->wParam) {
393             case 0: { how=mm->message==SCI_SEARCHNEXT?"next":"prev"; break; }
394             case 1: { how="sel"; break; }
395             case 2: { how="all"; break; }
396           }
397           text.format(_LUAMOD_".replace(\"%s\",\"%s\", {%s},\"%s\")",
398                          what.text(), with.text(), flags.text(), how );
399 
400         } else {
401 
402           text.format( _LUAMOD_".gofind(\"%s\", {%s}%s)",
403                          what.text(), flags.text(), mm->message==SCI_SEARCHNEXT?"":", false"  );
404         }
405         break;
406       }
407       case SCI_REPLACETARGET: {
408         break;
409       }
410       case SCI_PASTE: {
411         text=_LUAMOD_".paste()";
412         break;
413       }
414       case SCI_LINEDOWN: {
415         text.format(_LUAMOD_".go(\"line\", %ld)", mm->lParam+1);
416         break;
417       }
418       case SCI_LINEDOWNEXTEND: {
419         text.format(_LUAMOD_".go(\"line\", %ld, true)", mm->lParam+1);
420         break;
421       }
422       case SCI_PARADOWN: {
423         text.format(_LUAMOD_".go(\"para\", %ld)", mm->lParam+1);
424         break;
425       }
426       case SCI_PARADOWNEXTEND: {
427         text.format(_LUAMOD_".go(\"para\", %ld, true)", mm->lParam+1);
428         break;
429       }
430       case SCI_LINEUP: {
431         text.format(_LUAMOD_".go(\"line\", -%ld)", mm->lParam+1);
432         break;
433       }
434       case SCI_LINEUPEXTEND: {
435         text.format(_LUAMOD_".go(\"line\", -%ld, true)", mm->lParam+1);
436         break;
437       }
438       case SCI_PARAUP: {
439         text.format(_LUAMOD_".go(\"para\", -%ld)", mm->lParam+1);
440         break;
441       }
442       case SCI_PARAUPEXTEND: {
443         text.format(_LUAMOD_".go(\"para\", -%ld, true)", mm->lParam+1);
444         break;
445       }
446       case SCI_CHARLEFT: {
447         text.format(_LUAMOD_".go(\"char\", -%ld)", mm->lParam+1);
448         break;
449       }
450       case SCI_CHARLEFTEXTEND: {
451         text.format(_LUAMOD_".go(\"char\", -%ld, true)", mm->lParam+1);
452         break;
453       }
454       case SCI_CHARRIGHT: {
455         text.format(_LUAMOD_".go(\"char\", %ld)", mm->lParam+1);
456         break;
457       }
458       case SCI_CHARRIGHTEXTEND: {
459         text.format(_LUAMOD_".go(\"char\", %ld, true)", mm->lParam+1);
460         break;
461       }
462       case SCI_WORDLEFT: {
463         text.format(_LUAMOD_".go(\"word\", -%ld)", mm->lParam+1);
464         break;
465       }
466       case SCI_WORDLEFTEXTEND: {
467         text.format(_LUAMOD_".go(\"word\", -%ld, true)", mm->lParam+1);
468         break;
469       }
470       case SCI_WORDRIGHT: {
471         text.format(_LUAMOD_".go(\"word\", %ld)", mm->lParam+1);
472         break;
473       }
474       case SCI_WORDRIGHTEXTEND: {
475         text.format(_LUAMOD_".go(\"word\", %ld, true)", mm->lParam+1);
476         break;
477       }
478       case SCI_WORDPARTLEFT: {
479         text.format(_LUAMOD_".go(\"part\", -%ld)", mm->lParam+1);
480         break;
481       }
482       case SCI_WORDPARTLEFTEXTEND: {
483         text.format(_LUAMOD_".go(\"part\", -%ld, true)", mm->lParam+1);
484         break;
485       }
486       case SCI_WORDPARTRIGHT: {
487         text.format(_LUAMOD_".go(\"part\", %ld)", mm->lParam+1);
488         break;
489       }
490       case SCI_WORDPARTRIGHTEXTEND: {
491         text.format(_LUAMOD_".go(\"part\", %ld, true)", mm->lParam+1);
492         break;
493       }
494       case SCI_PAGEUP: {
495         text.format(_LUAMOD_".go(\"page\", -%ld)", mm->lParam+1);
496         break;
497       }
498       case SCI_PAGEUPEXTEND: {
499         text.format(_LUAMOD_".go(\"page\", -%ld, true)", mm->lParam+1);
500         break;
501       }
502       case SCI_PAGEDOWN: {
503         text.format(_LUAMOD_".go(\"page\", %ld)", mm->lParam+1);
504         break;
505       }
506       case SCI_PAGEDOWNEXTEND: {
507         text.format(_LUAMOD_".go(\"page\", %ld, true)", mm->lParam+1);
508         break;
509       }
510       case SCI_LINEDOWNRECTEXTEND: {
511         text.format(_LUAMOD_".go(\"line\", %ld, true, true)", mm->lParam+1);
512         break;
513       }
514       case SCI_LINEUPRECTEXTEND: {
515         text.format(_LUAMOD_".go(\"line\", -%ld, true, true)", mm->lParam+1);
516         break;
517       }
518       case SCI_CHARLEFTRECTEXTEND: {
519         text.format(_LUAMOD_".go(\"char\", -%ld, true, true)", mm->lParam+1);
520         break;
521       }
522       case SCI_CHARRIGHTRECTEXTEND: {
523         text.format(_LUAMOD_".go(\"char\", %ld, true, true)", mm->lParam+1);
524         break;
525       }
526       case SCI_PAGEUPRECTEXTEND: {
527         text.format(_LUAMOD_".go(\"page\", -%ld, true, true)", mm->lParam+1);
528         break;
529       }
530       case SCI_PAGEDOWNRECTEXTEND: {
531         text.format(_LUAMOD_".go(\"page\", %ld, true, true)", mm->lParam+1);
532         break;
533       }
534       case SCI_TAB: {
535         text.format(_LUAMOD_".indent(%ld)", mm->lParam+1);
536         break;
537       }
538       case SCI_BACKTAB: {
539         text.format(_LUAMOD_".indent(-%ld)", mm->lParam+1);
540         break;
541       }
542       case SCI_DELETEBACK: {
543         text.format(_LUAMOD_".delete(-%ld)", mm->lParam+1);
544         break;
545       }
546       case SCI_CLEAR: {
547         text.format(_LUAMOD_".delete(%ld)", mm->lParam+1);
548         break;
549       }
550       case SCI_UPPERCASE: {
551         text.format(_LUAMOD_".uppercase()");
552         break;
553       }
554       case SCI_LOWERCASE: {
555         text.format(_LUAMOD_".lowercase()");
556         break;
557       }
558       case SCI_DELWORDLEFT: {
559         text.format(_LUAMOD_".delete(\"word\", -%ld)", mm->lParam+1);
560         break;
561       }
562       case SCI_DELWORDRIGHT: {
563         text.format(_LUAMOD_".delete(\"word\", %ld)", mm->lParam+1);
564         break;
565       }
566       case SCI_DELLINELEFT: {
567         text.format(_LUAMOD_".delete(\"edge\", -1)");
568         break;
569       }
570       case SCI_DELLINERIGHT: {
571         text.format(_LUAMOD_".delete(\"edge\", 1)");
572         break;
573       }
574       case SCI_HOMEWRAP:                noimpl("SCI_HOMEWRAP");
575       case SCI_HOMEWRAPEXTEND:          noimpl("SCI_HOMEWRAP");
576       case SCI_LINEENDWRAP:             noimpl("SCI_LINEENDWRAP");
577       case SCI_LINEENDWRAPEXTEND:       noimpl("SCI_LINEENDWRAPEXTEND");
578       case SCI_LINECOPY:                noimpl("SCI_LINECOPY");
579       case SCI_LINECUT:                 noimpl("SCI_LINECUT");
580       case SCI_LINEDELETE:              noimpl("SCI_LINEDELETE");
581       case SCI_COPYALLOWLINE:           noimpl("SCI_COPYALLOWLINE");
582       case SCI_SEARCHANCHOR:            noimpl("SCI_SEARCHANCHOR");
583       case SCI_ADDTEXT:                 noimpl("SCI_ADDTEXT");
584       case SCI_INSERTTEXT:              noimpl("SCI_INSERTTEXT");
585       case SCI_APPENDTEXT:              noimpl("SCI_APPENDTEXT");
586       case SCI_WORDLEFTEND:             noimpl("SCI_WORDLEFTEND");
587       case SCI_WORDLEFTENDEXTEND:       noimpl("SCI_WORDLEFTENDEXTEND");
588       case SCI_WORDRIGHTEND:            noimpl("SCI_WORDRIGHTEND");
589       case SCI_WORDRIGHTENDEXTEND:      noimpl("SCI_WORDRIGHTENDEXTEND");
590       case SCI_STUTTEREDPAGEUP:         noimpl("SCI_STUTTEREDPAGEUP");
591       case SCI_STUTTEREDPAGEUPEXTEND:   noimpl("SCI_STUTTEREDPAGEUPEXTEND");
592       case SCI_STUTTEREDPAGEDOWN:       noimpl("SCI_STUTTEREDPAGEDOWN");
593       case SCI_STUTTEREDPAGEDOWNEXTEND: noimpl("SCI_STUTTEREDPAGEDOWNEXTEND");
594       case SCI_FORMFEED:                noimpl("SCI_FORMFEED");
595       case SCI_EDITTOGGLEOVERTYPE:      noimpl("SCI_EDITTOGGLEOVERTYPE");
596       case SCI_CANCEL:                  noimpl("SCI_CANCEL");
597 
598       case SCI_VCHOMEWRAP:              noimpl("SCI_VCHOMEWRAP");
599       case SCI_VCHOMEWRAPEXTEND:        noimpl("SCI_VCHOMEWRAPEXTEND");
600       case SCI_DELWORDRIGHTEND:         noimpl("SCI_DELWORDRIGHTEND");
601       case SCI_LINETRANSPOSE:           noimpl("SCI_LINETRANSPOSE");
602       case SCI_LINEDUPLICATE:           noimpl("SCI_LINEDUPLICATE");
603       case SCI_HOMEDISPLAY:             noimpl("SCI_HOMEDISPLAY");
604       case SCI_HOMEDISPLAYEXTEND:       noimpl("SCI_HOMEDISPLAYEXTEND");
605       case SCI_LINEENDDISPLAY:          noimpl("SCI_LINEENDDISPLAY");
606       case SCI_LINEENDDISPLAYEXTEND:    noimpl("SCI_LINEENDDISPLAYEXTEND");
607       case SCI_SELECTIONDUPLICATE:      noimpl("SCI_SELECTIONDUPLICATE");
608 
609       case SCI_LINESCROLLDOWN:          noimpl("SCI_LINESCROLLDOWN");
610       case SCI_LINESCROLLUP:            noimpl("SCI_LINESCROLLUP");
611       case SCI_DELETEBACKNOTLINE:       noimpl("SCI_DELETEBACKNOTLINE");
612       case SCI_SETSELECTIONMODE:        noimpl("SCI_SETSELECTIONMODE");
613     }
614     callback(text.text(), user_data);
615   }
616 }
617 
618 
619 
620 //  Used by the macro recorder to translate "machine language" into a Lua macro.
TranslatorCB(const char * text,void * user_data)621 static void TranslatorCB(const char*text, void*user_data)
622 {
623   FXString*all=(FXString*)user_data;
624   all->append(text);
625   all->append('\n');
626 }
627 
628 
629 
translate()630 const FXString MacroRecorder::translate()
631 {
632   FXString out;
633   translate(TranslatorCB,&out);
634   return out;
635 }
636 
637