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 <cctype>
21 #include <cerrno>
22 #include <fxkeys.h>
23 #include <fx.h>
24 #include <FXTextCodec.h>
25 #include <FXUTF16Codec.h>
26
27 #include "lang.h"
28 #include "prefs_base.h"
29 #include "compat.h"
30 #include "scisrch.h"
31
32 #include "intl.h"
33 #include "scidoc.h"
34
35
36 FXDEFMAP(SciDoc) SciDocMap[] = {
37 FXMAPFUNC(SEL_KEYPRESS, 0, SciDoc::onKeyPress),
38 FXMAPFUNC(SEL_RIGHTBUTTONPRESS, 0, SciDoc::onRightBtnPress),
39 FXMAPFUNC(SEL_COMMAND, SciDoc::ID_RECORD_REPLACE, SciDoc::onRecordReplace),
40 };
41
42 FXIMPLEMENT(SciDoc,FXScintilla,SciDocMap,ARRAYNUMBER(SciDocMap));
43
44
45 static const char* c_openers="{[(";
46 static const char* c_closers="}])";
47 static const char* h_openers="{[(<";
48 static const char* h_closers="}])>";
49
50
51
SciDoc(FXComposite * p,FXObject * tgt,FXSelector sel)52 SciDoc::SciDoc(FXComposite*p,FXObject*tgt,FXSelector sel):FXScintilla(p, tgt, sel, LAYOUT_FILL)
53 {
54 _openers=c_openers;
55 _closers=c_closers;
56 _filename="";
57 _lasterror="";
58 _loading=false;
59 _dirty=false;
60 _utf8=false;
61 need_styled=false;
62 need_backup=false;
63 smart_home=false;
64 _lang=NULL;
65 _filetime=0;
66 splitter_style=SPLIT_NONE;
67 search=new SciSearch(this,ID_RECORD_REPLACE);
68 user_undo_level=0;
69 recording=false;
70 memset(bom,0,sizeof(bom));
71 sendMessage(SCI_SETMARGINTYPEN, 0, SC_MARGIN_NUMBER);
72 sendMessage(SCI_SETMARGINWIDTHN, 1, 4);
73 for (const char*c="CDLVXY"; *c; c++) {
74 sendMessage(SCI_ASSIGNCMDKEY,(*c)+(SCMOD_CTRL<<16),0);
75 }
76 for (const char*c="LUT"; *c; c++) {
77 sendMessage(SCI_ASSIGNCMDKEY,(*c)+(SCMOD_CTRL<<16),0);
78 sendMessage(SCI_ASSIGNCMDKEY,(*c)+(SCMOD_CTRL<<16)+(SCMOD_SHIFT<<16),0);
79 }
80 sendMessage(SCI_ASSIGNCMDKEY,SCK_HOME,SCI_HOME);
81 sendMessage(SCI_ASSIGNCMDKEY,SCK_DELETE+(SCMOD_CTRL<<16), 0);
82 sendMessage(SCI_ASSIGNCMDKEY,SCK_DELETE+(SCMOD_CTRL<<16)+(SCMOD_SHIFT<<16), 0);
83 sendMessage(SCI_ASSIGNCMDKEY,SCK_BACK+(SCMOD_CTRL<<16), 0);
84 sendMessage(SCI_ASSIGNCMDKEY,SCK_BACK+(SCMOD_CTRL<<16)+(SCMOD_SHIFT<<16), 0);
85 sendMessage(SCI_SETSCROLLWIDTHTRACKING,true,0);
86 sendMessage(SCI_SETSCROLLWIDTH,4000,0);
87 sendMessage(SCI_SETEDGECOLOUR,HexToRGB("#FF0000"),0);
88 sendMessage(SCI_SETEOLMODE,SettingsBase::instance()->DefaultFileFormat,0);
89 sendMessage(SCI_SETXCARETPOLICY,CARET_SLOP,8);
90 }
91
92
93
~SciDoc()94 SciDoc::~SciDoc()
95 {
96 delete search;
97 }
98
99
100
ShowPopupMenu(int x,int y)101 void SciDoc::ShowPopupMenu(int x, int y)
102 {
103 long pos=sendMessage(SCI_GETCURRENTPOS,0,0);
104 if (x<0||y<0) {
105 x=sendMessage(SCI_POINTXFROMPOSITION,0,pos);
106 y=sendMessage(SCI_POINTYFROMPOSITION,0,pos);
107 translateCoordinatesTo(x,y,getApp()->getRootWindow(),x,y);
108 }
109 FXPoint p(x,y);
110 if (target && message) { target->tryHandle(this,FXSEL(SEL_PICKED,message), (void*)&p); }
111 }
112
113
114
onRightBtnPress(FXObject * o,FXSelector sel,void * p)115 long SciDoc::onRightBtnPress(FXObject *o, FXSelector sel, void *p)
116 {
117 FXEvent* ev=(FXEvent*)p;
118 ShowPopupMenu(ev->root_x-4,ev->root_y-2);
119 return 1;
120 }
121
122
123
onKeyPress(FXObject * o,FXSelector sel,void * p)124 long SciDoc::onKeyPress(FXObject *o, FXSelector sel, void *p)
125 {
126 FXEvent*ev=(FXEvent*)p;
127 switch (ev->code) {
128 case KEY_Page_Up:
129 case KEY_Page_Down: {
130 long ret=FXScintilla::onKeyPress(o,sel,p);
131 sendMessage(SCI_SCROLLCARET,0,0);
132 return ret;
133 }
134 case KEY_Tab: { // Ctrl+Tab forces a tab when "UseTabs" is off
135 if ((ev->state & CONTROLMASK) && (sendMessage(SCI_GETUSETABS, 0, 0)==0)) {
136 sendString(SCI_ADDTEXT, 1, "\t");
137 return 1;
138 } else { break; }
139 }
140 case KEY_Menu: {
141 ShowPopupMenu(-1,-1);
142 return 1;
143 }
144 case KEY_F10: {
145 if (ev->state & SHIFTMASK) {
146 ShowPopupMenu(-1,-1);
147 return 1;
148 } else { break; }
149 }
150 }
151 return FXScintilla::onKeyPress(o,sel,p);
152 }
153
154
155
moveContents(FXint x,FXint y)156 void SciDoc::moveContents(FXint x,FXint y)
157 {
158 FXScintilla::moveContents(x,y);
159 FXint sw=sendMessage(SCI_GETSCROLLWIDTH,0,0);
160 horizontal->setRange(sw);
161 }
162
163
164
SetEolModeFromContent()165 void SciDoc::SetEolModeFromContent()
166 {
167 char*contents=(char*)sendMessage(SCI_GETCHARACTERPOINTER,0,0);
168 FXuint nCR=0;
169 FXuint nLF=0;
170 for (char*p=contents; *p; p++) {
171 switch (*p) {
172 case '\r': { nCR++; break; }
173 case '\n': { nLF++; break; }
174 }
175 }
176 if (nCR==0) {
177 if (nLF>0) { // for sure, it's Unix
178 sendMessage(SCI_SETEOLMODE,SC_EOL_LF,0);
179 }
180 } else {
181 if (nCR==nLF) { // most likely, it's DOS
182 sendMessage(SCI_SETEOLMODE, SC_EOL_CRLF, 0);
183 } else {
184 if (nLF==0) { // for sure, it's Mac
185 sendMessage(SCI_SETEOLMODE, SC_EOL_CR, 0);
186 } else { // mixed line endings, so take a wild guess
187 if (nCR>nLF) { // Mac format is rare, so DOS wins here
188 sendMessage(SCI_SETEOLMODE, SC_EOL_CRLF, 0);
189 } else {
190 if ((nLF/2)>nCR) { // More Unix lines than DOS lines
191 sendMessage(SCI_SETEOLMODE,SC_EOL_LF,0);
192 } else { // More DOS lines than Unix lines
193 sendMessage(SCI_SETEOLMODE,SC_EOL_CRLF,0);
194 }
195 }
196 }
197 }
198 }
199 sendMessage(SCI_CONVERTEOLS,sendMessage(SCI_GETEOLMODE,0,0),0);
200 }
201
202
203
AdjustHScroll()204 void SciDoc::AdjustHScroll()
205 {
206 FXint linecount=sendMessage(SCI_GETLINECOUNT,0,0);
207 FXint widest=0;
208 FXint line=0;
209 for (FXint i=0; i<linecount; i++) {
210 FXint w=sendMessage(SCI_LINELENGTH,i,0);
211 if (w>widest) {
212 widest=w;
213 line=i;
214 }
215 }
216 FXint pos=sendMessage(SCI_GETLINEENDPOSITION,line,0);
217 FXint x=sendMessage(SCI_POINTXFROMPOSITION,0,pos);
218 if (sendMessage(SCI_GETSCROLLWIDTH,0,0)<x) {
219 sendMessage(SCI_SETSCROLLWIDTH,x,0);
220 }
221 }
222
223
224
225 extern "C" {
226 char get_file_encoding(const char*filename);
227 }
228
229
230
BinaryFileMessage()231 const char* SciDoc::BinaryFileMessage() { return "Binary file detected."; }
232
233
234
ConfirmOpenBinary(SciDoc * sci,const char * filename)235 static bool ConfirmOpenBinary(SciDoc*sci, const char*filename)
236 {
237 return FXMessageBox::warning(sci->getShell(), MBOX_YES_NO, _("Binary file"),
238 "%s\n%s\n\n%s", filename, _("does not appear to be a text file."),
239 _("Are you sure you want to open it?")) == MBOX_CLICKED_YES;
240 }
241
242
243
DoLoadFromFile(const char * filename,bool insert)244 bool SciDoc::DoLoadFromFile(const char*filename,bool insert)
245 {
246 _lasterror="";
247 errno=0;
248 memset(bom,0,sizeof(bom));
249 bool rv=true;
250 bool ro=GetReadOnly();
251 FXTextCodec *codec=NULL;
252 if (ro&&insert) {
253 _lasterror=_("Document is marked read-only.");
254 return false;
255 }
256 if (FXStat::isDirectory(filename)) {
257 _lasterror=_("is a directory");
258 return false;
259 }
260 bool DefaultToAscii=SettingsBase::instance()->DefaultToAscii;
261 bool DefaultToSbcs=SettingsBase::instance()->DefaultToSbcs;
262 switch (get_file_encoding(filename)) {
263 case 'B': { // Binary file
264 if ( !ConfirmOpenBinary(this,filename) ) {
265 _lasterror=BinaryFileMessage();
266 return false;
267 }
268 if (!insert) { SetUTF8(!DefaultToSbcs); }
269 break;
270 }
271 case 'T': { // Plain US-ASCII text file
272 if (!insert) { SetUTF8(!DefaultToAscii); }
273 break;
274 }
275 case 'H': { // High (extended ASCII) text file.
276 if (!insert) { SetUTF8(!DefaultToSbcs); }
277 break;
278 }
279 case 'U': { // UTF-8 encoded text file w/o BOM.
280 if (!insert) { SetUTF8(true); }
281 break;
282 }
283 case 'M': { // UTF-8 BOM.
284 if (!insert) {
285 strcpy(bom,"\0357\0273\0277");
286 SetUTF8(true);
287 }
288 break;
289 }
290 case 'Z': { // Zero-length (empty) file.
291 if (!insert) { SetUTF8(!DefaultToAscii); }
292 break;
293 }
294 case 'e': { // UTF-16LE BOM.
295 if (!insert) {
296 codec=new FXUTF16LECodec();
297 strcpy(bom,"\377\376");
298 SetUTF8(false);
299 }
300 break;
301 }
302 case 'E': { // UTF-16BE BOM.
303 if (!insert) {
304 codec=new FXUTF16BECodec();
305 strcpy(bom,"\376\377");
306 SetUTF8(false);
307 }
308 break;
309 }
310 case 'F': { // Failure, could not read the file.
311 _lasterror=SystemErrorStr();
312 return false;
313 break;
314 }
315 }
316 FXFile fh(filename, FXFile::Reading);
317 if (fh.isOpen()) {
318 if (ro) {
319 // This might happen e.g. if we are updating a document that has been modified externally
320 sendMessage(SCI_SETREADONLY,0,0);
321 }
322 static const int BUFSIZE=1025;
323 char buf[BUFSIZE];
324 fh.position(strlen(bom),FXIO::Begin);
325 long p=0;
326 _loading=!insert;
327 if (insert) {
328 p=sendMessage(SCI_GETCURRENTPOS, 0,0);
329 sendMessage(SCI_BEGINUNDOACTION, 0, 0);
330 } else {
331 sendMessage(SCI_CLEARALL,0,0);
332 }
333 do {
334 memset(buf,0,BUFSIZE);
335 FXival n=fh.readBlock(buf,BUFSIZE-1);
336 if (n<0) {
337 _lasterror=SystemErrorStr();
338 rv=false;
339 break;
340 }
341 buf[n]='\0';
342 if (insert) {
343 _dirty=true;
344 if (GetSelLength()>0) {
345 sendString(SCI_REPLACESEL,0,buf);
346 p=sendMessage(SCI_GETCURRENTPOS, 0,0);
347 } else {
348 sendString(SCI_INSERTTEXT,p,buf);
349 p+=n;
350 }
351 } else {
352 sendString(SCI_APPENDTEXT,n,buf);
353 }
354 } while (!fh.eof());
355 fh.close();
356 if (rv) {
357 if (insert) {
358 GoToPos(p);
359 sendMessage(SCI_CONVERTEOLS,sendMessage(SCI_GETEOLMODE,0,0),0);
360 sendMessage(SCI_ENDUNDOACTION,0,0);
361 } else {
362 _filename=FXPath::absolute(filename);
363 _filetime=FXStat::modified(_filename);
364 _dirty=false;
365 need_backup=false;
366 if (codec) {
367 const char *orig=(const char*)sendMessage(SCI_GETCHARACTERPOINTER,0,0);
368 FXString recode;
369 recode.length(codec->mb2utflen(orig,sendMessage(SCI_GETLENGTH,0,0)));
370 codec->mb2utf((char*)recode.text(),recode.length(),orig,sendMessage(SCI_GETLENGTH,0,0));
371 delete codec;
372 SetUTF8(true);
373 sendString(SCI_SETTEXT,0,recode.text());
374 }
375 SetEolModeFromContent();
376 sendMessage(SCI_EMPTYUNDOBUFFER,0,0);
377 }
378 AdjustHScroll();
379 }
380 if (ro) { sendMessage(SCI_SETREADONLY,1,0); }
381 } else {
382 _lasterror=SystemErrorStr();
383 rv=false;
384 }
385 _loading=false;
386 return rv;
387 }
388
GetEncoding()389 const char*SciDoc::GetEncoding()
390 {
391 switch ((FXuchar)bom[0]) {
392 case 0xFF: { return "UTF-16LE"; }
393 case 0xFE: { return "UTF-16BE"; }
394 default: { return _utf8?"UTF-8":"ASCII"; }
395 }
396 }
397
398
SaveToFile(const char * filename,bool as_itself)399 bool SciDoc::SaveToFile(const char*filename, bool as_itself)
400 {
401 #ifdef WIN32
402 FXFile fh(filename, FXIO::WriteOnly|FXIO::Truncate|((FXStat::exists(filename))?0:FXIO::Create));
403 #else
404 FXFile fh(filename, FXIO::Writing);
405 #endif
406 _lasterror="";
407 if (fh.isOpen()) {
408 FXbool en=isEnabled();
409 FXbool hf=hasFocus();
410 FXTextCodec *codec=NULL;
411 FXString recode=FXString::null;
412 if (en) { disable(); }
413 FXint len=sendMessage(SCI_GETLENGTH,0,0);
414 const char*buf=(const char*)sendMessage(SCI_GETCHARACTERPOINTER,0,0);
415 if (bom[0]) {
416 fh.writeBlock(bom,strlen(bom));
417 switch ((FXuchar)bom[0]) {
418 case 0xFF: {
419 codec=new FXUTF16LECodec();
420 break;
421 }
422 case 0xFE: {
423 codec=new FXUTF16BECodec();
424 break;
425 }
426 }
427 }
428 if (codec) {
429 FXint old_len=len;
430 len=codec->utf2mblen(buf,old_len);
431 recode.length(len);
432 codec->utf2mb((char*)recode.text(),len,buf,old_len);
433 delete codec;
434 buf=recode.text();
435 }
436 FXival wrote=fh.writeBlock(buf,len);
437 if (en) { enable(); }
438 if (hf) { setFocus(); }
439 if (fh.close() && (wrote==len)) {
440 if (as_itself) {
441 if (_filename.empty() && !getLanguage()) {
442 if (!SetLanguageForHeader(filename)) {
443 if (!setLanguageFromFileName(FXPath::name(filename).text())) {
444 setLanguageFromContent();
445 }
446 }
447 }
448 _filename=FXPath::absolute(filename);
449 _filetime=FXStat::modified(_filename);
450 sendMessage(SCI_SETSAVEPOINT,0,0);
451 DoStaleTest(true);
452 }
453 return true;
454 }
455 }
456 _lasterror=SystemErrorStr();
457 return false;
458 }
459
460
461
GtLtIsBrace(bool gtlt)462 void SciDoc::GtLtIsBrace(bool gtlt)
463 {
464 _openers=gtlt?h_openers:c_openers;
465 _closers=gtlt?h_closers:c_closers;
466 }
467
468
469
GtLtIsBrace()470 bool SciDoc::GtLtIsBrace()
471 {
472 return _openers==h_openers;
473 }
474
475
476 #define IsCloser(c) ((c)&&(strchr(_closers,(c))))
477 #define IsOpener(c) ((c)&&(strchr(_openers,(c))))
478
IsInsideBrace(long & pos)479 inline bool SciDoc::IsInsideBrace(long &pos)
480 {
481 char ThisBrace=sendMessage(SCI_GETCHARAT,pos,0);
482 if (!IsCloser(ThisBrace)) {
483 ThisBrace=sendMessage(SCI_GETCHARAT,pos-1,0);
484 if (IsOpener(ThisBrace)) {
485 pos--;
486 } else {
487 return false;
488 }
489 }
490 int charwidth = sendMessage(SCI_POSITIONAFTER,pos,0)-sendMessage(SCI_POSITIONBEFORE,pos,0);
491 return ((charwidth>=2)||((pos==0)&&(charwidth==1)));
492 }
493
494
495
IsOutsideBrace(long & pos)496 inline bool SciDoc::IsOutsideBrace(long &pos)
497 {
498 char ThisBrace=sendMessage(SCI_GETCHARAT,pos-1,0);
499 if (IsCloser(ThisBrace)) {
500 if (sendMessage(SCI_GETCURRENTPOS,0,0)==pos) { pos--; }
501 } else {
502 ThisBrace=sendMessage(SCI_GETCHARAT,pos,0);
503 if (!IsOpener(ThisBrace)) {
504 return false;
505 }
506 }
507 int charwidth = sendMessage(SCI_POSITIONAFTER,pos,0)-sendMessage(SCI_POSITIONBEFORE,pos,0);
508 return ((charwidth>=2)||((pos==0)&&(charwidth==1)));
509 }
510
511
512
IsAfterBrace(long & pos)513 inline bool SciDoc::IsAfterBrace(long &pos)
514 {
515 if (pos<=0) { return false; }
516 char ThisBrace=sendMessage(SCI_GETCHARAT,pos-1,0);
517 if (IsOpener(ThisBrace)||IsCloser(ThisBrace)) {
518 pos--;
519 } else {
520 return false;
521 }
522 int charwidth = sendMessage(SCI_POSITIONAFTER,pos,0)-sendMessage(SCI_POSITIONBEFORE,pos,0);
523 return ((charwidth>=2)||((pos==0)&&(charwidth==1)));
524 }
525
526
527
IsBrace(long & pos,int mode)528 inline bool SciDoc::IsBrace(long &pos, int mode)
529 {
530 switch (mode) {
531 case BRACEMATCH_INSIDE: { return IsInsideBrace(pos); }
532 case BRACEMATCH_OUTSIDE: { return IsOutsideBrace(pos); }
533 case BRACEMATCH_EITHER: { return (IsOutsideBrace(pos)||IsInsideBrace(pos)); }
534 case BRACEMATCH_AFTER: { return IsAfterBrace(pos); }
535 default: { return false; }
536 }
537 }
538
539
540
541 #define INVALID_RANGE 2147483647
542
543 #define StyleAt(idx) (sendMessage(SCI_GETSTYLEAT,(idx),0))
544
MatchBrace()545 void SciDoc::MatchBrace()
546 {
547 int mode=SettingsBase::instance()->BraceMatch;
548 long CurrPos=sendMessage(SCI_GETCURRENTPOS,0,0);
549 if ((CurrPos>0)&&(mode==BRACEMATCH_EITHER)) {
550 char ThisChar=sendMessage(SCI_GETCHARAT,CurrPos,0);
551 if (IsCloser(ThisChar)||IsOpener(ThisChar)) {
552 long PrevPos=sendMessage(SCI_POSITIONBEFORE,CurrPos,0);
553 char PrevChar=sendMessage(SCI_GETCHARAT,PrevPos,0);
554 if (IsCloser(PrevChar)||IsOpener(PrevChar)) { CurrPos=PrevPos; }
555 }
556 }
557 if (IsBrace(CurrPos,mode)) {
558 int ThatBrace=sendMessage(SCI_BRACEMATCH,CurrPos,0);
559 if ((ThatBrace>=0)&&(StyleAt(ThatBrace)!=StyleAt(CurrPos))) {
560 sendMessage(SCI_COLOURISE,0,-1);
561 ThatBrace=sendMessage(SCI_BRACEMATCH,CurrPos,0);
562 }
563 if (ThatBrace>=0) {
564 sendMessage(SCI_BRACEHIGHLIGHT,CurrPos,ThatBrace);
565 } else {
566 sendMessage(SCI_BRACEBADLIGHT,CurrPos,0);
567 }
568 } else {
569 sendMessage(SCI_BRACEHIGHLIGHT,INVALID_RANGE,INVALID_RANGE);
570 }
571 }
572
573
574
setFont(const FXString & font,int size)575 void SciDoc::setFont(const FXString &font, int size)
576 {
577 sendString(SCI_STYLESETFONT, STYLE_DEFAULT, font.text());
578 sendMessage(SCI_STYLESETSIZE, STYLE_DEFAULT, size);
579 UpdateStyle();
580 }
581
582
583
584 static StyleDef *GlobalStyle=NULL;
585
586
587
setLanguage(LangStyle * ls)588 bool SciDoc::setLanguage(LangStyle*ls)
589 {
590 _lang=ls;
591 UpdateStyle();
592 return (_lang!=NULL);
593 }
594
595
596
setLanguage(const char * name)597 bool SciDoc::setLanguage(const char*name)
598 {
599 return setLanguage(GetLangByName(name));
600 }
601
602
603
setLanguageFromFileName(const char * ext)604 bool SciDoc::setLanguageFromFileName(const char*ext)
605 {
606 return setLanguage(GetLangFromDocName(ext));
607 }
608
609
610 // Try to guess if a *.h file is C or C++, return FALSE if file is not *.h, TRUE otherwise.
SetLanguageForHeader(const FXString & fn)611 bool SciDoc::SetLanguageForHeader(const FXString &fn)
612 {
613 if (FXPath::extension(fn)=="h") {
614 FXString fnbase=FXPath::stripExtension(fn);
615 // Check for matching source file and set language accordingly if found...
616 if (FXStat::exists(fnbase+".c")) {
617 setLanguage("c");
618 } else if (FXStat::exists(fnbase+".cpp")||FXStat::exists(fnbase+".cxx")||FXStat::exists(fnbase+".cc")) {
619 setLanguage("cpp");
620 } else {
621 // Take a wild guess - if the file contains the word "class" it's probably C++
622 const char *content=(const char*)(sendMessage(SCI_GETCHARACTERPOINTER,0,0));
623 #ifdef FOX_1_7_50_OR_NEWER
624 if (FXRex("\\<class\\>").search(content,strlen(content),0,strlen(content))>=0) {
625 #else
626 if (FXRex("\\<class\\>").match(content)) {
627 #endif
628 setLanguage("cpp");
629 } else {
630 setLanguage("c");
631 }
632 }
633 return true;
634 } else {
635 return false;
636 }
637 }
638
639
640
641
642
643
644 /*
645 Currently, this only works for files beginning with "<?xml" or "<!DOCTYPE HTML"
646 and "shabang" files, e.g. #!/usr/bin/bash
647 */
648 bool SciDoc::setLanguageFromContent()
649 {
650 char buf[256];
651 char*p1=buf;
652 sendString(SCI_GETTEXT, sizeof(buf), buf);
653 while (isspace(*p1)) {p1++;}
654 switch(*p1) {
655 case '<': { // maybe markup
656 switch (p1[1]) {
657 case '!': {
658 if ((strncmp(p1, "<!DOCTYPE", 9) == 0) && isspace(p1[9])) {
659 p1+=9;
660 while (isspace(*p1)) {p1++;}
661 return ( (strncasecmp(p1, "html", 4)==0) && setLanguage("html") );
662 } else {
663 return false;
664 }
665 }
666 case '?':{
667 return ((strncasecmp(p1, "<?xml", 5)==0) && isspace(p1[5]) && setLanguage("xml"));
668 }
669 default: return false;
670 }
671 }
672 case '#': { // maybe shell script
673 if (p1[1]=='!') {
674 FXString appname="";
675 char*p2;
676 p1+=2;
677 while (isblank(*p1)) { p1++; }
678 p2=p1;
679 while ((*p2)&&!isspace(*p2)) { p2++; }
680 if (!*p2) { return false; }
681 *p2='\0';
682 appname=FXPath::name(p1);
683 if (strcmp(appname.text(), "env")==0) {
684 p1=p2+1;
685 while (isblank(*p1)) { p1++; }
686 p2=p1;
687 while ((*p2)&&!isspace(*p2)) { p2++; }
688 if (!*p2) { return false; }
689 *p2='\0';
690 appname=FXPath::name(p1);
691 }
692 return setLanguage(GetLangFromAppName(appname.text()));
693 } else {
694 return false;
695 }
696 }
697 default:return false;
698 }
699 return false;
700 }
701
702
703
704 void SciDoc::UpdateStyle()
705 {
706 StyleDef *sd;
707 int i;
708 sendMessage(SCI_STYLECLEARALL, 0, 0);
709 sendString(SCI_STYLESETFONT, STYLE_CALLTIP, getApp()->getNormalFont()->getName().text());
710 sendMessage(SCI_STYLESETSIZE, STYLE_CALLTIP, getApp()->getNormalFont()->getSize()/10);
711 sendMessage(SCI_CALLTIPUSESTYLE,32,1);
712
713 ColorName DefaultFG;
714 ColorName DefaultBG;
715
716 if (GlobalStyle) {
717 for (sd=GlobalStyle; sd&&sd->key; sd++) {
718 sendMessage(SCI_STYLESETFORE, sd->id, HexToRGB(sd->fg));
719 sendMessage(SCI_STYLESETBACK, sd->id, HexToRGB(sd->bg));
720 sendMessage(SCI_STYLESETITALIC, sd->id, sd->style & Italic);
721 sendMessage(SCI_STYLESETBOLD, sd->id, sd->style & Bold);
722 }
723 strncpy(DefaultFG, GlobalStyle->fg, 7);
724 strncpy(DefaultBG, GlobalStyle->bg, 7);
725 } else {
726 strncpy(DefaultFG, "#000000", 7);
727 strncpy(DefaultBG, "#ffffff", 7);
728 }
729 DefaultFG[7]='\0';
730 DefaultBG[7]='\0';
731 if (_lang) {
732 sendMessage(SCI_SETLEXER, _lang->id, 0);
733 sendMessage(SCI_SETSTYLEBITS, sendMessage(SCI_GETSTYLEBITSNEEDED,0,0), 0);
734 for (i=0; i<=KEYWORDSET_MAX; i++) { sendString(SCI_SETKEYWORDS, i, ""); }
735 if (_lang->words) {
736 for (i=0; _lang->words[i]; i++) {
737 sendString(SCI_SETKEYWORDS, i, _lang->words[i]);
738 }
739 }
740 if (_lang->styles) {
741 for (sd=_lang->styles; sd->key; sd++) {
742 sendMessage(SCI_STYLESETFORE, sd->id, HexToRGB(sd->fg[0]?sd->fg:DefaultFG));
743 sendMessage(SCI_STYLESETBACK, sd->id, HexToRGB(sd->bg[0]?sd->bg:DefaultBG));
744 sendMessage(SCI_STYLESETITALIC, sd->id, sd->style & Italic);
745 sendMessage(SCI_STYLESETBOLD, sd->id, sd->style & Bold);
746 // sendMessage(SCI_STYLESETUNDERLINE, sd->id, sd->style & Underline);
747 // sendMessage(SCI_STYLESETEOLFILLED, sd->id, sd->style & EOLFill);
748 }
749 }
750 switch (_lang->tabs) {
751 case TABS_DEFAULT: {
752 UseTabs(SettingsBase::instance()->UseTabs);
753 break;
754 }
755 case TABS_ALWAYS: {
756 UseTabs(true);
757 break;
758 }
759 case TABS_NEVER: {
760 UseTabs(false);
761 }
762 case TABS_AUTO:{
763 char*contents=(char*)sendMessage(SCI_GETCHARACTERPOINTER,0,0);
764 if (contents && ( (contents[0]=='\t') || strstr(contents,"\n\t") ) ) {
765 UseTabs(true);
766 } else {
767 UseTabs(SettingsBase::instance()->UseTabs);
768 }
769 }
770 }
771 TabWidth(_lang->tabwidth>0?_lang->tabwidth:SettingsBase::instance()->TabWidth);
772 SetProperty("lexer.cpp.track.preprocessor","0");
773 SetProperty("lexer.cpp.update.preprocessor","0");
774 } else {
775 sendMessage(SCI_SETLEXER, 0, 0);
776 sendMessage(SCI_SETLEXERLANGUAGE, 0, 0);
777 UseTabs(SettingsBase::instance()->UseTabs);
778 TabWidth(SettingsBase::instance()->TabWidth);
779 }
780 sendMessage(SCI_COLOURISE,0,-1);
781 if (Slave()) { Slave()->UpdateStyle(); }
782 }
783
784
785
786 #define slave(f,v) { if (Slave()) { Slave()->f(v); } }
787
788
789
790 void SciDoc::CaretLineBG(const char*bgcolor)
791 {
792 memset(_caretlinebg,0,sizeof(_caretlinebg));
793 if (bgcolor) { strncpy(_caretlinebg, bgcolor, sizeof(_caretlinebg)-1); }
794 sendMessage(SCI_SETCARETLINEBACK, HexToRGB(_caretlinebg), 0);
795 sendMessage(SCI_SETCARETLINEVISIBLE, _caretlinebg[0]?1:0, 0);
796 slave(CaretLineBG,bgcolor);
797 }
798
799
800
801 void SciDoc::RightMarginBG(const char*bgcolor)
802 {
803 if (bgcolor) {
804 memset(_rightmarginbg,0,sizeof(_rightmarginbg));
805 strncpy(_rightmarginbg, bgcolor, sizeof(_rightmarginbg)-1);
806 } else {
807 _rightmarginbg[0]='\0';
808 }
809 sendMessage(SCI_SETEDGECOLOUR, HexToRGB(_rightmarginbg), 0);
810 slave(RightMarginBG,bgcolor);
811 }
812
813
814
815 // 0=transparent to 255=opaque.
816 void SciDoc::CaretLineAlpha(const char*alpha)
817 {
818 // setting SCI_SETCARETLINEBACKALPHA does not work correctly!
819 // and furthermore, it even makes SCI_SETCARETLINEBACK fail!
820 #if 0
821 if (alpha) {
822 memset(_caretlinealpha,0,sizeof(_caretlinealpha));
823 strncpy(_caretlinealpha, alpha, sizeof(_caretlinealpha)-1);
824 unsigned int r=0,g=0,b=0, rgb;
825 sscanf(_caretlinealpha+1,"%2x%2x%2x",&r,&g,&b);
826 rgb=((r+g+b)/3);
827 sendMessage(SCI_SETCARETLINEBACKALPHA, rgb, 0);
828 } else {
829 _caretlinebg[0]='\0';
830 }
831 slave(CaretLineAlpha,alpha);
832 #endif
833 }
834
835
836
837 void SciDoc::WhiteSpaceBG(const char*bgcolor)
838 {
839 if (bgcolor) {
840 strncpy(_whitespacebg, bgcolor, sizeof(_whitespacebg)-1);
841 sendMessage(SCI_SETWHITESPACEBACK, 1, HexToRGB(_whitespacebg));
842 } else {
843 sendMessage(SCI_SETWHITESPACEBACK, 0, 0);
844 _whitespacebg[0]='\0';
845 }
846 slave(WhiteSpaceBG,bgcolor);
847 }
848
849
850
851 void SciDoc::WhiteSpaceFG(const char*fgcolor)
852 {
853 if (fgcolor) {
854 strncpy(_whitespacefg, fgcolor, sizeof(_whitespacefg)-1);
855 sendMessage(SCI_SETWHITESPACEFORE, 1, HexToRGB(_whitespacefg));
856 } else {
857 sendMessage(SCI_SETWHITESPACEFORE, 0, 0);
858 _whitespacefg[0]='\0';
859 }
860 slave(WhiteSpaceFG,fgcolor);
861 }
862
863
864
865 void SciDoc::CaretFG(const char*fgcolor)
866 {
867 if (fgcolor) {
868 strncpy(_caretfg, fgcolor, sizeof(_caretfg)-1);
869 sendMessage(SCI_SETCARETFORE, HexToRGB(_caretfg), 0);
870 } else {
871 sendMessage(SCI_SETCARETFORE, 0, 0);
872 _caretfg[0]='\0';
873 }
874 slave(CaretFG,fgcolor);
875 }
876
877
878
879 void SciDoc::SelectionBG(const char*bgcolor)
880 {
881 if (bgcolor) {
882 strncpy(_selectionbg, bgcolor, sizeof(_selectionbg)-1);
883 sendMessage(SCI_SETSELBACK, 1, HexToRGB(_selectionbg));
884 } else {
885 sendMessage(SCI_SETCARETFORE, 0, 0);
886 _caretfg[0]='\0';
887 }
888 slave(SelectionBG,bgcolor);
889 }
890
891
892
893 void SciDoc::DefaultStyles(StyleDef*styles)
894 {
895 GlobalStyle=styles;
896 }
897
898
899
900 StyleDef* SciDoc::DefaultStyles()
901 {
902 return GlobalStyle;
903 }
904
905
906
907 long SciDoc::GetSelLength()
908 {
909 long start=sendMessage(SCI_GETSELECTIONSTART,0,0);
910 long end=sendMessage(SCI_GETSELECTIONEND,0,0);
911 return (end>start)?end-start:start-end;
912 }
913
914
915
916 long SciDoc::GetSelText(FXString&txt)
917 {
918 long len=sendMessage(SCI_GETSELTEXT,0,0);
919 txt="";
920 if (len<=1) { return 0; }
921 txt.length(len-1);
922 sendString(SCI_GETSELTEXT,0,txt.text());
923 return len;
924 }
925
926
927
928 void SciDoc::SetSelText(const FXString&source)
929 {
930 sendString(SCI_REPLACESEL,0,source.text());
931 }
932
933
934
935 long SciDoc::GetText(FXString&txt)
936 {
937 long len=sendMessage(SCI_GETLENGTH,0,0);
938 txt.length(len+1);
939 sendString(SCI_GETTEXT, len+1, txt.text());
940 return txt.length();
941 }
942
943
944
945 void SciDoc::SetText(const char *source) {
946 sendMessage(SCI_SETTARGETSTART,0,0);
947 sendMessage(SCI_SETTARGETEND,sendMessage(SCI_GETLENGTH,0,0),0);
948 sendString(SCI_REPLACETARGET,strlen(source),source);
949 repaint();
950 }
951
952
953
954 #define WORD_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"
955
956 long SciDoc::WordAtPos(FXString&s, long pos)
957 {
958 s="";
959 if (pos<0) { pos= sendMessage(SCI_GETCURRENTPOS,0,0); }
960 long line=sendMessage(SCI_LINEFROMPOSITION,pos,0);
961 GetLineText(line,s);
962 if (!s.empty()) {
963 long bow=sendMessage(SCI_GETCOLUMN,pos,0);
964 long eow=bow;
965 while (strchr(WORD_CHARS, s[bow])) { bow--; }
966 while (strchr(WORD_CHARS, s[eow])) { eow++; }
967 s.trunc(eow);
968 s.erase(0,bow+1);
969 }
970 s.length(strlen(s.text()));
971 return s.length();
972 }
973
974
975
976 long SciDoc::PrefixAtPos(FXString&s, long pos)
977 {
978 s="";
979 if (pos<0) { pos= sendMessage(SCI_GETCURRENTPOS,0,0); }
980 long line=sendMessage(SCI_LINEFROMPOSITION,pos,0);
981 GetLineText(line,s);
982 if (!s.empty()) {
983 long bow=sendMessage(SCI_GETCOLUMN,pos,0);
984 long eow=bow;
985 do { bow--; } while (strchr(WORD_CHARS, s[bow]));
986 s.trunc(eow);
987 s.erase(0,bow+1);
988 }
989 s.length(strlen(s.text()));
990 return s.length();
991 }
992
993
994 // Accept any one of these chars as a delimiter
995 // for line:column coordinates
996 static const char* RowColDelims=":,.";
997
998
999 /*
1000 Try to parse row:column coordinates from a string like "123:456"
1001 or "645,321" and navigate to that location.
1002 return true if we at least found a line number.
1003 */
1004 bool SciDoc::GoToStringCoords(const char*coords)
1005 {
1006 long row,col,rows,cols,pos;
1007 char buf[256];
1008 char*p1, *p2=NULL;
1009 if (!coords) { return false; }
1010 strncpy(buf,coords,sizeof(buf)-1);
1011 for (p1=buf; *p1; p1++) {
1012 if (strchr(RowColDelims, *p1)) {
1013 p2=p1+1;
1014 *p1='\0';
1015 }
1016 }
1017 row=strtoul(buf,&p1,10);
1018 if ((p1&&!*p1)&&row>0) {
1019 rows=sendMessage(SCI_GETLINECOUNT,0,0);
1020 if (row>rows) { row=rows; }
1021 if (p2) {
1022 col=strtoul(p2,&p1,10);
1023 if ((!p1)||(*p1)) { col=0; }
1024 } else { col=0; }
1025 cols=sendMessage(SCI_LINELENGTH,row-1,0);
1026 if (col>=cols) { col=(cols-1); }
1027 pos=sendMessage(SCI_POSITIONFROMLINE,row-1,0)+col;
1028 GoToPos(pos);
1029 return true;
1030 }
1031 return false;
1032 }
1033
1034
1035
1036
1037 // Move caret to specified row and column coordinates -
1038 // NOTE: First row is one, NOT zero!!!
1039 void SciDoc::GoToCoords(long row, long col)
1040 {
1041 long rows,cols,pos;
1042 rows=sendMessage(SCI_GETLINECOUNT,0,0);
1043 if (row>rows) { row=rows; }
1044 cols=sendMessage(SCI_LINELENGTH,row-1,0);
1045 if (col>=cols) { col=(cols-1); }
1046 pos=sendMessage(SCI_POSITIONFROMLINE,row-1,0)+col;
1047 GoToPos(pos);
1048 }
1049
1050
1051
1052 void SciDoc::ScrollCaret(long pos)
1053 {
1054 long top,btm,cur;
1055 sendMessage(SCI_SETXOFFSET,0,0);
1056 if (pos<0) sendMessage(SCI_SCROLLCARET,0,0);
1057 if (GetWordWrap()) { return; }
1058 for (FXWindow *w=this; w; w=w->getParent()) { w->layout(); }
1059 if (pos>=0) { sendMessage(SCI_GOTOPOS,pos,0); }
1060 top=sendMessage(SCI_GETFIRSTVISIBLELINE,0,0);
1061 cur=sendMessage(SCI_LINEFROMPOSITION,pos>=0?pos:sendMessage(SCI_GETCURRENTPOS,0,0),0);
1062 btm=sendMessage(SCI_LINESONSCREEN,0,0)+top;
1063 if ( (cur>top)&&(cur<btm) ) { return; }
1064 if ( (top>0) && (top>=cur) ) {
1065 sendMessage(SCI_LINESCROLL, 0, -2);
1066 }
1067 }
1068
1069
1070
1071 void SciDoc::GoToPos(long pos)
1072 {
1073 sendMessage(SCI_GOTOPOS,pos,0);
1074 ScrollCaret(pos);
1075 }
1076
1077
1078
1079 void SciDoc::ScrollWrappedInsert()
1080 {
1081 if (GetWordWrap()) {
1082 getApp()->runWhileEvents();
1083 sendMessage(SCI_SCROLLCARET,0,0);
1084 }
1085 }
1086
1087
1088
1089 void SciDoc::ZoomStep(int direction)
1090 {
1091 long msg=SCI_SETZOOM;
1092 long val=0;
1093 switch (direction) {
1094 case -2: { msg=SCI_SETZOOM; val=-10; break; }
1095 case -1: { msg=SCI_ZOOMOUT; break; }
1096 case 0: { msg=SCI_SETZOOM; val=0; break; }
1097 case 1: { msg=SCI_ZOOMIN; break; }
1098 case 2: { msg=SCI_SETZOOM; val=20; break; }
1099 }
1100 sendMessage(msg,val,0);
1101 if ((msg==SCI_ZOOMOUT)||(msg==SCI_ZOOMIN)) { sendMessage(msg,val,0); }
1102
1103 sendMessage(SCI_SCROLLCARET,0,0);
1104 if (ShowLineNumbers()) { ShowLineNumbers(true); }
1105 slave(ZoomStep,direction);
1106 }
1107
1108
1109
1110 void SciDoc::SetZoom(int zoom)
1111 {
1112 sendMessage(SCI_SETZOOM,zoom,0);
1113 slave(SetZoom,zoom);
1114 }
1115
1116
1117
1118 int SciDoc::GetZoom()
1119 {
1120 return sendMessage(SCI_GETZOOM,0,0);
1121 }
1122
1123
1124
1125 void SciDoc::ShowLineNumbers(bool showit)
1126 {
1127 if (showit) {
1128 sendMessage(SCI_SETMARGINWIDTHN, 0, sendString(SCI_TEXTWIDTH,STYLE_LINENUMBER, "99999"));
1129 } else {
1130 sendMessage(SCI_SETMARGINWIDTHN, 0, 0);
1131 }
1132 slave(ShowLineNumbers,showit);
1133 }
1134
1135
1136
1137 void SciDoc::ShowWhiteSpace(bool showit)
1138 {
1139 sendMessage(SCI_SETVIEWWS, showit?SCWS_VISIBLEALWAYS:SCWS_INVISIBLE, 0);
1140 sendMessage(SCI_SETVIEWEOL, showit? (showit&&SettingsBase::instance()->WhitespaceShowsEOL):0 , 0);
1141 slave(ShowWhiteSpace,showit);
1142 }
1143
1144
1145
1146 void SciDoc::SetUTF8(bool utf8)
1147 {
1148 _utf8=utf8;
1149 sendMessage(SCI_SETCODEPAGE, utf8 ? SC_CP_UTF8 : 0, 0);
1150 slave(SetUTF8,utf8);
1151 }
1152
1153
1154
1155 void SciDoc::GetSelection(CharacterRange &crange)
1156 {
1157 crange.cpMin = sendMessage(SCI_GETSELECTIONSTART,0,0);
1158 crange.cpMax = sendMessage(SCI_GETSELECTIONEND,0,0);
1159 }
1160
1161
1162
1163 void SciDoc::SetLineIndentation(int line, int indent)
1164 {
1165 if (indent < 0) { return; }
1166 sendMessage(SCI_BEGINUNDOACTION,0,0);
1167 CharacterRange crange;
1168 GetSelection(crange);
1169 int posBefore = sendMessage(SCI_GETLINEINDENTATION,line,0);
1170 sendMessage(SCI_SETLINEINDENTATION, line, indent);
1171 int posAfter = sendMessage(SCI_GETLINEINDENTATION,line,0);
1172 int posDifference = posAfter - posBefore;
1173 if (posAfter > posBefore) {
1174 if (crange.cpMin >= posBefore) { crange.cpMin += posDifference; }
1175 if (crange.cpMax >= posBefore) { crange.cpMax += posDifference; }
1176 } else {
1177 if (posAfter < posBefore) {
1178 if (crange.cpMin >= posAfter) {
1179 if (crange.cpMin >= posBefore) {
1180 crange.cpMin += posDifference;
1181 } else {
1182 crange.cpMin = posAfter;
1183 }
1184 }
1185 if (crange.cpMax >= posAfter) {
1186 if (crange.cpMax >= posBefore) {
1187 crange.cpMax += posDifference;
1188 } else {
1189 crange.cpMax = posAfter;
1190 }
1191 }
1192 }
1193 }
1194 sendMessage(SCI_SETSEL, crange.cpMin, crange.cpMax);
1195 sendMessage(SCI_ENDUNDOACTION,0,0);
1196 }
1197
1198
1199
1200 int SciDoc::GetLineLength(int line)
1201 {
1202 return sendMessage(SCI_GETLINEENDPOSITION, line, 0) - sendMessage(SCI_POSITIONFROMLINE, line, 0);
1203 }
1204
1205
1206
1207 long SciDoc::GetLineText(long linenum, FXString &text)
1208 {
1209 text="";
1210 if (linenum==-1) {
1211 long pos=sendMessage(SCI_GETCURRENTPOS,0,0);
1212 linenum=sendMessage(SCI_LINEFROMPOSITION,pos,0);
1213 }
1214 if ( (linenum>=GetLineCount()) || (linenum<0) ) {
1215 return -1;
1216 }
1217 long len=sendMessage(SCI_LINELENGTH,linenum,0);
1218 if (len>0) {
1219 text.length(len+1);
1220 sendString(SCI_GETLINE, linenum, &(text[0]));
1221 text[len]='\0';
1222 }
1223 return len;
1224 }
1225
1226
1227
1228 int SciDoc::Stale() {
1229 if (!check_stale) { return 0; }
1230 if (_filename.empty()) { return 0; }
1231 FXStat info;
1232 if (!FXStat::statFile(_filename,info)) {
1233 _lasterror=SystemErrorStr();
1234 return 2;
1235 }
1236 return ((info.modified() != _filetime))?1:0;
1237 }
1238
1239
1240
1241 void SciDoc::SetSplit(FXint style)
1242 {
1243 FXSplitter*sp=(FXSplitter*)getParent();
1244 if (style==splitter_style) { return; }
1245 splitter_style=style;
1246 switch (style) {
1247 case SPLIT_NONE: {
1248 delete getNext();
1249 return;
1250 }
1251 case SPLIT_BESIDE: {
1252 sp->setSplitterStyle(SPLITTER_HORIZONTAL);
1253 sp->setSplit(0, sp->getWidth()/2);
1254 break;
1255 }
1256 case SPLIT_BELOW: {
1257 sp->setSplitterStyle(SPLITTER_VERTICAL);
1258 sp->setSplit(0, sp->getHeight()/2);
1259 break;
1260 }
1261 }
1262 if (sp->numChildren()==1) {
1263 SciDoc*sci=new SciDoc(sp,target,message);
1264 sci->create();
1265 sci->sendMessage(SCI_SETDOCPOINTER, 0, sendMessage(SCI_GETDOCPOINTER, 0, 0));
1266 sci->setLanguage(getLanguage());
1267 sci->ShowLineNumbers(ShowLineNumbers());
1268 sci->ShowWhiteSpace(ShowWhiteSpace());
1269 sci->GoToPos(GetCaretPos());
1270 sci->sendMessage(SCI_SETZOOM,sendMessage(SCI_GETZOOM,0,0),0);
1271 sci->SetShowEdge(GetShowEdge());
1272 sci->SetWordWrap(GetWordWrap());
1273 }
1274 }
1275
1276
1277
1278 void SciDoc::SmartHome(bool smart)
1279 {
1280 smart_home=smart;
1281 sendMessage(SCI_ASSIGNCMDKEY,SCK_HOME,smart?SCI_VCHOME:SCI_HOME);
1282 sendMessage(SCI_ASSIGNCMDKEY,SCK_HOME+(SCMOD_SHIFT<<16),smart?SCI_VCHOMEEXTEND:SCI_HOMEEXTEND);
1283 sendMessage(SCI_ASSIGNCMDKEY,
1284 SCK_HOME+(SCMOD_SHIFT<<16)+(SCMOD_ALT<<16), smart?SCI_VCHOMERECTEXTEND:SCI_HOMERECTEXTEND);
1285 }
1286
1287
1288
1289 // Strange things can happen when we allow grouping of undo actions by end-users.
1290 // For example, if a Lua script calls SCI_BEGINUNDOACTION and forgets to call
1291 // SCI_ENDUNDOACTION, or if the script terminates abnormally, the document might
1292 // continue collecting actions that can't be individually un-done. To help prevent
1293 // such situations, all script-generated calls to affect undo grouping should be
1294 // made through this method:
1295 void SciDoc::SetUserUndoLevel(FXint action)
1296 {
1297 switch (action) {
1298 case -1: { // pop one undo level
1299 if (user_undo_level>0) {
1300 user_undo_level--;
1301 sendMessage(SCI_ENDUNDOACTION, 0, 0);
1302 }
1303 break;
1304 }
1305 case 0: { // pop all undo levels
1306 while (user_undo_level>0) {
1307 user_undo_level--;
1308 sendMessage(SCI_ENDUNDOACTION, 0, 0);
1309 }
1310 break;
1311 }
1312 case 1: { // push one undo level
1313 if (user_undo_level<16) {
1314 user_undo_level++;
1315 sendMessage(SCI_BEGINUNDOACTION, 0, 0);
1316 }
1317 break;
1318 }
1319 }
1320 }
1321
1322
1323
1324 void SciDoc::SetProperty(const char*key, const char*value)
1325 {
1326 sendMessage(SCI_SETPROPERTY, reinterpret_cast<long>(key), reinterpret_cast<long>(value));
1327 }
1328
1329
1330
1331 // Lookup the property 'key' and place the result in 'value'.
1332 // If 'expanded' is true "keyword replacement" will be performed as described
1333 // in the Scintilla documentation. Returns true if the key is found, otherwise
1334 // it sets 'value' to an empty string and returns false.
1335 bool SciDoc::GetProperty(const FXString &key, FXString &value, bool expanded)
1336 {
1337 int gp=expanded?SCI_GETPROPERTYEXPANDED:SCI_GETPROPERTY;
1338 int len=sendMessage(gp,reinterpret_cast<long>(key.text()),0);
1339 if (len>0) {
1340 value.length(len+1);
1341 sendMessage(gp,reinterpret_cast<long>(key.text()),reinterpret_cast<long>(value.text()));
1342 return true;
1343 } else {
1344 value="";
1345 return false;
1346 }
1347 }
1348
1349
1350
1351 // Lookup the integer property 'key' and return its value.
1352 // If 'key' is not found, return 'default_value'
1353 // If 'key' is found but is not an integer, return zero.
1354 int SciDoc::GetPropertyInt(const char*key, int default_value)
1355 {
1356 return sendMessage(SCI_GETPROPERTYINT, reinterpret_cast<long>(key), default_value);
1357 }
1358
1359
1360
1361 void SciDoc::TabWidth(int w) {
1362 if (_lang && (_lang->tabwidth>0)) { w=_lang->tabwidth; }
1363 sendMessage(SCI_SETTABWIDTH,(w<1)?1:(w>16)?16:w,0);
1364 }
1365
1366
1367
1368 void SciDoc::SetWordWrap(bool on)
1369 {
1370 sendMessage(SCI_SETWRAPMODE,on?SC_WRAP_WORD:SC_WRAP_NONE,0);
1371 setScrollStyle(on?
1372 ((getScrollStyle()&~HSCROLLER_ALWAYS)|HSCROLLER_NEVER):
1373 ((getScrollStyle()&~HSCROLLER_NEVER)|HSCROLLER_ALWAYS)
1374 );
1375 slave(SetWordWrap,on);
1376 }
1377
1378
1379
1380 bool SciDoc::GetWordWrap()
1381 {
1382 return sendMessage(SCI_GETWRAPMODE,0,0)!=SC_WRAP_NONE;
1383 }
1384
1385
1386 #define LongFromTwoShorts(a,b) ( (a) | ((b) << 16) )
1387
1388
1389 #define AssignKey(key,mods,cmd) sendMessage(SCI_ASSIGNCMDKEY, \
1390 LongFromTwoShorts(static_cast<short>(key), \
1391 static_cast<short>(mods)), cmd);
1392
1393 void SciDoc::SetWrapAware(bool aware)
1394 {
1395 if (aware) {
1396 if (smart_home) {
1397 AssignKey(SCK_HOME, 0, SCI_VCHOMEWRAP);
1398 AssignKey(SCK_HOME, SCMOD_SHIFT, SCI_VCHOMEWRAPEXTEND);
1399 AssignKey(SCK_HOME, SCMOD_SHIFT|SCMOD_ALT, SCI_VCHOMERECTEXTEND);
1400 } else {
1401 AssignKey(SCK_HOME, 0, SCI_HOMEWRAP);
1402 AssignKey(SCK_HOME, SCMOD_SHIFT, SCI_HOMEWRAPEXTEND);
1403 AssignKey(SCK_HOME, SCMOD_SHIFT|SCMOD_ALT, SCI_HOMERECTEXTEND);
1404 }
1405 AssignKey(SCK_END, 0, SCI_LINEENDWRAP);
1406 AssignKey(SCK_END, SCMOD_SHIFT, SCI_LINEENDWRAPEXTEND);
1407 } else {
1408 if (smart_home) {
1409 AssignKey(SCK_HOME, 0, SCI_VCHOME);
1410 AssignKey(SCK_HOME, SCMOD_SHIFT, SCI_VCHOMEEXTEND);
1411 AssignKey(SCK_HOME, SCMOD_SHIFT|SCMOD_ALT, SCI_VCHOMERECTEXTEND);
1412 } else {
1413 AssignKey(SCK_HOME, 0, SCI_HOME);
1414 AssignKey(SCK_HOME, SCMOD_SHIFT, SCI_HOMEEXTEND);
1415 AssignKey(SCK_HOME, SCMOD_SHIFT|SCMOD_ALT, SCI_HOMERECTEXTEND);
1416 }
1417 AssignKey(SCK_END, 0, SCI_LINEEND);
1418 AssignKey(SCK_END, SCMOD_SHIFT, SCI_LINEENDEXTEND);
1419 }
1420 }
1421
1422
1423
1424 void SciDoc::SetCaseOfSelection(int msg)
1425 {
1426 FXString seltext;
1427 long anchor=sendMessage(SCI_GETANCHOR,0,0);
1428 long currentpos=sendMessage(SCI_GETCURRENTPOS,0,0);
1429 long selcount=sendMessage(SCI_GETSELECTIONS,0,0);
1430 sendMessage(SCI_BEGINUNDOACTION, 0, 0);
1431 for (long selnum=0; selnum<selcount; selnum++) {
1432 Sci_TextRange trng;
1433 trng.chrg.cpMin=sendMessage(SCI_GETSELECTIONNSTART,selnum,0);
1434 trng.chrg.cpMax=sendMessage(SCI_GETSELECTIONNEND,selnum,0);
1435 seltext.length((trng.chrg.cpMax-trng.chrg.cpMin)+1);
1436 trng.lpstrText=&seltext[0];
1437 sendMessage(SCI_GETTEXTRANGE,0,reinterpret_cast<long>(&trng));
1438 seltext.length((trng.chrg.cpMax-trng.chrg.cpMin));
1439 switch (msg) {
1440 case SCI_UPPERCASE: {
1441 seltext.upper();
1442 break;
1443 }
1444 case SCI_LOWERCASE: {
1445 seltext.lower();
1446 break;
1447 }
1448 }
1449 sendMessage(SCI_SETTARGETSTART,trng.chrg.cpMin,0);
1450 sendMessage(SCI_SETTARGETEND,trng.chrg.cpMax,0);
1451 sendString(SCI_REPLACETARGET,seltext.length(),seltext.text());
1452 }
1453 sendMessage(SCI_SETCURRENTPOS,currentpos,0);
1454 sendMessage(SCI_SETANCHOR,anchor,0);
1455 sendMessage(SCI_ENDUNDOACTION, 0, 0);
1456 }
1457
1458
1459
1460 void SciDoc::SelectionToUpper()
1461 {
1462 SetCaseOfSelection(SCI_UPPERCASE);
1463 }
1464
1465
1466
1467 void SciDoc::SelectionToLower()
1468 {
1469 SetCaseOfSelection(SCI_LOWERCASE);
1470 }
1471
1472
1473
1474 void SciDoc::EnableRecorder(bool enable_recorder)
1475 {
1476 if (enable_recorder) {
1477 sendMessage(SCI_STARTRECORD, 0, 0);
1478 } else {
1479 sendMessage(SCI_STOPRECORD, 0, 0);
1480 }
1481 recording=enable_recorder;
1482 }
1483
1484
1485
1486 long SciDoc::onRecordReplace(FXObject *o, FXSelector sel, void *p)
1487 {
1488 if (!recording) { return 1;}
1489 if (target) { target->handle(this,FXSEL(SEL_COMMAND,message), p); }
1490 return 1;
1491 }
1492
1493