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