Blender V2.61 - r43446

text.c

Go to the documentation of this file.
00001 /*
00002  * ***** BEGIN GPL LICENSE BLOCK *****
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
00019  * All rights reserved.
00020  *
00021  * The Original Code is: all of this file.
00022  *
00023  * Contributor(s): none yet.
00024  *
00025  * ***** END GPL LICENSE BLOCK *****
00026  */
00027 
00033 #include <string.h> /* strstr */
00034 #include <sys/types.h>
00035 #include <sys/stat.h>
00036 #include <wchar.h>
00037 #include <wctype.h>
00038 
00039 #include "MEM_guardedalloc.h"
00040 
00041 #include "BLI_blenlib.h"
00042 #include "BLI_utildefines.h"
00043 
00044 #include "DNA_constraint_types.h"
00045 #include "DNA_controller_types.h"
00046 #include "DNA_scene_types.h"
00047 #include "DNA_screen_types.h"
00048 #include "DNA_space_types.h"
00049 #include "DNA_text_types.h"
00050 #include "DNA_userdef_types.h"
00051 #include "DNA_object_types.h"
00052 
00053 #include "BKE_depsgraph.h"
00054 #include "BKE_global.h"
00055 #include "BKE_library.h"
00056 #include "BKE_main.h"
00057 #include "BKE_text.h"
00058 
00059 
00060 #ifdef WITH_PYTHON
00061 #include "BPY_extern.h"
00062 #endif
00063 
00064 /***************/ /*
00065 
00066 How Texts should work
00067 --
00068 A text should relate to a file as follows -
00069 (Text *)->name should be the place where the 
00070     file will or has been saved.
00071     
00072 (Text *)->flags has the following bits
00073     TXT_ISDIRTY - should always be set if the file in mem. differs from
00074                     the file on disk, or if there is no file on disk.
00075     TXT_ISMEM - should always be set if the Text has not been mapped to
00076                     a file, in which case (Text *)->name may be NULL or garbage.            
00077     TXT_ISEXT - should always be set if the Text is not to be written into
00078                     the .blend
00079     TXT_ISSCRIPT - should be set if the user has designated the text
00080                     as a script. (NEW: this was unused, but now it is needed by
00081                     space handler script links (see header_view3d.c, for example)
00082 
00083 ->>> see also: /makesdna/DNA_text_types.h
00084 
00085 Display
00086 --
00087 The st->top determines at what line the top of the text is displayed.
00088 If the user moves the cursor the st containing that cursor should
00089 be popped ... other st's retain their own top location.
00090 
00091 Markers
00092 --
00093 The mrk->flags define the behaviour and relationships between markers. The
00094 upper two bytes are used to hold a group ID, the lower two are normal flags. If
00095 TMARK_EDITALL is set the group ID defines which other markers should be edited.
00096 
00097 The mrk->clr field is used to visually group markers where the flags may not
00098 match. A template system, for example, may allow editing of repeating tokens
00099 (in one group) but include other marked positions (in another group) all in the
00100 same template with the same color.
00101 
00102 Undo
00103 --
00104 Undo/Redo works by storing
00105 events in a queue, and a pointer
00106 to the current position in the
00107 queue...
00108 
00109 Events are stored using an
00110 arbitrary op-code system
00111 to keep track of
00112 a) the two cursors (normal and selected)
00113 b) input (visible and control (ie backspace))
00114 
00115 input data is stored as its
00116 ASCII value, the opcodes are
00117 then selected to not conflict.
00118 
00119 opcodes with data in between are
00120 written at the beginning and end
00121 of the data to allow undo and redo
00122 to simply check the code at the current
00123 undo position
00124 
00125 */ /***************/
00126 
00127 /***/
00128 
00129 static void txt_pop_first(Text *text);
00130 static void txt_pop_last(Text *text);
00131 static void txt_undo_add_op(Text *text, int op);
00132 static void txt_undo_add_block(Text *text, int op, const char *buf);
00133 static void txt_delete_line(Text *text, TextLine *line);
00134 static void txt_delete_sel (Text *text);
00135 static void txt_make_dirty (Text *text);
00136 
00137 /***/
00138 
00139 static unsigned char undoing;
00140 
00141 /* allow to switch off undoing externally */
00142 void txt_set_undostate(int u)
00143 {
00144     undoing = u;
00145 }
00146 
00147 int txt_get_undostate(void)
00148 {
00149     return undoing;
00150 }
00151 
00152 static void init_undo_text(Text *text)
00153 {
00154     text->undo_pos= -1;
00155     text->undo_len= TXT_INIT_UNDO;
00156     text->undo_buf= MEM_mallocN(text->undo_len, "undo buf");
00157 }
00158 
00159 void free_text(Text *text)
00160 {
00161     TextLine *tmp;
00162 
00163     for (tmp= text->lines.first; tmp; tmp= tmp->next) {
00164         MEM_freeN(tmp->line);
00165         if (tmp->format)
00166           MEM_freeN(tmp->format);
00167     }
00168     
00169     BLI_freelistN(&text->lines);
00170     BLI_freelistN(&text->markers);
00171 
00172     if(text->name) MEM_freeN(text->name);
00173     MEM_freeN(text->undo_buf);
00174 #ifdef WITH_PYTHON
00175     if (text->compiled) BPY_text_free_code(text);
00176 #endif
00177 }
00178 
00179 Text *add_empty_text(const char *name) 
00180 {
00181     Main *bmain= G.main;
00182     Text *ta;
00183     TextLine *tmp;
00184     
00185     ta= alloc_libblock(&bmain->text, ID_TXT, name);
00186     ta->id.us= 1;
00187     
00188     ta->name= NULL;
00189 
00190     init_undo_text(ta);
00191 
00192     ta->nlines=1;
00193     ta->flags= TXT_ISDIRTY | TXT_ISMEM;
00194     if((U.flag & USER_TXT_TABSTOSPACES_DISABLE)==0)
00195         ta->flags |= TXT_TABSTOSPACES;
00196 
00197     ta->lines.first= ta->lines.last= NULL;
00198     ta->markers.first= ta->markers.last= NULL;
00199 
00200     tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
00201     tmp->line= (char*) MEM_mallocN(1, "textline_string");
00202     tmp->format= NULL;
00203     
00204     tmp->line[0]=0;
00205     tmp->len= 0;
00206                 
00207     tmp->next= NULL;
00208     tmp->prev= NULL;
00209                 
00210     BLI_addhead(&ta->lines, tmp);
00211     
00212     ta->curl= ta->lines.first;
00213     ta->curc= 0;
00214     ta->sell= ta->lines.first;
00215     ta->selc= 0;
00216 
00217     return ta;
00218 }
00219 
00220 /* this function replaces extended ascii characters */
00221 /* to a valid utf-8 sequences */
00222 int txt_extended_ascii_as_utf8(char **str)
00223 {
00224     int bad_char, added= 0, i= 0;
00225     int length = strlen(*str);
00226 
00227     while ((*str)[i]) {
00228         if((bad_char= BLI_utf8_invalid_byte(*str+i, length)) == -1)
00229             break;
00230 
00231         added++;
00232         i+= bad_char + 1;
00233     }
00234     
00235     if (added != 0) {
00236         char *newstr = MEM_mallocN(length+added+1, "text_line");
00237         int mi = 0;
00238         i= 0;
00239         
00240         while ((*str)[i]) {
00241             if((bad_char= BLI_utf8_invalid_byte((*str)+i, length)) == -1) {
00242                 memcpy(newstr+mi, (*str)+i, length - i + 1);
00243                 break;
00244             }
00245             
00246             memcpy(newstr+mi, (*str)+i, bad_char);
00247 
00248             BLI_str_utf8_from_unicode((*str)[i+bad_char], newstr+mi+bad_char);
00249             i+= bad_char+1;
00250             mi+= bad_char+2;
00251         }
00252         newstr[length+added] = '\0';
00253         MEM_freeN(*str);
00254         *str = newstr;
00255     }
00256     
00257     return added;
00258 }
00259 
00260 // this function removes any control characters from
00261 // a textline and fixes invalid utf-8 sequences
00262 
00263 static void cleanup_textline(TextLine * tl)
00264 {
00265     int i;
00266 
00267     for (i = 0; i < tl->len; i++ ) {
00268         if (tl->line[i] < ' ' && tl->line[i] != '\t') {
00269             memmove(tl->line + i, tl->line + i + 1, tl->len - i);
00270             tl->len--;
00271             i--;
00272         }
00273     }
00274     tl->len+= txt_extended_ascii_as_utf8(&tl->line);
00275 }
00276 
00277 int reopen_text(Text *text)
00278 {
00279     FILE *fp;
00280     int i, llen, len;
00281     unsigned char *buffer;
00282     TextLine *tmp;
00283     char str[FILE_MAX];
00284     struct stat st;
00285 
00286     if (!text || !text->name) return 0;
00287     
00288     BLI_strncpy(str, text->name, FILE_MAX);
00289     BLI_path_abs(str, G.main->name);
00290     
00291     fp= fopen(str, "r");
00292     if(fp==NULL) return 0;
00293 
00294     /* free memory: */
00295 
00296     for (tmp= text->lines.first; tmp; tmp= tmp->next) {
00297         MEM_freeN(tmp->line);
00298         if (tmp->format) MEM_freeN(tmp->format);
00299     }
00300     
00301     BLI_freelistN(&text->lines);
00302 
00303     text->lines.first= text->lines.last= NULL;
00304     text->curl= text->sell= NULL;
00305 
00306     /* clear undo buffer */
00307     MEM_freeN(text->undo_buf);
00308     init_undo_text(text);
00309     
00310     fseek(fp, 0L, SEEK_END);
00311     len= ftell(fp);
00312     fseek(fp, 0L, SEEK_SET);    
00313 
00314     text->undo_pos= -1;
00315     
00316     buffer= MEM_mallocN(len, "text_buffer");
00317     // under windows fread can return less then len bytes because
00318     // of CR stripping
00319     len = fread(buffer, 1, len, fp);
00320 
00321     fclose(fp);
00322 
00323     stat(str, &st);
00324     text->mtime= st.st_mtime;
00325     
00326     text->nlines=0;
00327     llen=0;
00328     for(i=0; i<len; i++) {
00329         if (buffer[i]=='\n') {
00330             tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
00331             tmp->line= (char*) MEM_mallocN(llen+1, "textline_string");
00332             tmp->format= NULL;
00333             
00334             if(llen) memcpy(tmp->line, &buffer[i-llen], llen);
00335             tmp->line[llen]=0;
00336             tmp->len= llen;
00337                 
00338             cleanup_textline(tmp);
00339 
00340             BLI_addtail(&text->lines, tmp);
00341             text->nlines++;
00342                 
00343             llen=0;
00344             continue;
00345         }
00346         llen++;
00347     }
00348 
00349     if (llen!=0 || text->nlines==0) {
00350         tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
00351         tmp->line= (char*) MEM_mallocN(llen+1, "textline_string");
00352         tmp->format= NULL;
00353         
00354         if(llen) memcpy(tmp->line, &buffer[i-llen], llen);
00355 
00356         tmp->line[llen]=0;
00357         tmp->len= llen;
00358         
00359         cleanup_textline(tmp);
00360 
00361         BLI_addtail(&text->lines, tmp);
00362         text->nlines++;
00363     }
00364     
00365     text->curl= text->sell= text->lines.first;
00366     text->curc= text->selc= 0;
00367     
00368     MEM_freeN(buffer);  
00369     return 1;
00370 }
00371 
00372 Text *add_text(const char *file, const char *relpath) 
00373 {
00374     Main *bmain= G.main;
00375     FILE *fp;
00376     int i, llen, len;
00377     unsigned char *buffer;
00378     TextLine *tmp;
00379     Text *ta;
00380     char str[FILE_MAX];
00381     struct stat st;
00382 
00383     BLI_strncpy(str, file, FILE_MAX);
00384     if (relpath) /* can be NULL (bg mode) */
00385         BLI_path_abs(str, relpath);
00386     
00387     fp= fopen(str, "r");
00388     if(fp==NULL) return NULL;
00389     
00390     ta= alloc_libblock(&bmain->text, ID_TXT, BLI_path_basename(str));
00391     ta->id.us= 1;
00392 
00393     ta->lines.first= ta->lines.last= NULL;
00394     ta->markers.first= ta->markers.last= NULL;
00395     ta->curl= ta->sell= NULL;
00396 
00397     if((U.flag & USER_TXT_TABSTOSPACES_DISABLE)==0)
00398         ta->flags= TXT_TABSTOSPACES;
00399 
00400     fseek(fp, 0L, SEEK_END);
00401     len= ftell(fp);
00402     fseek(fp, 0L, SEEK_SET);    
00403 
00404     ta->name= MEM_mallocN(strlen(file)+1, "text_name");
00405     strcpy(ta->name, file);
00406 
00407     init_undo_text(ta);
00408     
00409     buffer= MEM_mallocN(len, "text_buffer");
00410     // under windows fread can return less then len bytes because
00411     // of CR stripping
00412     len = fread(buffer, 1, len, fp);
00413 
00414     fclose(fp);
00415 
00416     stat(str, &st);
00417     ta->mtime= st.st_mtime;
00418     
00419     ta->nlines=0;
00420     llen=0;
00421     for(i=0; i<len; i++) {
00422         if (buffer[i]=='\n') {
00423             tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
00424             tmp->line= (char*) MEM_mallocN(llen+1, "textline_string");
00425             tmp->format= NULL;
00426             
00427             if(llen) memcpy(tmp->line, &buffer[i-llen], llen);
00428             tmp->line[llen]=0;
00429             tmp->len= llen;
00430             
00431             cleanup_textline(tmp);
00432 
00433             BLI_addtail(&ta->lines, tmp);
00434             ta->nlines++;
00435                 
00436             llen=0;
00437             continue;
00438         }
00439         llen++;
00440     }
00441 
00442     /* create new line in cases:
00443        - rest of line (if last line in file hasn't got \n terminator).
00444          in this case content of such line would be used to fill text line buffer
00445        - file is empty. in this case new line is needed to start editing from.
00446        - last characted in buffer is \n. in this case new line is needed to
00447          deal with newline at end of file. (see [#28087]) (sergey) */
00448     if (llen!=0 || ta->nlines==0 || buffer[len-1]=='\n') {
00449         tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
00450         tmp->line= (char*) MEM_mallocN(llen+1, "textline_string");
00451         tmp->format= NULL;
00452         
00453         if(llen) memcpy(tmp->line, &buffer[i-llen], llen);
00454 
00455         tmp->line[llen]=0;
00456         tmp->len= llen;
00457         
00458         cleanup_textline(tmp);
00459 
00460         BLI_addtail(&ta->lines, tmp);
00461         ta->nlines++;
00462     }
00463     
00464     ta->curl= ta->sell= ta->lines.first;
00465     ta->curc= ta->selc= 0;
00466     
00467     MEM_freeN(buffer);  
00468 
00469     return ta;
00470 }
00471 
00472 Text *copy_text(Text *ta)
00473 {
00474     Text *tan;
00475     TextLine *line, *tmp;
00476     
00477     tan= copy_libblock(&ta->id);
00478     
00479     /* file name can be NULL */
00480     if(ta->name) {
00481         tan->name= MEM_mallocN(strlen(ta->name)+1, "text_name");
00482         strcpy(tan->name, ta->name);
00483     }
00484     else {
00485         tan->name= NULL;
00486     }
00487 
00488     tan->flags = ta->flags | TXT_ISDIRTY;
00489     
00490     tan->lines.first= tan->lines.last= NULL;
00491     tan->markers.first= tan->markers.last= NULL;
00492     tan->curl= tan->sell= NULL;
00493     
00494     tan->nlines= ta->nlines;
00495 
00496     line= ta->lines.first;  
00497     /* Walk down, reconstructing */
00498     while (line) {
00499         tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
00500         tmp->line= MEM_mallocN(line->len+1, "textline_string");
00501         tmp->format= NULL;
00502         
00503         strcpy(tmp->line, line->line);
00504 
00505         tmp->len= line->len;
00506         
00507         BLI_addtail(&tan->lines, tmp);
00508         
00509         line= line->next;
00510     }
00511 
00512     tan->curl= tan->sell= tan->lines.first;
00513     tan->curc= tan->selc= 0;
00514 
00515     init_undo_text(tan);
00516 
00517     return tan;
00518 }
00519 
00520 void unlink_text(Main *bmain, Text *text)
00521 {
00522     bScreen *scr;
00523     ScrArea *area;
00524     SpaceLink *sl;
00525     Object *ob;
00526     bController *cont;
00527     bConstraint *con;
00528     short update;
00529 
00530     for(ob=bmain->object.first; ob; ob=ob->id.next) {
00531         /* game controllers */
00532         for(cont=ob->controllers.first; cont; cont=cont->next) {
00533             if(cont->type==CONT_PYTHON) {
00534                 bPythonCont *pc;
00535                 
00536                 pc= cont->data;
00537                 if(pc->text==text) pc->text= NULL;
00538             }
00539         }
00540 
00541         /* pyconstraints */
00542         update = 0;
00543 
00544         if(ob->type==OB_ARMATURE && ob->pose) {
00545             bPoseChannel *pchan;
00546             for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
00547                 for(con = pchan->constraints.first; con; con=con->next) {
00548                     if(con->type==CONSTRAINT_TYPE_PYTHON) {
00549                         bPythonConstraint *data = con->data;
00550                         if (data->text==text) data->text = NULL;
00551                         update = 1;
00552                         
00553                     }
00554                 }
00555             }
00556         }
00557 
00558         for(con = ob->constraints.first; con; con=con->next) {
00559             if(con->type==CONSTRAINT_TYPE_PYTHON) {
00560                 bPythonConstraint *data = con->data;
00561                 if (data->text==text) data->text = NULL;
00562                 update = 1;
00563             }
00564         }
00565         
00566         if(update)
00567             DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
00568     }
00569 
00570     /* pynodes */
00571     // XXX nodeDynamicUnlinkText(&text->id);
00572     
00573     /* text space */
00574     for(scr= bmain->screen.first; scr; scr= scr->id.next) {
00575         for(area= scr->areabase.first; area; area= area->next) {
00576             for(sl= area->spacedata.first; sl; sl= sl->next) {
00577                 if(sl->spacetype==SPACE_TEXT) {
00578                     SpaceText *st= (SpaceText*) sl;
00579                     
00580                     if(st->text==text) {
00581                         st->text= NULL;
00582                         st->top= 0;
00583                     }
00584                 }
00585             }
00586         }
00587     }
00588 
00589     text->id.us= 0;
00590 }
00591 
00592 void clear_text(Text *text) /* called directly from rna */
00593 {
00594     int oldstate;
00595 
00596     oldstate = txt_get_undostate(  );
00597     txt_set_undostate( 1 );
00598     txt_sel_all( text );
00599     txt_delete_sel(text);
00600     txt_set_undostate( oldstate );
00601 
00602     txt_make_dirty(text);
00603 }
00604 
00605 void write_text(Text *text, const char *str) /* called directly from rna */
00606 {
00607     int oldstate;
00608 
00609     oldstate = txt_get_undostate(  );
00610     txt_insert_buf( text, str );
00611     txt_move_eof( text, 0 );
00612     txt_set_undostate( oldstate );
00613 
00614     txt_make_dirty(text);
00615 }
00616 
00617 /*****************************/
00618 /* Editing utility functions */
00619 /*****************************/
00620 
00621 static void make_new_line (TextLine *line, char *newline) 
00622 {
00623     if (line->line) MEM_freeN(line->line);
00624     if (line->format) MEM_freeN(line->format);
00625     
00626     line->line= newline;
00627     line->len= strlen(newline);
00628     line->format= NULL;
00629 }
00630 
00631 static TextLine *txt_new_line(const char *str)
00632 {
00633     TextLine *tmp;
00634 
00635     if(!str) str= "";
00636     
00637     tmp= (TextLine *) MEM_mallocN(sizeof(TextLine), "textline");
00638     tmp->line= MEM_mallocN(strlen(str)+1, "textline_string");
00639     tmp->format= NULL;
00640     
00641     strcpy(tmp->line, str);
00642     
00643     tmp->len= strlen(str);
00644     tmp->next= tmp->prev= NULL;
00645     
00646     return tmp;
00647 }
00648 
00649 static TextLine *txt_new_linen(const char *str, int n)
00650 {
00651     TextLine *tmp;
00652 
00653     tmp= (TextLine *) MEM_mallocN(sizeof(TextLine), "textline");
00654     tmp->line= MEM_mallocN(n+1, "textline_string");
00655     tmp->format= NULL;
00656     
00657     BLI_strncpy(tmp->line, (str)? str: "", n+1);
00658     
00659     tmp->len= strlen(tmp->line);
00660     tmp->next= tmp->prev= NULL;
00661     
00662     return tmp;
00663 }
00664 
00665 void txt_clean_text (Text *text) 
00666 {   
00667     TextLine **top, **bot;
00668     
00669     if (!text) return;
00670     
00671     if (!text->lines.first) {
00672         if (text->lines.last) text->lines.first= text->lines.last;
00673         else text->lines.first= text->lines.last= txt_new_line(NULL);
00674     } 
00675     
00676     if (!text->lines.last) text->lines.last= text->lines.first;
00677 
00678     top= (TextLine **) &text->lines.first;
00679     bot= (TextLine **) &text->lines.last;
00680     
00681     while ((*top)->prev) *top= (*top)->prev;
00682     while ((*bot)->next) *bot= (*bot)->next;
00683 
00684     if(!text->curl) {
00685         if(text->sell) text->curl= text->sell;
00686         else text->curl= text->lines.first;
00687         text->curc= 0;
00688     }
00689 
00690     if(!text->sell) {
00691         text->sell= text->curl;
00692         text->selc= 0;
00693     }
00694 }
00695 
00696 int txt_get_span (TextLine *from, TextLine *to)
00697 {
00698     int ret=0;
00699     TextLine *tmp= from;
00700 
00701     if (!to || !from) return 0;
00702     if (from==to) return 0;
00703 
00704     /* Look forwards */
00705     while (tmp) {
00706         if (tmp == to) return ret;
00707         ret++;
00708         tmp= tmp->next;
00709     }
00710 
00711     /* Look backwards */
00712     if (!tmp) {
00713         tmp= from;
00714         ret=0;
00715         while(tmp) {
00716             if (tmp == to) break;
00717             ret--;
00718             tmp= tmp->prev;
00719         }
00720         if(!tmp) ret=0;
00721     }
00722 
00723     return ret; 
00724 }
00725 
00726 static void txt_make_dirty (Text *text)
00727 {
00728     text->flags |= TXT_ISDIRTY;
00729 #ifdef WITH_PYTHON
00730     if (text->compiled) BPY_text_free_code(text);
00731 #endif
00732 }
00733 
00734 /* 0:whitespace, 1:punct, 2:alphanumeric */
00735 static short txt_char_type(unsigned int ch)
00736 {
00737     if (iswspace(ch)) return 0;
00738     if (iswalpha(ch) || iswdigit(ch)) return 2;
00739     return 1;
00740 }
00741 
00742 /****************************/
00743 /* Cursor utility functions */
00744 /****************************/
00745 
00746 static void txt_curs_cur (Text *text, TextLine ***linep, int **charp)
00747 {
00748     *linep= &text->curl; *charp= &text->curc;
00749 }
00750 
00751 static void txt_curs_sel (Text *text, TextLine ***linep, int **charp)
00752 {
00753     *linep= &text->sell; *charp= &text->selc;
00754 }
00755 
00756 static void txt_curs_first (Text *text, TextLine **linep, int *charp)
00757 {
00758     if (text->curl==text->sell) {
00759         *linep= text->curl;
00760         if (text->curc<text->selc) *charp= text->curc;
00761         else *charp= text->selc;
00762     } else if (txt_get_span(text->lines.first, text->curl)<txt_get_span(text->lines.first, text->sell)) {
00763         *linep= text->curl;
00764         *charp= text->curc;
00765     } else {
00766         *linep= text->sell;
00767         *charp= text->selc;     
00768     }
00769 }
00770 
00771 /*****************************/
00772 /* Cursor movement functions */
00773 /*****************************/
00774 
00775 int txt_utf8_offset_to_index(char *str, int offset)
00776 {
00777     int index= 0, pos= 0;
00778     while (pos != offset) {
00779         pos += BLI_str_utf8_size(str + pos);
00780         index++;
00781     }
00782     return index;
00783 }
00784 
00785 int txt_utf8_index_to_offset(char *str, int index)
00786 {
00787     int offset= 0, pos= 0;
00788     while (pos != index) {
00789         offset += BLI_str_utf8_size(str + offset);
00790         pos++;
00791     }
00792     return offset;
00793 }
00794 
00795 /* returns the real number of characters in string */
00796 /* not the same as BLI_strlen_utf8, which returns length for wide characters */
00797 static int txt_utf8_len(const char *src)
00798 {
00799     int len;
00800 
00801     for (len=0; *src; len++) {
00802         src += BLI_str_utf8_size(src);
00803     }
00804 
00805     return len;
00806 }
00807 
00808 void txt_move_up(Text *text, short sel)
00809 {
00810     TextLine **linep;
00811     int *charp, old;
00812     
00813     if (!text) return;
00814     if(sel) txt_curs_sel(text, &linep, &charp);
00815     else { txt_pop_first(text); txt_curs_cur(text, &linep, &charp); }
00816     if (!*linep) return;
00817     old= *charp;
00818 
00819     if((*linep)->prev) {
00820         int index = txt_utf8_offset_to_index((*linep)->line, *charp);
00821         *linep= (*linep)->prev;
00822         if (index > txt_utf8_len((*linep)->line)) *charp= (*linep)->len;
00823         else *charp= txt_utf8_index_to_offset((*linep)->line, index);
00824         
00825         if(!undoing)
00826             txt_undo_add_op(text, sel?UNDO_SUP:UNDO_CUP);
00827     } else {
00828         txt_move_bol(text, sel);
00829     }
00830 
00831     if(!sel) txt_pop_sel(text);
00832 }
00833 
00834 void txt_move_down(Text *text, short sel) 
00835 {
00836     TextLine **linep;
00837     int *charp, old;
00838     
00839     if (!text) return;
00840     if(sel) txt_curs_sel(text, &linep, &charp);
00841     else { txt_pop_last(text); txt_curs_cur(text, &linep, &charp); }
00842     if (!*linep) return;
00843     old= *charp;
00844 
00845     if((*linep)->next) {
00846         int index = txt_utf8_offset_to_index((*linep)->line, *charp);
00847         *linep= (*linep)->next;
00848         if (index > txt_utf8_len((*linep)->line)) *charp= (*linep)->len;
00849         else *charp= txt_utf8_index_to_offset((*linep)->line, index);
00850         
00851         if(!undoing)
00852             txt_undo_add_op(text, sel?UNDO_SDOWN:UNDO_CDOWN);
00853     } else {
00854         txt_move_eol(text, sel);
00855     }
00856 
00857     if(!sel) txt_pop_sel(text);
00858 }
00859 
00860 void txt_move_left(Text *text, short sel) 
00861 {
00862     TextLine **linep;
00863     int *charp, oundoing= undoing;
00864     int tabsize= 0, i= 0;
00865     
00866     if (!text) return;
00867     if(sel) txt_curs_sel(text, &linep, &charp);
00868     else { txt_pop_first(text); txt_curs_cur(text, &linep, &charp); }
00869     if (!*linep) return;
00870 
00871     undoing= 1;
00872 
00873     if (*charp== 0) {
00874         if ((*linep)->prev) {
00875             txt_move_up(text, sel);
00876             *charp= (*linep)->len;
00877         }
00878     }
00879     else {
00880         // do nice left only if there are only spaces
00881         // TXT_TABSIZE hardcoded in DNA_text_types.h
00882         if (text->flags & TXT_TABSTOSPACES) {
00883             tabsize= (*charp < TXT_TABSIZE) ? *charp : TXT_TABSIZE;
00884             
00885             for (i=0; i<(*charp); i++)
00886                 if ((*linep)->line[i] != ' ') {
00887                     tabsize= 0;
00888                     break;
00889                 }
00890             
00891             // if in the middle of the space-tab
00892             if (tabsize && (*charp) % TXT_TABSIZE != 0)
00893                 tabsize= ((*charp) % TXT_TABSIZE);
00894         }
00895         
00896         if (tabsize)
00897             (*charp)-= tabsize;
00898         else {
00899             const char *prev= BLI_str_prev_char_utf8((*linep)->line + *charp);
00900             *charp= prev - (*linep)->line;
00901         }
00902     }
00903 
00904     undoing= oundoing;
00905     if(!undoing) txt_undo_add_op(text, sel?UNDO_SLEFT:UNDO_CLEFT);
00906     
00907     if(!sel) txt_pop_sel(text);
00908 }
00909 
00910 void txt_move_right(Text *text, short sel) 
00911 {
00912     TextLine **linep;
00913     int *charp, oundoing= undoing, do_tab= 0, i;
00914     
00915     if (!text) return;
00916     if(sel) txt_curs_sel(text, &linep, &charp);
00917     else { txt_pop_last(text); txt_curs_cur(text, &linep, &charp); }
00918     if (!*linep) return;
00919 
00920     undoing= 1;
00921 
00922     if (*charp== (*linep)->len) {
00923         if ((*linep)->next) {
00924             txt_move_down(text, sel);
00925             *charp= 0;
00926         }
00927     } 
00928     else {
00929         // do nice right only if there are only spaces
00930         // spaces hardcoded in DNA_text_types.h
00931         if (text->flags & TXT_TABSTOSPACES && (*linep)->line[*charp]== ' ') {
00932             do_tab= 1;
00933             for (i=0; i<*charp; i++)
00934                 if ((*linep)->line[i]!= ' ') {
00935                     do_tab= 0;
00936                     break;
00937                 }
00938         }
00939         
00940         if (do_tab) {
00941             int tabsize= (*charp) % TXT_TABSIZE + 1;
00942             for (i=*charp+1; (*linep)->line[i]==' ' && tabsize<TXT_TABSIZE; i++)
00943                 tabsize++;
00944             (*charp)= i;
00945         }
00946         else (*charp)+= BLI_str_utf8_size((*linep)->line + *charp);
00947     }
00948     
00949     undoing= oundoing;
00950     if(!undoing) txt_undo_add_op(text, sel?UNDO_SRIGHT:UNDO_CRIGHT);
00951 
00952     if(!sel) txt_pop_sel(text);
00953 }
00954 
00955 void txt_jump_left(Text *text, short sel)
00956 {
00957     TextLine **linep, *oldl;
00958     int *charp, oldc, count, i;
00959     unsigned char oldu;
00960 
00961     if (!text) return;
00962     if(sel) txt_curs_sel(text, &linep, &charp);
00963     else { txt_pop_first(text); txt_curs_cur(text, &linep, &charp); }
00964     if (!*linep) return;
00965 
00966     oldl= *linep;
00967     oldc= *charp;
00968     oldu= undoing;
00969     undoing= 1; /* Don't push individual moves to undo stack */
00970 
00971     count= 0;
00972     for (i=0; i<3; i++) {
00973         if (count < 2) {
00974             while (*charp>0) {
00975                 char *sym= BLI_str_prev_char_utf8((*linep)->line + *charp);
00976                 if (txt_char_type(BLI_str_utf8_as_unicode(sym))==i) {
00977                     txt_move_left(text, sel);
00978                     count++;
00979                 } else break;
00980             }
00981         }
00982     }
00983     if (count==0) txt_move_left(text, sel);
00984 
00985     undoing= oldu;
00986     if(!undoing) txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, txt_get_span(text->lines.first, oldl), oldc, txt_get_span(text->lines.first, *linep), (unsigned short)*charp);
00987 }
00988 
00989 void txt_jump_right(Text *text, short sel)
00990 {
00991     TextLine **linep, *oldl;
00992     int *charp, oldc, count, i;
00993     unsigned char oldu;
00994 
00995     if (!text) return;
00996     if(sel) txt_curs_sel(text, &linep, &charp);
00997     else { txt_pop_last(text); txt_curs_cur(text, &linep, &charp); }
00998     if (!*linep) return;
00999 
01000     oldl= *linep;
01001     oldc= *charp;
01002     oldu= undoing;
01003     undoing= 1; /* Don't push individual moves to undo stack */
01004 
01005     count= 0;
01006     for (i=0; i<3; i++) {
01007         if (count < 2) {
01008             while (*charp<(*linep)->len) {
01009                 char *sym= (*linep)->line + *charp;
01010                 if (txt_char_type(BLI_str_utf8_as_unicode(sym))==i) {
01011                     txt_move_right(text, sel);
01012                     count++;
01013                 } else break;
01014             }
01015         }
01016     }
01017     if (count==0) txt_move_right(text, sel);
01018 
01019     undoing= oldu;
01020     if(!undoing) txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, txt_get_span(text->lines.first, oldl), oldc, txt_get_span(text->lines.first, *linep), (unsigned short)*charp);
01021 }
01022 
01023 void txt_move_bol (Text *text, short sel) 
01024 {
01025     TextLine **linep;
01026     int *charp, old;
01027     
01028     if (!text) return;
01029     if(sel) txt_curs_sel(text, &linep, &charp);
01030     else txt_curs_cur(text, &linep, &charp);
01031     if (!*linep) return;
01032     old= *charp;
01033     
01034     *charp= 0;
01035 
01036     if(!sel) txt_pop_sel(text);
01037     if(!undoing) txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, txt_get_span(text->lines.first, *linep), old, txt_get_span(text->lines.first, *linep), (unsigned short)*charp);
01038 }
01039 
01040 void txt_move_eol (Text *text, short sel) 
01041 {
01042     TextLine **linep;
01043     int *charp, old;
01044     
01045     if (!text) return;
01046     if(sel) txt_curs_sel(text, &linep, &charp);
01047     else txt_curs_cur(text, &linep, &charp);
01048     if (!*linep) return;
01049     old= *charp;
01050         
01051     *charp= (*linep)->len;
01052 
01053     if(!sel) txt_pop_sel(text);
01054     if(!undoing) txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, txt_get_span(text->lines.first, *linep), old, txt_get_span(text->lines.first, *linep), (unsigned short)*charp);
01055 }
01056 
01057 void txt_move_bof (Text *text, short sel)
01058 {
01059     TextLine **linep;
01060     int *charp, old;
01061     
01062     if (!text) return;
01063     if(sel) txt_curs_sel(text, &linep, &charp);
01064     else txt_curs_cur(text, &linep, &charp);
01065     if (!*linep) return;
01066     old= *charp;
01067 
01068     *linep= text->lines.first;
01069     *charp= 0;
01070 
01071     if(!sel) txt_pop_sel(text);
01072     if(!undoing) txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, txt_get_span(text->lines.first, *linep), old, txt_get_span(text->lines.first, *linep), (unsigned short)*charp);
01073 }
01074 
01075 void txt_move_eof (Text *text, short sel)
01076 {
01077     TextLine **linep;
01078     int *charp, old;
01079     
01080     if (!text) return;
01081     if(sel) txt_curs_sel(text, &linep, &charp);
01082     else txt_curs_cur(text, &linep, &charp);
01083     if (!*linep) return;
01084     old= *charp;
01085 
01086     *linep= text->lines.last;
01087     *charp= (*linep)->len;
01088 
01089     if(!sel) txt_pop_sel(text);
01090     if(!undoing) txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, txt_get_span(text->lines.first, *linep), old, txt_get_span(text->lines.first, *linep), (unsigned short)*charp); 
01091 }
01092 
01093 void txt_move_toline (Text *text, unsigned int line, short sel)
01094 {
01095     txt_move_to(text, line, 0, sel);
01096 }
01097 
01098 /* Moves to a certain byte in a line, not a certain utf8-character! */
01099 void txt_move_to (Text *text, unsigned int line, unsigned int ch, short sel)
01100 {
01101     TextLine **linep, *oldl;
01102     int *charp, oldc;
01103     unsigned int i;
01104     
01105     if (!text) return;
01106     if(sel) txt_curs_sel(text, &linep, &charp);
01107     else txt_curs_cur(text, &linep, &charp);
01108     if (!*linep) return;
01109     oldc= *charp;
01110     oldl= *linep;
01111     
01112     *linep= text->lines.first;
01113     for (i=0; i<line; i++) {
01114         if ((*linep)->next) *linep= (*linep)->next;
01115         else break;
01116     }
01117     if (ch>(unsigned int)((*linep)->len))
01118         ch= (unsigned int)((*linep)->len);
01119     *charp= ch;
01120     
01121     if(!sel) txt_pop_sel(text);
01122     if(!undoing) txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, txt_get_span(text->lines.first, oldl), oldc, txt_get_span(text->lines.first, *linep), (unsigned short)*charp);
01123 }
01124 
01125 /****************************/
01126 /* Text selection functions */
01127 /****************************/
01128 
01129 static void txt_curs_swap (Text *text)
01130 {
01131     TextLine *tmpl;
01132     int tmpc;
01133         
01134     tmpl= text->curl;
01135     text->curl= text->sell;
01136     text->sell= tmpl;
01137     
01138     tmpc= text->curc;
01139     text->curc= text->selc;
01140     text->selc= tmpc;
01141     
01142     if(!undoing) txt_undo_add_op(text, UNDO_SWAP);
01143 }
01144 
01145 static void txt_pop_first (Text *text)
01146 {
01147             
01148     if (txt_get_span(text->curl, text->sell)<0 ||
01149         (text->curl==text->sell && text->curc>text->selc)) {    
01150         txt_curs_swap(text);
01151     }
01152 
01153     if(!undoing) txt_undo_add_toop(text, UNDO_STO,
01154         txt_get_span(text->lines.first, text->sell), 
01155         text->selc, 
01156         txt_get_span(text->lines.first, text->curl), 
01157         text->curc);        
01158     
01159     txt_pop_sel(text);
01160 }
01161 
01162 static void txt_pop_last (Text *text)
01163 {
01164     if (txt_get_span(text->curl, text->sell)>0 ||
01165         (text->curl==text->sell && text->curc<text->selc)) {
01166         txt_curs_swap(text);
01167     }
01168 
01169     if(!undoing) txt_undo_add_toop(text, UNDO_STO,
01170         txt_get_span(text->lines.first, text->sell), 
01171         text->selc, 
01172         txt_get_span(text->lines.first, text->curl), 
01173         text->curc);        
01174     
01175     txt_pop_sel(text);
01176 }
01177 
01178 /* never used: CVS 1.19 */
01179 /*  static void txt_pop_selr (Text *text) */
01180 
01181 void txt_pop_sel (Text *text)
01182 {
01183     text->sell= text->curl;
01184     text->selc= text->curc; 
01185 }
01186 
01187 void txt_order_cursors(Text *text)
01188 {
01189     if (!text) return;
01190     if (!text->curl) return;
01191     if (!text->sell) return;
01192     
01193         /* Flip so text->curl is before text->sell */
01194     if (txt_get_span(text->curl, text->sell)<0 ||
01195             (text->curl==text->sell && text->curc>text->selc))
01196         txt_curs_swap(text);
01197 }
01198 
01199 int txt_has_sel(Text *text)
01200 {
01201     return ((text->curl!=text->sell) || (text->curc!=text->selc));
01202 }
01203 
01204 static void txt_delete_sel (Text *text)
01205 {
01206     TextLine *tmpl;
01207     TextMarker *mrk;
01208     char *buf;
01209     int move, lineno;
01210     
01211     if (!text) return;
01212     if (!text->curl) return;
01213     if (!text->sell) return;
01214 
01215     if (!txt_has_sel(text)) return;
01216     
01217     txt_order_cursors(text);
01218 
01219     if(!undoing) {
01220         buf= txt_sel_to_buf(text);
01221         txt_undo_add_block(text, UNDO_DBLOCK, buf);
01222         MEM_freeN(buf);
01223     }
01224 
01225     buf= MEM_mallocN(text->curc+(text->sell->len - text->selc)+1, "textline_string");
01226     
01227     if (text->curl != text->sell) {
01228         txt_clear_marker_region(text, text->curl, text->curc, text->curl->len, 0, 0);
01229         move= txt_get_span(text->curl, text->sell);
01230     } else {
01231         mrk= txt_find_marker_region(text, text->curl, text->curc, text->selc, 0, 0);
01232         if (mrk && (mrk->start > text->curc || mrk->end < text->selc))
01233             txt_clear_marker_region(text, text->curl, text->curc, text->selc, 0, 0);
01234         move= 0;
01235     }
01236 
01237     mrk= txt_find_marker_region(text, text->sell, text->selc-1, text->sell->len, 0, 0);
01238     if (mrk) {
01239         lineno= mrk->lineno;
01240         do {
01241             mrk->lineno -= move;
01242             if (mrk->start > text->curc) mrk->start -= text->selc - text->curc;
01243             mrk->end -= text->selc - text->curc;
01244             mrk= mrk->next;
01245         } while (mrk && mrk->lineno==lineno);
01246     }
01247 
01248     strncpy(buf, text->curl->line, text->curc);
01249     strcpy(buf+text->curc, text->sell->line + text->selc);
01250     buf[text->curc+(text->sell->len - text->selc)]=0;
01251 
01252     make_new_line(text->curl, buf);
01253     
01254     tmpl= text->sell;
01255     while (tmpl != text->curl) {
01256         tmpl= tmpl->prev;
01257         if (!tmpl) break;
01258         
01259         txt_delete_line(text, tmpl->next);
01260     }
01261     
01262     text->sell= text->curl;
01263     text->selc= text->curc;
01264 }
01265 
01266 void txt_sel_all (Text *text)
01267 {
01268     if (!text) return;
01269 
01270     text->curl= text->lines.first;
01271     text->curc= 0;
01272     
01273     text->sell= text->lines.last;
01274     text->selc= text->sell->len;
01275 }
01276 
01277 void txt_sel_line (Text *text)
01278 {
01279     if (!text) return;
01280     if (!text->curl) return;
01281     
01282     text->curc= 0;
01283     text->sell= text->curl;
01284     text->selc= text->sell->len;
01285 }
01286 
01287 /***************************/
01288 /* Cut and paste functions */
01289 /***************************/
01290 
01291 char *txt_to_buf (Text *text)
01292 {
01293     int length;
01294     TextLine *tmp, *linef, *linel;
01295     int charf, charl;
01296     char *buf;
01297     
01298     if (!text) return NULL;
01299     if (!text->curl) return NULL;
01300     if (!text->sell) return NULL;
01301     if (!text->lines.first) return NULL;
01302 
01303     linef= text->lines.first;
01304     charf= 0;
01305         
01306     linel= text->lines.last;
01307     charl= linel->len;
01308 
01309     if (linef == text->lines.last) {
01310         length= charl-charf;
01311 
01312         buf= MEM_mallocN(length+2, "text buffer");
01313         
01314         BLI_strncpy(buf, linef->line + charf, length+1);
01315         buf[length]=0;
01316     } else {
01317         length= linef->len - charf;
01318         length+= charl;
01319         length+= 2; /* For the 2 '\n' */
01320         
01321         tmp= linef->next;
01322         while (tmp && tmp!= linel) {
01323             length+= tmp->len+1;
01324             tmp= tmp->next;
01325         }
01326         
01327         buf= MEM_mallocN(length+1, "cut buffer");
01328 
01329         strncpy(buf, linef->line + charf, linef->len-charf);
01330         length= linef->len - charf;
01331         
01332         buf[length++]='\n';
01333         
01334         tmp= linef->next;
01335         while (tmp && tmp!=linel) {
01336             strncpy(buf+length, tmp->line, tmp->len);
01337             length+= tmp->len;
01338             
01339             buf[length++]='\n';         
01340             
01341             tmp= tmp->next;
01342         }
01343         strncpy(buf+length, linel->line, charl);
01344         length+= charl;
01345         
01346         /* python compiler wants an empty end line */
01347         buf[length++]='\n';         
01348         buf[length]=0;
01349     }
01350     
01351     return buf;
01352 }
01353 
01354 int txt_find_string(Text *text, const char *findstr, int wrap, int match_case)
01355 {
01356     TextLine *tl, *startl;
01357     char *s= NULL;
01358 
01359     if (!text || !text->curl || !text->sell) return 0;
01360     
01361     txt_order_cursors(text);
01362 
01363     tl= startl= text->sell;
01364     
01365     if(match_case) s= strstr(&tl->line[text->selc], findstr);
01366     else s= BLI_strcasestr(&tl->line[text->selc], findstr);
01367     while (!s) {
01368         tl= tl->next;
01369         if (!tl) {
01370             if (wrap)
01371                 tl= text->lines.first;
01372             else
01373                 break;
01374         }
01375 
01376         if(match_case) s= strstr(tl->line, findstr);
01377         else s= BLI_strcasestr(tl->line, findstr);
01378         if (tl==startl)
01379             break;
01380     }
01381     
01382     if (s) {
01383         int newl= txt_get_span(text->lines.first, tl);
01384         int newc= (int)(s-tl->line);
01385         txt_move_to(text, newl, newc, 0);
01386         txt_move_to(text, newl, newc + strlen(findstr), 1);
01387         return 1;               
01388     } else
01389         return 0;
01390 }
01391 
01392 char *txt_sel_to_buf (Text *text)
01393 {
01394     char *buf;
01395     int length=0;
01396     TextLine *tmp, *linef, *linel;
01397     int charf, charl;
01398     
01399     if (!text) return NULL;
01400     if (!text->curl) return NULL;
01401     if (!text->sell) return NULL;
01402     
01403     if (text->curl==text->sell) {
01404         linef= linel= text->curl;
01405         
01406         if (text->curc < text->selc) {
01407             charf= text->curc;
01408             charl= text->selc;
01409         } else{
01410             charf= text->selc;
01411             charl= text->curc;
01412         }
01413     } else if (txt_get_span(text->curl, text->sell)<0) {
01414         linef= text->sell;
01415         linel= text->curl;
01416 
01417         charf= text->selc;      
01418         charl= text->curc;
01419     } else {
01420         linef= text->curl;
01421         linel= text->sell;
01422         
01423         charf= text->curc;
01424         charl= text->selc;
01425     }
01426 
01427     if (linef == linel) {
01428         length= charl-charf;
01429 
01430         buf= MEM_mallocN(length+1, "sel buffer");
01431         
01432         BLI_strncpy(buf, linef->line + charf, length+1);
01433     } else {
01434         length+= linef->len - charf;
01435         length+= charl;
01436         length++; /* For the '\n' */
01437         
01438         tmp= linef->next;
01439         while (tmp && tmp!= linel) {
01440             length+= tmp->len+1;
01441             tmp= tmp->next;
01442         }
01443         
01444         buf= MEM_mallocN(length+1, "sel buffer");
01445         
01446         strncpy(buf, linef->line+ charf, linef->len-charf);
01447         length= linef->len-charf;
01448         
01449         buf[length++]='\n';
01450         
01451         tmp= linef->next;
01452         while (tmp && tmp!=linel) {
01453             strncpy(buf+length, tmp->line, tmp->len);
01454             length+= tmp->len;
01455             
01456             buf[length++]='\n';         
01457             
01458             tmp= tmp->next;
01459         }
01460         strncpy(buf+length, linel->line, charl);
01461         length+= charl;
01462         
01463         buf[length]=0;
01464     }   
01465 
01466     return buf;
01467 }
01468 
01469 static void txt_shift_markers(Text *text, int lineno, int count)
01470 {
01471     TextMarker *marker;
01472 
01473     for (marker=text->markers.first; marker; marker= marker->next)
01474         if (marker->lineno>=lineno) {
01475             marker->lineno+= count;
01476         }
01477 }
01478 
01479 void txt_insert_buf(Text *text, const char *in_buffer)
01480 {
01481     int l=0, u, len, lineno= -1, count= 0;
01482     size_t i=0, j;
01483     TextLine *add;
01484     char *buffer;
01485 
01486     if (!text) return;
01487     if (!in_buffer) return;
01488 
01489     txt_delete_sel(text);
01490     
01491     len= strlen(in_buffer);
01492     buffer= BLI_strdupn(in_buffer, len);
01493     len+= txt_extended_ascii_as_utf8(&buffer);
01494     
01495     if(!undoing) txt_undo_add_block(text, UNDO_IBLOCK, buffer);
01496 
01497     u= undoing;
01498     undoing= 1;
01499 
01500     /* Read the first line (or as close as possible */
01501     while (buffer[i] && buffer[i]!='\n')
01502         txt_add_raw_char(text, BLI_str_utf8_as_unicode_step(buffer, &i));
01503     
01504     if (buffer[i]=='\n') txt_split_curline(text);
01505     else { undoing = u; MEM_freeN(buffer); return; }
01506     i++;
01507 
01508     /* Read as many full lines as we can */
01509     lineno= txt_get_span(text->lines.first, text->curl);
01510 
01511     while (i<len) {
01512         l=0;
01513 
01514         while (buffer[i] && buffer[i]!='\n') {
01515             i++; l++;
01516         }
01517     
01518         if(buffer[i]=='\n') {
01519             add= txt_new_linen(buffer +(i-l), l);
01520             BLI_insertlinkbefore(&text->lines, text->curl, add);
01521             i++;
01522             count++;
01523         } else {
01524             if(count) {
01525                 txt_shift_markers(text, lineno, count);
01526                 count= 0;
01527             }
01528 
01529             for (j= i-l; j<i && j<len; )
01530                 txt_add_raw_char(text, BLI_str_utf8_as_unicode_step(buffer, &j));
01531             break;
01532         }
01533     }
01534     
01535     MEM_freeN(buffer);
01536 
01537     if(count) {
01538         txt_shift_markers(text, lineno, count);
01539     }
01540 
01541     undoing= u;
01542 }
01543 
01544 /******************/
01545 /* Undo functions */
01546 /******************/
01547 
01548 static int max_undo_test(Text *text, int x)
01549 {
01550     while (text->undo_pos+x >= text->undo_len) {
01551         if(text->undo_len*2 > TXT_MAX_UNDO) {
01552             /* XXX error("Undo limit reached, buffer cleared\n"); */
01553             MEM_freeN(text->undo_buf);
01554             init_undo_text(text);
01555             return 0;
01556         } else {
01557             void *tmp= text->undo_buf;
01558             text->undo_buf= MEM_callocN(text->undo_len*2, "undo buf");
01559             memcpy(text->undo_buf, tmp, text->undo_len);
01560             text->undo_len*=2;
01561             MEM_freeN(tmp);
01562         }
01563     }
01564 
01565     return 1;
01566 }
01567 
01568 static void dump_buffer(Text *text) 
01569 {
01570     int i= 0;
01571     
01572     while (i++<text->undo_pos) printf("%d: %d %c\n", i, text->undo_buf[i], text->undo_buf[i]);
01573 }
01574 
01575 void txt_print_undo(Text *text)
01576 {
01577     int i= 0;
01578     int op;
01579     const char *ops;
01580     int linep, charp;
01581     
01582     dump_buffer(text);
01583     
01584     printf ("---< Undo Buffer >---\n");
01585     
01586     printf ("UndoPosition is %d\n", text->undo_pos);
01587     
01588     while (i<=text->undo_pos) {
01589         op= text->undo_buf[i];
01590         
01591         if (op==UNDO_CLEFT) {
01592             ops= "Cursor left";
01593         } else if (op==UNDO_CRIGHT) {
01594             ops= "Cursor right";
01595         } else if (op==UNDO_CUP) {
01596             ops= "Cursor up";
01597         } else if (op==UNDO_CDOWN) {
01598             ops= "Cursor down";
01599         } else if (op==UNDO_SLEFT) {
01600             ops= "Selection left";
01601         } else if (op==UNDO_SRIGHT) {
01602             ops= "Selection right";
01603         } else if (op==UNDO_SUP) {
01604             ops= "Selection up";
01605         } else if (op==UNDO_SDOWN) {
01606             ops= "Selection down";
01607         } else if (op==UNDO_STO) {
01608             ops= "Selection ";
01609         } else if (op==UNDO_CTO) {
01610             ops= "Cursor ";
01611         } else if (op==UNDO_INSERT_1) {
01612             ops= "Insert ascii ";
01613         } else if (op==UNDO_INSERT_2) {
01614             ops= "Insert 2 bytes ";
01615         } else if (op==UNDO_INSERT_3) {
01616             ops= "Insert 3 bytes ";
01617         } else if (op==UNDO_INSERT_4) {
01618             ops= "Insert unicode ";
01619         } else if (op==UNDO_BS_1) {
01620             ops= "Backspace for ascii ";
01621         } else if (op==UNDO_BS_2) {
01622             ops= "Backspace for 2 bytes ";
01623         } else if (op==UNDO_BS_3) {
01624             ops= "Backspace for 3 bytes ";
01625         } else if (op==UNDO_BS_4) {
01626             ops= "Backspace for unicode ";
01627         } else if (op==UNDO_DEL_1) {
01628             ops= "Delete ascii ";
01629         } else if (op==UNDO_DEL_2) {
01630             ops= "Delete 2 bytes ";
01631         } else if (op==UNDO_DEL_3) {
01632             ops= "Delete 3 bytes ";
01633         } else if (op==UNDO_DEL_4) {
01634             ops= "Delete unicode ";
01635         } else if (op==UNDO_SWAP) {
01636             ops= "Cursor swap";
01637         } else if (op==UNDO_DBLOCK) {
01638             ops= "Delete text block";
01639         } else if (op==UNDO_IBLOCK) {
01640             ops= "Insert text block";
01641         } else if (op==UNDO_INDENT) {
01642             ops= "Indent ";
01643         } else if (op==UNDO_UNINDENT) {
01644             ops= "Unindent ";
01645         } else if (op==UNDO_COMMENT) {
01646             ops= "Comment ";
01647         } else if (op==UNDO_UNCOMMENT) {
01648             ops= "Uncomment ";
01649         } else {
01650             ops= "Unknown";
01651         }
01652         
01653         printf ("Op (%o) at %d = %s", op, i, ops);
01654         if (op >= UNDO_INSERT_1 && op <= UNDO_DEL_4) {
01655             i++;
01656             printf (" - Char is ");
01657             switch (op) {
01658                 case UNDO_INSERT_1: case UNDO_BS_1: case UNDO_DEL_1:
01659                     printf ("%c", text->undo_buf[i]);  
01660                     i++;
01661                     break;
01662                 case UNDO_INSERT_2: case UNDO_BS_2: case UNDO_DEL_2:
01663                     printf ("%c%c", text->undo_buf[i], text->undo_buf[i+1]);  
01664                     i+=2;
01665                     break;
01666                 case UNDO_INSERT_3: case UNDO_BS_3: case UNDO_DEL_3:
01667                     printf ("%c%c%c", text->undo_buf[i], text->undo_buf[i+1], text->undo_buf[i+2]);  
01668                     i+=3;
01669                     break;
01670                 case UNDO_INSERT_4: case UNDO_BS_4: case UNDO_DEL_4: {
01671                     unsigned int uc;
01672                     char c[BLI_UTF8_MAX+1];
01673                     size_t c_len;
01674                     uc= text->undo_buf[i]; i++;
01675                     uc= uc+(text->undo_buf[i]<<8); i++;
01676                     uc= uc+(text->undo_buf[i]<<16); i++;
01677                     uc= uc+(text->undo_buf[i]<<24); i++;
01678                     c_len= BLI_str_utf8_from_unicode(uc, c);
01679                     c[c_len]= '\0';
01680                     printf ("%s", c);
01681                 }
01682             }
01683         } else if (op==UNDO_STO || op==UNDO_CTO) {
01684             i++;
01685 
01686             charp= text->undo_buf[i]; i++;
01687             charp= charp+(text->undo_buf[i]<<8); i++;
01688 
01689             linep= text->undo_buf[i]; i++;
01690             linep= linep+(text->undo_buf[i]<<8); i++;
01691             linep= linep+(text->undo_buf[i]<<16); i++;
01692             linep= linep+(text->undo_buf[i]<<24); i++;
01693             
01694             printf ("to <%d, %d> ", linep, charp);
01695 
01696             charp= text->undo_buf[i]; i++;
01697             charp= charp+(text->undo_buf[i]<<8); i++;
01698 
01699             linep= text->undo_buf[i]; i++;
01700             linep= linep+(text->undo_buf[i]<<8); i++;
01701             linep= linep+(text->undo_buf[i]<<16); i++;
01702             linep= linep+(text->undo_buf[i]<<24); i++;
01703             
01704             printf ("from <%d, %d>", linep, charp);
01705         } else if (op==UNDO_DBLOCK || op==UNDO_IBLOCK) {
01706             i++;
01707 
01708             linep= text->undo_buf[i]; i++;
01709             linep= linep+(text->undo_buf[i]<<8); i++;
01710             linep= linep+(text->undo_buf[i]<<16); i++;
01711             linep= linep+(text->undo_buf[i]<<24); i++;
01712             
01713             printf (" (length %d) <", linep);
01714             
01715             while (linep>0) {
01716                 putchar(text->undo_buf[i]);
01717                 linep--; i++;
01718             }
01719             
01720             linep= text->undo_buf[i]; i++;
01721             linep= linep+(text->undo_buf[i]<<8); i++;
01722             linep= linep+(text->undo_buf[i]<<16); i++;
01723             linep= linep+(text->undo_buf[i]<<24); i++;
01724             printf ("> (%d)", linep);
01725         } else if (op==UNDO_INDENT || op==UNDO_UNINDENT) {
01726             i++;
01727 
01728             charp= text->undo_buf[i]; i++;
01729             charp= charp+(text->undo_buf[i]<<8); i++;
01730 
01731             linep= text->undo_buf[i]; i++;
01732             linep= linep+(text->undo_buf[i]<<8); i++;
01733             linep= linep+(text->undo_buf[i]<<16); i++;
01734             linep= linep+(text->undo_buf[i]<<24); i++;
01735             
01736             printf ("to <%d, %d> ", linep, charp);
01737 
01738             charp= text->undo_buf[i]; i++;
01739             charp= charp+(text->undo_buf[i]<<8); i++;
01740 
01741             linep= text->undo_buf[i]; i++;
01742             linep= linep+(text->undo_buf[i]<<8); i++;
01743             linep= linep+(text->undo_buf[i]<<16); i++;
01744             linep= linep+(text->undo_buf[i]<<24); i++;
01745             
01746             printf ("from <%d, %d>", linep, charp);
01747         }
01748         
01749         printf (" %d\n",  i);
01750         i++;
01751     }
01752 }
01753 
01754 static void txt_undo_add_op(Text *text, int op)
01755 {
01756     if(!max_undo_test(text, 2))
01757         return;
01758     
01759     text->undo_pos++;
01760     text->undo_buf[text->undo_pos]= op;
01761     text->undo_buf[text->undo_pos+1]= 0;
01762 }
01763 
01764 static void txt_undo_store_uint16(char *undo_buf, int *undo_pos, unsigned short value) 
01765 {
01766     undo_buf[*undo_pos]= (value)&0xff;
01767     (*undo_pos)++;
01768     undo_buf[*undo_pos]= (value>>8)&0xff;
01769     (*undo_pos)++;
01770 }
01771 
01772 static void txt_undo_store_uint32(char *undo_buf, int *undo_pos, unsigned int value) 
01773 {
01774     undo_buf[*undo_pos]= (value)&0xff;
01775     (*undo_pos)++;
01776     undo_buf[*undo_pos]= (value>>8)&0xff;
01777     (*undo_pos)++;
01778     undo_buf[*undo_pos]= (value>>16)&0xff;
01779     (*undo_pos)++;
01780     undo_buf[*undo_pos]= (value>>24)&0xff;
01781     (*undo_pos)++;
01782 }
01783 
01784 static void txt_undo_add_block(Text *text, int op, const char *buf)
01785 {
01786     unsigned int length= strlen(buf);
01787     
01788     if(!max_undo_test(text, length+11))
01789         return;
01790 
01791     text->undo_pos++;
01792     text->undo_buf[text->undo_pos]= op;
01793     text->undo_pos++;
01794     
01795     txt_undo_store_uint32(text->undo_buf, &text->undo_pos, length);
01796     
01797     strncpy(text->undo_buf+text->undo_pos, buf, length);
01798     text->undo_pos+=length;
01799 
01800     txt_undo_store_uint32(text->undo_buf, &text->undo_pos, length);
01801     text->undo_buf[text->undo_pos]= op;
01802     
01803     text->undo_buf[text->undo_pos+1]= 0;
01804 }
01805 
01806 void txt_undo_add_toop(Text *text, int op, unsigned int froml, unsigned short fromc, unsigned int tol, unsigned short toc)
01807 {
01808     if(!max_undo_test(text, 15))
01809         return;
01810 
01811     if (froml==tol && fromc==toc) return;
01812 
01813     text->undo_pos++;
01814     text->undo_buf[text->undo_pos]= op;
01815 
01816     text->undo_pos++;
01817     
01818     txt_undo_store_uint16(text->undo_buf, &text->undo_pos, fromc);
01819     txt_undo_store_uint32(text->undo_buf, &text->undo_pos, froml);
01820     txt_undo_store_uint16(text->undo_buf, &text->undo_pos, toc);
01821     txt_undo_store_uint32(text->undo_buf, &text->undo_pos, tol);
01822         
01823     text->undo_buf[text->undo_pos]= op;
01824 
01825     text->undo_buf[text->undo_pos+1]= 0;
01826 }
01827 
01828 static void txt_undo_add_charop(Text *text, int op_start, unsigned int c)
01829 {
01830     char utf8[BLI_UTF8_MAX];
01831     size_t i, utf8_size = BLI_str_utf8_from_unicode(c, utf8);
01832     
01833     if(!max_undo_test(text, 3 + utf8_size))
01834         return;
01835     
01836     text->undo_pos++;
01837     
01838     if (utf8_size < 4) {
01839         text->undo_buf[text->undo_pos]= op_start + utf8_size - 1;
01840         text->undo_pos++;
01841         
01842         for (i = 0; i < utf8_size; i++) {
01843             text->undo_buf[text->undo_pos]= utf8[i];
01844             text->undo_pos++;
01845         }
01846         
01847         text->undo_buf[text->undo_pos]= op_start + utf8_size - 1;
01848     } else {
01849         text->undo_buf[text->undo_pos]= op_start + 3;
01850         text->undo_pos++;
01851         txt_undo_store_uint32(text->undo_buf, &text->undo_pos, c);
01852         text->undo_buf[text->undo_pos]= op_start + 3;
01853     }
01854     
01855     text->undo_buf[text->undo_pos+1]= 0;
01856 }
01857 
01858 static unsigned short txt_undo_read_uint16(const char *undo_buf, int *undo_pos)
01859 {
01860     unsigned short val;
01861     val= undo_buf[*undo_pos]; (*undo_pos)--;
01862     val= (val<<8)+undo_buf[*undo_pos]; (*undo_pos)--;
01863     return val;
01864 }
01865 
01866 static unsigned int txt_undo_read_uint32(const char *undo_buf, int *undo_pos)
01867 {
01868     unsigned int val;
01869     val= undo_buf[*undo_pos]; (*undo_pos)--;
01870     val= (val<<8)+undo_buf[*undo_pos]; (*undo_pos)--;
01871     val= (val<<8)+undo_buf[*undo_pos]; (*undo_pos)--;
01872     val= (val<<8)+undo_buf[*undo_pos]; (*undo_pos)--;
01873     return val;
01874 }
01875 
01876 static unsigned int txt_undo_read_unicode(const char *undo_buf, int *undo_pos, short bytes)
01877 {
01878     unsigned int unicode;
01879     char utf8[BLI_UTF8_MAX+1];
01880     
01881     switch (bytes) {
01882         case 1: /* ascii */
01883             unicode = undo_buf[*undo_pos]; (*undo_pos)--; 
01884             break;
01885         case 2: /* 2-byte symbol */
01886             utf8[2] = '\0';
01887             utf8[1] = undo_buf[*undo_pos]; (*undo_pos)--;
01888             utf8[0] = undo_buf[*undo_pos]; (*undo_pos)--;
01889             unicode= BLI_str_utf8_as_unicode(utf8);
01890             break;
01891         case 3: /* 3-byte symbol */
01892             utf8[3] = '\0';
01893             utf8[2] = undo_buf[*undo_pos]; (*undo_pos)--;
01894             utf8[1] = undo_buf[*undo_pos]; (*undo_pos)--;
01895             utf8[0] = undo_buf[*undo_pos]; (*undo_pos)--;
01896             unicode= BLI_str_utf8_as_unicode(utf8);
01897             break;
01898         case 4: /* 32-bit unicode symbol */
01899             unicode= txt_undo_read_uint32(undo_buf, undo_pos);
01900     }
01901     
01902     return unicode;
01903 }
01904 
01905 static unsigned short txt_redo_read_uint16(const char *undo_buf, int *undo_pos)
01906 {
01907     unsigned short val;
01908     val = undo_buf[*undo_pos]; (*undo_pos)++;
01909     val = val+(undo_buf[*undo_pos]<<8); (*undo_pos)++;
01910     return val;
01911 }
01912 
01913 static unsigned int txt_redo_read_uint32(const char *undo_buf, int *undo_pos)
01914 {
01915     unsigned int val;
01916     val= undo_buf[*undo_pos]; (*undo_pos)++;
01917     val= val+(undo_buf[*undo_pos]<<8); (*undo_pos)++;
01918     val= val+(undo_buf[*undo_pos]<<16); (*undo_pos)++;
01919     val= val+(undo_buf[*undo_pos]<<24); (*undo_pos)++;
01920     return val;
01921 }
01922 
01923 static unsigned int txt_redo_read_unicode(const char *undo_buf, int *undo_pos, short bytes)
01924 {
01925     unsigned int unicode;
01926     char utf8[BLI_UTF8_MAX+1];
01927     
01928     switch (bytes) {
01929         case 1: /* ascii */
01930             unicode = undo_buf[*undo_pos]; (*undo_pos)++; 
01931             break;
01932         case 2: /* 2-byte symbol */
01933             utf8[0] = undo_buf[*undo_pos]; (*undo_pos)++;
01934             utf8[1] = undo_buf[*undo_pos]; (*undo_pos)++;
01935             utf8[2] = '\0';
01936             unicode= BLI_str_utf8_as_unicode(utf8);
01937             break;
01938         case 3: /* 3-byte symbol */
01939             utf8[0] = undo_buf[*undo_pos]; (*undo_pos)++;
01940             utf8[1] = undo_buf[*undo_pos]; (*undo_pos)++;
01941             utf8[2] = undo_buf[*undo_pos]; (*undo_pos)++;
01942             utf8[3] = '\0';
01943             unicode= BLI_str_utf8_as_unicode(utf8);
01944             break;
01945         case 4: /* 32-bit unicode symbol */
01946             unicode= txt_undo_read_uint32(undo_buf, undo_pos);
01947     }
01948     
01949     return unicode;
01950 }
01951 
01952 void txt_do_undo(Text *text)
01953 {
01954     int op= text->undo_buf[text->undo_pos];
01955     unsigned int linep, i;
01956     unsigned short charp;
01957     TextLine *holdl;
01958     int holdc, holdln;
01959     char *buf;
01960     
01961     if (text->undo_pos<0) {
01962         return;
01963     }
01964 
01965     text->undo_pos--;
01966 
01967     undoing= 1;
01968     
01969     switch(op) {
01970         case UNDO_CLEFT:
01971             txt_move_right(text, 0);
01972             break;
01973             
01974         case UNDO_CRIGHT:
01975             txt_move_left(text, 0);
01976             break;
01977             
01978         case UNDO_CUP:
01979             txt_move_down(text, 0);
01980             break;
01981             
01982         case UNDO_CDOWN:
01983             txt_move_up(text, 0);
01984             break;
01985 
01986         case UNDO_SLEFT:
01987             txt_move_right(text, 1);
01988             break;
01989 
01990         case UNDO_SRIGHT:
01991             txt_move_left(text, 1);
01992             break;
01993 
01994         case UNDO_SUP:
01995             txt_move_down(text, 1);
01996             break;
01997 
01998         case UNDO_SDOWN:
01999             txt_move_up(text, 1);
02000             break;
02001         
02002         case UNDO_CTO:
02003         case UNDO_STO:
02004             text->undo_pos--;
02005             text->undo_pos--;
02006             text->undo_pos--;
02007             text->undo_pos--;
02008         
02009             text->undo_pos--;
02010             text->undo_pos--;
02011         
02012             linep= txt_undo_read_uint32(text->undo_buf, &text->undo_pos);
02013             charp= txt_undo_read_uint16(text->undo_buf, &text->undo_pos);
02014             
02015             if (op==UNDO_CTO) {
02016                 txt_move_toline(text, linep, 0);
02017                 text->curc= charp;
02018                 txt_pop_sel(text);
02019             } else {
02020                 txt_move_toline(text, linep, 1);
02021                 text->selc= charp;
02022             }
02023             
02024             text->undo_pos--;
02025             break;
02026             
02027         case UNDO_INSERT_1: case UNDO_INSERT_2: case UNDO_INSERT_3: case UNDO_INSERT_4:
02028             txt_backspace_char(text);
02029             text->undo_pos-= op - UNDO_INSERT_1 + 1;
02030             text->undo_pos--;
02031             break;
02032 
02033         case UNDO_BS_1: case UNDO_BS_2: case UNDO_BS_3: case UNDO_BS_4:
02034             charp = op - UNDO_BS_1 + 1;
02035             txt_add_char(text, txt_undo_read_unicode(text->undo_buf, &text->undo_pos, charp));
02036             text->undo_pos--;
02037             break;      
02038             
02039         case UNDO_DEL_1: case UNDO_DEL_2: case UNDO_DEL_3: case UNDO_DEL_4: 
02040             charp = op - UNDO_DEL_1 + 1;
02041             txt_add_char(text, txt_undo_read_unicode(text->undo_buf, &text->undo_pos, charp));
02042             txt_move_left(text, 0);
02043             text->undo_pos--;
02044             break;
02045 
02046         case UNDO_SWAP:
02047             txt_curs_swap(text);
02048             break;
02049 
02050         case UNDO_DBLOCK:
02051             linep= txt_undo_read_uint32(text->undo_buf, &text->undo_pos);
02052 
02053             buf= MEM_mallocN(linep+1, "dblock buffer");
02054             for (i=0; i < linep; i++){
02055                 buf[(linep-1)-i]= text->undo_buf[text->undo_pos]; 
02056                 text->undo_pos--;
02057             }
02058             buf[i]= 0;
02059             
02060             txt_curs_first(text, &holdl, &holdc);
02061             holdln= txt_get_span(text->lines.first, holdl);
02062             
02063             txt_insert_buf(text, buf);          
02064             MEM_freeN(buf);
02065 
02066             text->curl= text->lines.first;
02067             while (holdln>0) {
02068                 if(text->curl->next)
02069                     text->curl= text->curl->next;
02070                     
02071                 holdln--;
02072             }
02073             text->curc= holdc;
02074 
02075             text->undo_pos--;
02076             text->undo_pos--;
02077             text->undo_pos--; 
02078             text->undo_pos--;
02079 
02080             text->undo_pos--;
02081             
02082             break;
02083 
02084         case UNDO_IBLOCK:
02085             linep= txt_undo_read_uint32(text->undo_buf, &text->undo_pos);
02086             txt_delete_sel(text);
02087 
02088             /* txt_backspace_char removes utf8-characters, not bytes */
02089             buf= MEM_mallocN(linep+1, "iblock buffer");
02090             for (i=0; i < linep; i++){
02091                 buf[(linep-1)-i]= text->undo_buf[text->undo_pos]; 
02092                 text->undo_pos--;
02093             }
02094             buf[i]= 0;
02095             linep= txt_utf8_len(buf);
02096             MEM_freeN(buf);
02097 
02098             while (linep>0) {
02099                 txt_backspace_char(text);
02100                 linep--;
02101             }
02102 
02103             text->undo_pos--;
02104             text->undo_pos--;
02105             text->undo_pos--; 
02106             text->undo_pos--;
02107             
02108             text->undo_pos--;
02109 
02110             break;
02111         case UNDO_INDENT:
02112         case UNDO_UNINDENT:
02113         case UNDO_COMMENT:
02114         case UNDO_UNCOMMENT:
02115             linep= txt_undo_read_uint32(text->undo_buf, &text->undo_pos);
02116             //linep is now the end line of the selection
02117             
02118             charp = txt_undo_read_uint16(text->undo_buf, &text->undo_pos);
02119             //charp is the last char selected or text->line->len
02120             
02121             //set the selection for this now
02122             text->selc = charp;
02123             text->sell = text->lines.first;
02124             for (i= 0; i < linep; i++) {
02125                 text->sell = text->sell->next;
02126             }
02127 
02128             linep= txt_undo_read_uint32(text->undo_buf, &text->undo_pos);
02129             //first line to be selected
02130             
02131             charp = txt_undo_read_uint16(text->undo_buf, &text->undo_pos);
02132             //first postion to be selected
02133             text->curc = charp;
02134             text->curl = text->lines.first;
02135             for (i = 0; i < linep; i++) {
02136                 text->curl = text->curl->next;
02137             }
02138 
02139             
02140             if (op==UNDO_INDENT) {
02141                 txt_unindent(text);
02142             } else if (op== UNDO_UNINDENT) {
02143                 txt_indent(text);
02144             } else if (op == UNDO_COMMENT) {
02145                 txt_uncomment(text);
02146             } else if (op == UNDO_UNCOMMENT) {
02147                 txt_comment(text);
02148             }
02149 
02150             text->undo_pos--;
02151             break;
02152         default:
02153             //XXX error("Undo buffer error - resetting");
02154             text->undo_pos= -1;
02155             
02156             break;
02157     }
02158 
02159     /* next undo step may need evaluating */
02160     if (text->undo_pos>=0) {
02161         switch (text->undo_buf[text->undo_pos]) {
02162             case UNDO_STO:
02163                 txt_do_undo(text);
02164                 txt_do_redo(text); /* selections need restoring */
02165                 break;
02166             case UNDO_SWAP:
02167                 txt_do_undo(text); /* swaps should appear transparent */
02168                 break;
02169         }
02170     }
02171     
02172     undoing= 0; 
02173 }
02174 
02175 void txt_do_redo(Text *text)
02176 {
02177     char op;
02178     unsigned int linep, i;
02179     unsigned short charp;
02180     char *buf;
02181     
02182     text->undo_pos++;   
02183     op= text->undo_buf[text->undo_pos];
02184     
02185     if (!op) {
02186         text->undo_pos--;
02187         return;
02188     }
02189     
02190     undoing= 1;
02191 
02192     switch(op) {
02193         case UNDO_CLEFT:
02194             txt_move_left(text, 0);
02195             break;
02196             
02197         case UNDO_CRIGHT:
02198             txt_move_right(text, 0);
02199             break;
02200             
02201         case UNDO_CUP:
02202             txt_move_up(text, 0);
02203             break;
02204             
02205         case UNDO_CDOWN:
02206             txt_move_down(text, 0);
02207             break;
02208 
02209         case UNDO_SLEFT:
02210             txt_move_left(text, 1);
02211             break;
02212 
02213         case UNDO_SRIGHT:
02214             txt_move_right(text, 1);
02215             break;
02216 
02217         case UNDO_SUP:
02218             txt_move_up(text, 1);
02219             break;
02220 
02221         case UNDO_SDOWN:
02222             txt_move_down(text, 1);
02223             break;
02224         
02225         case UNDO_INSERT_1: case UNDO_INSERT_2: case UNDO_INSERT_3: case UNDO_INSERT_4:
02226             text->undo_pos++;
02227             charp = op - UNDO_INSERT_1 + 1;
02228             txt_add_char(text, txt_redo_read_unicode(text->undo_buf, &text->undo_pos, charp));
02229             break;
02230 
02231         case UNDO_BS_1: case UNDO_BS_2: case UNDO_BS_3: case UNDO_BS_4:
02232             text->undo_pos++;
02233             txt_backspace_char(text);
02234             text->undo_pos+= op - UNDO_BS_1 + 1;
02235             break;
02236 
02237         case UNDO_DEL_1: case UNDO_DEL_2: case UNDO_DEL_3: case UNDO_DEL_4:
02238             text->undo_pos++;
02239             txt_delete_char(text);
02240             text->undo_pos+= op - UNDO_DEL_1 + 1;
02241             break;
02242 
02243         case UNDO_SWAP:
02244             txt_curs_swap(text);
02245             txt_do_redo(text); /* swaps should appear transparent a*/
02246             break;
02247             
02248         case UNDO_CTO:
02249         case UNDO_STO:
02250             text->undo_pos++;
02251             text->undo_pos++;
02252 
02253             text->undo_pos++;
02254             text->undo_pos++;
02255             text->undo_pos++;
02256             text->undo_pos++;
02257 
02258             text->undo_pos++;
02259 
02260             charp= txt_redo_read_uint16(text->undo_buf, &text->undo_pos);
02261             linep= txt_redo_read_uint32(text->undo_buf, &text->undo_pos);
02262             
02263             if (op==UNDO_CTO) {
02264                 txt_move_toline(text, linep, 0);
02265                 text->curc= charp;
02266                 txt_pop_sel(text);
02267             } else {
02268                 txt_move_toline(text, linep, 1);
02269                 text->selc= charp;
02270             }
02271 
02272             break;
02273 
02274         case UNDO_DBLOCK:
02275             text->undo_pos++;
02276             linep= txt_redo_read_uint32(text->undo_buf, &text->undo_pos);
02277             txt_delete_sel(text);
02278             
02279             text->undo_pos+=linep;
02280 
02281             text->undo_pos++;
02282             text->undo_pos++;
02283             text->undo_pos++; 
02284             text->undo_pos++;
02285             
02286             break;
02287 
02288         case UNDO_IBLOCK:
02289             text->undo_pos++;
02290             linep= txt_redo_read_uint32(text->undo_buf, &text->undo_pos);
02291 
02292             buf= MEM_mallocN(linep+1, "iblock buffer");
02293             memcpy (buf, &text->undo_buf[text->undo_pos], linep);
02294             text->undo_pos+= linep;
02295             buf[linep]= 0;
02296             
02297             txt_insert_buf(text, buf);          
02298             MEM_freeN(buf);
02299 
02300             text->undo_pos++;
02301             text->undo_pos++;
02302             text->undo_pos++; 
02303             text->undo_pos++;
02304             break;
02305             
02306         case UNDO_INDENT:
02307         case UNDO_UNINDENT:
02308         case UNDO_COMMENT:
02309         case UNDO_UNCOMMENT:
02310             text->undo_pos++;
02311             charp = txt_redo_read_uint16(text->undo_buf, &text->undo_pos);
02312             //charp is the first char selected or 0
02313             
02314             linep= txt_redo_read_uint32(text->undo_buf, &text->undo_pos);
02315             //linep is now the first line of the selection          
02316             //set the selcetion for this now
02317             text->curc = charp;
02318             text->curl = text->lines.first;
02319             for (i= 0; i < linep; i++) {
02320                 text->curl = text->curl->next;
02321             }
02322             
02323             charp = txt_redo_read_uint16(text->undo_buf, &text->undo_pos);
02324             //last postion to be selected
02325             
02326             linep= txt_redo_read_uint32(text->undo_buf, &text->undo_pos);
02327             //Last line to be selected
02328             
02329             text->selc = charp;
02330             text->sell = text->lines.first;
02331             for (i = 0; i < linep; i++) {
02332                 text->sell = text->sell->next;
02333             }
02334 
02335             if (op==UNDO_INDENT) {
02336                 txt_indent(text);
02337             } else if (op== UNDO_UNINDENT) {
02338                 txt_unindent(text);
02339             } else if (op == UNDO_COMMENT) {
02340                 txt_comment(text);
02341             } else if (op == UNDO_UNCOMMENT) {
02342                 txt_uncomment(text);
02343             }
02344             break;
02345         default:
02346             //XXX error("Undo buffer error - resetting");
02347             text->undo_pos= -1;
02348 
02349             break;
02350     }
02351     
02352     undoing= 0; 
02353 }
02354 
02355 /**************************/
02356 /* Line editing functions */ 
02357 /**************************/
02358 
02359 void txt_split_curline (Text *text) 
02360 {
02361     TextLine *ins;
02362     TextMarker *mrk;
02363     char *left, *right;
02364     int lineno= -1;
02365     
02366     if (!text) return;
02367     if (!text->curl) return;
02368 
02369     txt_delete_sel(text);
02370 
02371     /* Move markers */
02372 
02373     lineno= txt_get_span(text->lines.first, text->curl);
02374     mrk= text->markers.first;
02375     while (mrk) {
02376         if (mrk->lineno==lineno && mrk->start>text->curc) {
02377             mrk->lineno++;
02378             mrk->start -= text->curc;
02379             mrk->end -= text->curc;
02380         } else if (mrk->lineno > lineno) {
02381             mrk->lineno++;
02382         }
02383         mrk= mrk->next;
02384     }
02385 
02386     /* Make the two half strings */
02387 
02388     left= MEM_mallocN(text->curc+1, "textline_string");
02389     if (text->curc) memcpy(left, text->curl->line, text->curc);
02390     left[text->curc]=0;
02391     
02392     right= MEM_mallocN(text->curl->len - text->curc+1, "textline_string");
02393     memcpy(right, text->curl->line+text->curc, text->curl->len-text->curc+1);
02394 
02395     MEM_freeN(text->curl->line);
02396     if (text->curl->format) MEM_freeN(text->curl->format);
02397 
02398     /* Make the new TextLine */
02399     
02400     ins= MEM_mallocN(sizeof(TextLine), "textline");
02401     ins->line= left;
02402     ins->format= NULL;
02403     ins->len= text->curc;
02404     
02405     text->curl->line= right;
02406     text->curl->format= NULL;
02407     text->curl->len= text->curl->len - text->curc;
02408     
02409     BLI_insertlinkbefore(&text->lines, text->curl, ins);    
02410     
02411     text->curc=0;
02412     
02413     txt_make_dirty(text);
02414     txt_clean_text(text);
02415     
02416     txt_pop_sel(text);
02417     if(!undoing) txt_undo_add_charop(text, UNDO_INSERT_1, '\n');
02418 }
02419 
02420 static void txt_delete_line (Text *text, TextLine *line) 
02421 {
02422     TextMarker *mrk=NULL, *nxt;
02423     int lineno= -1;
02424 
02425     if (!text) return;
02426     if (!text->curl) return;
02427 
02428     lineno= txt_get_span(text->lines.first, line);
02429     mrk= text->markers.first;
02430     while (mrk) {
02431         nxt= mrk->next;
02432         if (mrk->lineno==lineno)
02433             BLI_freelinkN(&text->markers, mrk);
02434         else if (mrk->lineno > lineno)
02435             mrk->lineno--;
02436         mrk= nxt;
02437     }
02438 
02439     BLI_remlink (&text->lines, line);
02440     
02441     if (line->line) MEM_freeN(line->line);
02442     if (line->format) MEM_freeN(line->format);
02443 
02444     MEM_freeN(line);
02445 
02446     txt_make_dirty(text);
02447     txt_clean_text(text);
02448 }
02449 
02450 static void txt_combine_lines (Text *text, TextLine *linea, TextLine *lineb)
02451 {
02452     char *tmp;
02453     TextMarker *mrk= NULL;
02454     int lineno=-1;
02455     
02456     if (!text) return;
02457     
02458     if(!linea || !lineb) return;
02459 
02460     mrk= txt_find_marker_region(text, lineb, 0, lineb->len, 0, 0);
02461     if (mrk) {
02462         lineno= mrk->lineno;
02463         do {
02464             mrk->lineno--;
02465             mrk->start += linea->len;
02466             mrk->end += linea->len;
02467             mrk= mrk->next;
02468         } while (mrk && mrk->lineno==lineno);
02469     }
02470     if (lineno==-1) lineno= txt_get_span(text->lines.first, lineb);
02471     
02472     tmp= MEM_mallocN(linea->len+lineb->len+1, "textline_string");
02473     
02474     strcpy(tmp, linea->line);
02475     strcat(tmp, lineb->line);
02476 
02477     make_new_line(linea, tmp);
02478     
02479     txt_delete_line(text, lineb);
02480     
02481     txt_make_dirty(text);
02482     txt_clean_text(text);
02483 }
02484 
02485 void txt_delete_char(Text *text) 
02486 {
02487     unsigned int c='\n';
02488     
02489     if (!text) return;
02490     if (!text->curl) return;
02491 
02492     if (txt_has_sel(text)) { /* deleting a selection */
02493         txt_delete_sel(text);
02494         txt_make_dirty(text);
02495         return;
02496     }
02497     else if (text->curc== text->curl->len) { /* Appending two lines */
02498         if (text->curl->next) {
02499             txt_combine_lines(text, text->curl, text->curl->next);
02500             txt_pop_sel(text);
02501         }
02502     } else { /* Just deleting a char */
02503         size_t c_len = 0;
02504         TextMarker *mrk;
02505         c= BLI_str_utf8_as_unicode_and_size(text->curl->line + text->curc, &c_len);
02506 
02507         mrk= txt_find_marker_region(text, text->curl, text->curc - c_len, text->curl->len, 0, 0);
02508         if (mrk) {
02509             int lineno= mrk->lineno;
02510             if (mrk->end==text->curc) {
02511                 if ((mrk->flags & TMARK_TEMP) && !(mrk->flags & TMARK_EDITALL)) {
02512                     txt_clear_markers(text, mrk->group, TMARK_TEMP);
02513                 } else {
02514                     BLI_freelinkN(&text->markers, mrk);
02515                 }
02516                 return;
02517             }
02518             do {
02519                 if (mrk->start>text->curc) mrk->start-= c_len;
02520                 mrk->end-= c_len;
02521                 mrk= mrk->next;
02522             } while (mrk && mrk->lineno==lineno);
02523         }
02524         
02525         memmove(text->curl->line+text->curc, text->curl->line+text->curc+c_len, text->curl->len-text->curc-c_len+1);
02526 
02527         text->curl->len-= c_len;
02528 
02529         txt_pop_sel(text);
02530     }
02531 
02532     txt_make_dirty(text);
02533     txt_clean_text(text);
02534     
02535     if(!undoing) txt_undo_add_charop(text, UNDO_DEL_1, c);
02536 }
02537 
02538 void txt_delete_word (Text *text) 
02539 {
02540     txt_jump_right(text, 1);
02541     txt_delete_sel(text);
02542 }
02543 
02544 void txt_backspace_char (Text *text) 
02545 {
02546     unsigned int c='\n';
02547     
02548     if (!text) return;
02549     if (!text->curl) return;
02550     
02551     if (txt_has_sel(text)) { /* deleting a selection */
02552         txt_delete_sel(text);
02553         txt_make_dirty(text);
02554         return;
02555     }
02556     else if (text->curc==0) { /* Appending two lines */
02557         if (!text->curl->prev) return;
02558         
02559         text->curl= text->curl->prev;
02560         text->curc= text->curl->len;
02561         
02562         txt_combine_lines(text, text->curl, text->curl->next);
02563         txt_pop_sel(text);
02564     }
02565     else { /* Just backspacing a char */
02566         size_t c_len = 0;
02567         TextMarker *mrk;
02568         char *prev = BLI_str_prev_char_utf8(text->curl->line + text->curc);
02569         c= BLI_str_utf8_as_unicode_and_size(prev, &c_len);
02570 
02571         mrk= txt_find_marker_region(text, text->curl, text->curc - c_len, text->curl->len, 0, 0);
02572         if (mrk) {
02573             int lineno= mrk->lineno;
02574             if (mrk->start==text->curc) {
02575                 if ((mrk->flags & TMARK_TEMP) && !(mrk->flags & TMARK_EDITALL)) {
02576                     txt_clear_markers(text, mrk->group, TMARK_TEMP);
02577                 } else {
02578                     BLI_freelinkN(&text->markers, mrk);
02579                 }
02580                 return;
02581             }
02582             do {
02583                 if (mrk->start>text->curc - c_len) mrk->start-= c_len;
02584                 mrk->end-= c_len;
02585                 mrk= mrk->next;
02586             } while (mrk && mrk->lineno==lineno);
02587         }
02588         
02589         memcpy(text->curl->line + text->curc - c_len, text->curl->line + text->curc, text->curl->len-text->curc+1);
02590 
02591         text->curl->len-= c_len;
02592         text->curc-= c_len;
02593 
02594         txt_pop_sel(text);
02595     }
02596 
02597     txt_make_dirty(text);
02598     txt_clean_text(text);
02599     
02600     if(!undoing) txt_undo_add_charop(text, UNDO_BS_1, c);
02601 }
02602 
02603 void txt_backspace_word (Text *text) 
02604 {
02605     txt_jump_left(text, 1);
02606     txt_delete_sel(text);
02607 }
02608 
02609 /* Max spaces to replace a tab with, currently hardcoded to TXT_TABSIZE = 4.
02610  * Used by txt_convert_tab_to_spaces, indent and unintent.
02611  * Remember to change this string according to max tab size */
02612 static char tab_to_spaces[] = "    ";
02613 
02614 static void txt_convert_tab_to_spaces (Text *text)
02615 {
02616     /* sb aims to pad adjust the tab-width needed so that the right number of spaces
02617      * is added so that the indention of the line is the right width (i.e. aligned
02618      * to multiples of TXT_TABSIZE)
02619      */
02620     char *sb = &tab_to_spaces[text->curc % TXT_TABSIZE];
02621     txt_insert_buf(text, sb);
02622 }
02623 
02624 static int txt_add_char_intern (Text *text, unsigned int add, int replace_tabs)
02625 {
02626     int lineno;
02627     char *tmp, ch[BLI_UTF8_MAX];
02628     TextMarker *mrk;
02629     size_t add_len;
02630     
02631     if (!text) return 0;
02632     if (!text->curl) return 0;
02633 
02634     if (add=='\n') {
02635         txt_split_curline(text);
02636         return 1;
02637     }
02638     
02639     /* insert spaces rather than tabs */
02640     if (add == '\t' && replace_tabs) {
02641         txt_convert_tab_to_spaces(text);
02642         return 1;
02643     }
02644 
02645     txt_delete_sel(text);
02646     
02647     add_len = BLI_str_utf8_from_unicode(add, ch);
02648     mrk= txt_find_marker_region(text, text->curl, text->curc-1, text->curl->len, 0, 0);
02649     if (mrk) {
02650         lineno= mrk->lineno;
02651         do {
02652             if (mrk->start>text->curc) mrk->start+= add_len;
02653             mrk->end+= add_len;
02654             mrk= mrk->next;
02655         } while (mrk && mrk->lineno==lineno);
02656     }
02657     
02658     tmp= MEM_mallocN(text->curl->len+add_len+1, "textline_string");
02659     
02660     memcpy(tmp, text->curl->line, text->curc);
02661     memcpy(tmp+text->curc, ch, add_len);
02662     memcpy(tmp+text->curc+add_len, text->curl->line+text->curc, text->curl->len-text->curc+1);
02663 
02664     make_new_line(text->curl, tmp);
02665         
02666     text->curc+= add_len;
02667 
02668     txt_pop_sel(text);
02669     
02670     txt_make_dirty(text);
02671     txt_clean_text(text);
02672 
02673     if(!undoing) txt_undo_add_charop(text, UNDO_INSERT_1, add);
02674     return 1;
02675 }
02676 
02677 int txt_add_char (Text *text, unsigned int add)
02678 {
02679     return txt_add_char_intern(text, add, text->flags & TXT_TABSTOSPACES);
02680 }
02681 
02682 int txt_add_raw_char (Text *text, unsigned int add)
02683 {
02684     return txt_add_char_intern(text, add, 0);
02685 }
02686 
02687 void txt_delete_selected(Text *text)
02688 {
02689     txt_delete_sel(text);
02690     txt_make_dirty(text);
02691 }
02692 
02693 int txt_replace_char (Text *text, unsigned int add)
02694 {
02695     unsigned int del;
02696     size_t del_size = 0, add_size;
02697     char ch[BLI_UTF8_MAX];
02698     
02699     if (!text) return 0;
02700     if (!text->curl) return 0;
02701 
02702     /* If text is selected or we're at the end of the line just use txt_add_char */
02703     if (text->curc==text->curl->len || txt_has_sel(text) || add=='\n') {
02704         int i= txt_add_char(text, add);
02705         TextMarker *mrk= txt_find_marker(text, text->curl, text->curc, 0, 0);
02706         if (mrk) BLI_freelinkN(&text->markers, mrk);
02707         return i;
02708     }
02709     
02710     del= BLI_str_utf8_as_unicode_and_size(text->curl->line + text->curc, &del_size);
02711     add_size= BLI_str_utf8_from_unicode(add, ch);
02712     
02713     if (add_size > del_size) {
02714         char *tmp= MEM_mallocN(text->curl->len+add_size-del_size+1, "textline_string");
02715         memcpy(tmp, text->curl->line, text->curc);
02716         memcpy(tmp+text->curc+add_size, text->curl->line+text->curc+del_size, text->curl->len-text->curc-del_size+1);
02717         MEM_freeN(text->curl->line);
02718         text->curl->line = tmp;
02719     } else if (add_size < del_size) {
02720         char *tmp= text->curl->line;
02721         memmove(tmp+text->curc+add_size, tmp+text->curc+del_size, text->curl->len-text->curc-del_size+1);
02722     }
02723     
02724     memcpy(text->curl->line + text->curc, ch, add_size);
02725     text->curc+= add_size;
02726     
02727     txt_pop_sel(text);
02728     txt_make_dirty(text);
02729     txt_clean_text(text);
02730 
02731     /* Should probably create a new op for this */
02732     if(!undoing) {
02733         txt_undo_add_charop(text, UNDO_DEL_1, del);
02734         txt_undo_add_charop(text, UNDO_INSERT_1, add);
02735     }
02736     return 1;
02737 }
02738 
02739 void txt_indent(Text *text)
02740 {
02741     int len, num;
02742     char *tmp;
02743 
02744     const char *add = "\t";
02745     int indentlen = 1;
02746     
02747     /* hardcoded: TXT_TABSIZE = 4 spaces: */
02748     int spaceslen = TXT_TABSIZE;
02749 
02750     /* insert spaces rather than tabs */
02751     if (text->flags & TXT_TABSTOSPACES){
02752         add = tab_to_spaces;
02753         indentlen = spaceslen;
02754     }
02755     
02756     if (!text) return;
02757     if (!text->curl) return;
02758     if (!text->sell) return;
02759 
02760     num = 0;
02761     while (TRUE)
02762     {
02763         tmp= MEM_mallocN(text->curl->len+indentlen+1, "textline_string");
02764         
02765         text->curc = 0; 
02766         if(text->curc) memcpy(tmp, text->curl->line, text->curc); /* XXX never true, check prev line */
02767         memcpy(tmp+text->curc, add, indentlen);
02768         
02769         len= text->curl->len - text->curc;
02770         if(len>0) memcpy(tmp+text->curc+indentlen, text->curl->line+text->curc, len);
02771         tmp[text->curl->len+indentlen]= 0;
02772 
02773         make_new_line(text->curl, tmp);
02774             
02775         text->curc+= indentlen;
02776         
02777         txt_make_dirty(text);
02778         txt_clean_text(text);
02779         
02780         if(text->curl == text->sell) 
02781         {
02782             text->selc = text->sell->len;
02783             break;
02784         } else {
02785             text->curl = text->curl->next;
02786             num++;
02787         }
02788     }
02789     text->curc = 0;
02790     while( num > 0 )
02791     {
02792         text->curl = text->curl->prev;
02793         num--;
02794     }
02795     
02796     if(!undoing) 
02797     {
02798         txt_undo_add_toop(text, UNDO_INDENT, txt_get_span(text->lines.first, text->curl), text->curc, txt_get_span(text->lines.first, text->sell), text->selc);
02799     }
02800 }
02801 
02802 void txt_unindent(Text *text)
02803 {
02804     int num = 0;
02805     const char *remove = "\t";
02806     int indent = 1;
02807     
02808     /* hardcoded: TXT_TABSIZE = 4 spaces: */
02809     int spaceslen = TXT_TABSIZE;
02810 
02811     /* insert spaces rather than tabs */
02812     if (text->flags & TXT_TABSTOSPACES){
02813         remove = tab_to_spaces;
02814         indent = spaceslen;
02815     }
02816 
02817     if (!text) return;
02818     if (!text->curl) return;
02819     if (!text->sell) return;
02820 
02821     while(TRUE)
02822     {
02823         int i = 0;
02824         
02825         if (BLI_strncasecmp(text->curl->line, remove, indent) == 0)
02826         {
02827             while(i< text->curl->len) {
02828                 text->curl->line[i]= text->curl->line[i+indent];
02829                 i++;
02830             }
02831             text->curl->len-= indent;
02832         }
02833     
02834         txt_make_dirty(text);
02835         txt_clean_text(text);
02836         
02837         if(text->curl == text->sell) 
02838         {
02839             text->selc = text->sell->len;
02840             break;
02841         } else {
02842             text->curl = text->curl->next;
02843             num++;
02844         }
02845         
02846     }
02847     text->curc = 0;
02848     while( num > 0 )
02849     {
02850         text->curl = text->curl->prev;
02851         num--;
02852     }
02853     
02854     if(!undoing) 
02855     {
02856         txt_undo_add_toop(text, UNDO_UNINDENT, txt_get_span(text->lines.first, text->curl), text->curc, txt_get_span(text->lines.first, text->sell), text->selc);
02857     }
02858 }
02859 
02860 void txt_comment(Text *text)
02861 {
02862     int len, num;
02863     char *tmp;
02864     char add = '#';
02865     
02866     if (!text) return;
02867     if (!text->curl) return;
02868     if (!text->sell) return;// Need to change this need to check if only one line is selected to more then one
02869 
02870     num = 0;
02871     while (TRUE)
02872     {
02873         tmp= MEM_mallocN(text->curl->len+2, "textline_string");
02874         
02875         text->curc = 0; 
02876         if(text->curc) memcpy(tmp, text->curl->line, text->curc);
02877         tmp[text->curc]= add;
02878         
02879         len= text->curl->len - text->curc;
02880         if(len>0) memcpy(tmp+text->curc+1, text->curl->line+text->curc, len);
02881         tmp[text->curl->len+1]=0;
02882 
02883         make_new_line(text->curl, tmp);
02884             
02885         text->curc++;
02886         
02887         txt_make_dirty(text);
02888         txt_clean_text(text);
02889         
02890         if(text->curl == text->sell) 
02891         {
02892             text->selc = text->sell->len;
02893             break;
02894         } else {
02895             text->curl = text->curl->next;
02896             num++;
02897         }
02898     }
02899     text->curc = 0;
02900     while( num > 0 )
02901     {
02902         text->curl = text->curl->prev;
02903         num--;
02904     }
02905     
02906     if(!undoing) 
02907     {
02908         txt_undo_add_toop(text, UNDO_COMMENT, txt_get_span(text->lines.first, text->curl), text->curc, txt_get_span(text->lines.first, text->sell), text->selc);
02909     }
02910 }
02911 
02912 void txt_uncomment(Text *text)
02913 {
02914     int num = 0;
02915     char remove = '#';
02916     
02917     if (!text) return;
02918     if (!text->curl) return;
02919     if (!text->sell) return;
02920 
02921     while(TRUE)
02922     {
02923         int i = 0;
02924         
02925         if (text->curl->line[i] == remove)
02926         {
02927             while(i< text->curl->len) {
02928                 text->curl->line[i]= text->curl->line[i+1];
02929                 i++;
02930             }
02931             text->curl->len--;
02932         }
02933              
02934     
02935         txt_make_dirty(text);
02936         txt_clean_text(text);
02937         
02938         if(text->curl == text->sell) 
02939         {
02940             text->selc = text->sell->len;
02941             break;
02942         } else {
02943             text->curl = text->curl->next;
02944             num++;
02945         }
02946         
02947     }
02948     text->curc = 0;
02949     while( num > 0 )
02950     {
02951         text->curl = text->curl->prev;
02952         num--;
02953     }
02954     
02955     if(!undoing) 
02956     {
02957         txt_undo_add_toop(text, UNDO_UNCOMMENT, txt_get_span(text->lines.first, text->curl), text->curc, txt_get_span(text->lines.first, text->sell), text->selc);
02958     }
02959 }
02960 
02961 int setcurr_tab_spaces (Text *text, int space)
02962 {
02963     int i = 0;
02964     int test = 0;
02965     const char *word = ":";
02966     const char *comm = "#";
02967     const char indent= (text->flags & TXT_TABSTOSPACES) ? ' ' : '\t';
02968     static const char *back_words[]= {"return", "break", "continue", "pass", "yield", NULL};
02969     if (!text) return 0;
02970     if (!text->curl) return 0;
02971 
02972     while (text->curl->line[i] == indent)
02973     {
02974         //we only count those tabs/spaces that are before any text or before the curs;
02975         if (i == text->curc)
02976         {
02977             return i;
02978         } else {
02979             i++;
02980         }
02981     }
02982     if(strstr(text->curl->line, word))
02983     {
02984         /* if we find a ':' on this line, then add a tab but not if it is:
02985          *  1) in a comment
02986          *  2) within an identifier
02987          *  3) after the cursor (text->curc), i.e. when creating space before a function def [#25414] 
02988          */
02989         int a, is_indent = 0;
02990         for(a=0; (a < text->curc) && (text->curl->line[a] != '\0'); a++)
02991         {
02992             char ch= text->curl->line[a];
02993             if (ch=='#') {
02994                 break;
02995             } else if (ch==':') {
02996                 is_indent = 1;
02997             } else if (ch!=' ' && ch!='\t') {
02998                 is_indent = 0;
02999             }
03000         }
03001         if (is_indent) {
03002             i += space;
03003         }
03004     }
03005 
03006     for(test=0; back_words[test]; test++)
03007     {
03008         /* if there are these key words then remove a tab because we are done with the block */
03009         if(strstr(text->curl->line, back_words[test]) && i > 0)
03010         {
03011             if(strcspn(text->curl->line, back_words[test]) < strcspn(text->curl->line, comm))
03012             {
03013                 i -= space;
03014             }
03015         }
03016     }
03017     return i;
03018 }
03019 
03020 /*********************************/
03021 /* Text marker utility functions */
03022 /*********************************/
03023 
03024 /* Creates and adds a marker to the list maintaining sorted order */
03025 void txt_add_marker(Text *text, TextLine *line, int start, int end, const unsigned char color[4], int group, int flags)
03026 {
03027     TextMarker *tmp, *marker;
03028 
03029     marker= MEM_mallocN(sizeof(TextMarker), "text_marker");
03030     
03031     marker->lineno= txt_get_span(text->lines.first, line);
03032     marker->start= MIN2(start, end);
03033     marker->end= MAX2(start, end);
03034     marker->group= group;
03035     marker->flags= flags;
03036 
03037     marker->color[0]= color[0];
03038     marker->color[1]= color[1];
03039     marker->color[2]= color[2];
03040     marker->color[3]= color[3];
03041 
03042     for (tmp=text->markers.last; tmp; tmp=tmp->prev)
03043         if (tmp->lineno < marker->lineno || (tmp->lineno==marker->lineno && tmp->start < marker->start))
03044             break;
03045 
03046     if (tmp) BLI_insertlinkafter(&text->markers, tmp, marker);
03047     else BLI_addhead(&text->markers, marker);
03048 }
03049 
03050 /* Returns the first matching marker on the specified line between two points.
03051    If the group or flags fields are non-zero the returned flag must be in the
03052    specified group and have at least the specified flags set. */
03053 TextMarker *txt_find_marker_region(Text *text, TextLine *line, int start, int end, int group, int flags)
03054 {
03055     TextMarker *marker, *next;
03056     int lineno= txt_get_span(text->lines.first, line);
03057     
03058     for (marker=text->markers.first; marker; marker=next) {
03059         next= marker->next;
03060 
03061         if (group && marker->group != group) continue;
03062         else if ((marker->flags & flags) != flags) continue;
03063         else if (marker->lineno < lineno) continue;
03064         else if (marker->lineno > lineno) break;
03065 
03066         if ((marker->start==marker->end && start<=marker->start && marker->start<=end) ||
03067                 (marker->start<end && marker->end>start))
03068             return marker;
03069     }
03070     return NULL;
03071 }
03072 
03073 /* Clears all markers on the specified line between two points. If the group or
03074    flags fields are non-zero the returned flag must be in the specified group
03075    and have at least the specified flags set. */
03076 short txt_clear_marker_region(Text *text, TextLine *line, int start, int end, int group, int flags)
03077 {
03078     TextMarker *marker, *next;
03079     int lineno= txt_get_span(text->lines.first, line);
03080     short cleared= 0;
03081     
03082     for (marker=text->markers.first; marker; marker=next) {
03083         next= marker->next;
03084 
03085         if (group && marker->group != group) continue;
03086         else if ((marker->flags & flags) != flags) continue;
03087         else if (marker->lineno < lineno) continue;
03088         else if (marker->lineno > lineno) break;
03089 
03090         if ((marker->start==marker->end && start<=marker->start && marker->start<=end) ||
03091             (marker->start<end && marker->end>start)) {
03092             BLI_freelinkN(&text->markers, marker);
03093             cleared= 1;
03094         }
03095     }
03096     return cleared;
03097 }
03098 
03099 /* Clears all markers in the specified group (if given) with at least the
03100    specified flags set. Useful for clearing temporary markers (group=0,
03101    flags=TMARK_TEMP) */
03102 short txt_clear_markers(Text *text, int group, int flags)
03103 {
03104     TextMarker *marker, *next;
03105     short cleared= 0;
03106     
03107     for (marker=text->markers.first; marker; marker=next) {
03108         next= marker->next;
03109 
03110         if ((!group || marker->group==group) &&
03111                 (marker->flags & flags) == flags) {
03112             BLI_freelinkN(&text->markers, marker);
03113             cleared= 1;
03114         }
03115     }
03116     return cleared;
03117 }
03118 
03119 /* Finds the marker at the specified line and cursor position with at least the
03120    specified flags set in the given group (if non-zero). */
03121 TextMarker *txt_find_marker(Text *text, TextLine *line, int curs, int group, int flags)
03122 {
03123     TextMarker *marker;
03124     int lineno= txt_get_span(text->lines.first, line);
03125     
03126     for (marker=text->markers.first; marker; marker=marker->next) {
03127         if (group && marker->group != group) continue;
03128         else if ((marker->flags & flags) != flags) continue;
03129         else if (marker->lineno < lineno) continue;
03130         else if (marker->lineno > lineno) break;
03131 
03132         if (marker->start <= curs && curs <= marker->end)
03133             return marker;
03134     }
03135     return NULL;
03136 }
03137 
03138 /* Finds the previous marker in the same group. If no other is found, the same
03139    marker will be returned */
03140 TextMarker *txt_prev_marker(Text *text, TextMarker *marker)
03141 {
03142     TextMarker *tmp= marker;
03143     while (tmp) {
03144         if (tmp->prev) tmp= tmp->prev;
03145         else tmp= text->markers.last;
03146         if (tmp->group == marker->group)
03147             return tmp;
03148     }
03149     return NULL; /* Only if marker==NULL */
03150 }
03151 
03152 /* Finds the next marker in the same group. If no other is found, the same
03153    marker will be returned */
03154 TextMarker *txt_next_marker(Text *text, TextMarker *marker)
03155 {
03156     TextMarker *tmp= marker;
03157     while (tmp) {
03158         if (tmp->next) tmp= tmp->next;
03159         else tmp= text->markers.first;
03160         if (tmp->group == marker->group)
03161             return tmp;
03162     }
03163     return NULL; /* Only if marker==NULL */
03164 }
03165 
03166 
03167 /*******************************/
03168 /* Character utility functions */
03169 /*******************************/
03170 
03171 int text_check_bracket(char ch)
03172 {
03173     int a;
03174     char opens[] = "([{";
03175     char close[] = ")]}";
03176 
03177     for(a=0; a<(sizeof(opens)-1); a++) {
03178         if(ch==opens[a])
03179             return a+1;
03180         else if(ch==close[a])
03181             return -(a+1);
03182     }
03183     return 0;
03184 }
03185 
03186 int text_check_delim(char ch)
03187 {
03188     int a;
03189     char delims[] = "():\"\' ~!%^&*-+=[]{};/<>|.#\t,";
03190 
03191     for(a=0; a<(sizeof(delims)-1); a++) {
03192         if(ch==delims[a])
03193             return 1;
03194     }
03195     return 0;
03196 }
03197 
03198 int text_check_digit(char ch)
03199 {
03200     if(ch < '0') return 0;
03201     if(ch <= '9') return 1;
03202     return 0;
03203 }
03204 
03205 int text_check_identifier(char ch)
03206 {
03207     if(ch < '0') return 0;
03208     if(ch <= '9') return 1;
03209     if(ch < 'A') return 0;
03210     if(ch <= 'Z' || ch == '_') return 1;
03211     if(ch < 'a') return 0;
03212     if(ch <= 'z') return 1;
03213     return 0;
03214 }
03215 
03216 int text_check_whitespace(char ch)
03217 {
03218     if(ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
03219         return 1;
03220     return 0;
03221 }