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