Blender  V2.59
text_ops.c
Go to the documentation of this file.
00001 /*
00002  * $Id: text_ops.c 38751 2011-07-27 06:55:20Z campbellbarton $
00003  *
00004  * ***** BEGIN GPL LICENSE BLOCK *****
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License
00008  * as published by the Free Software Foundation; either version 2
00009  * of the License, or (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software Foundation,
00018  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019  *
00020  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
00021  * All rights reserved.
00022  *
00023  * The Original Code is: all of this file.
00024  *
00025  * Contributor(s): none yet.
00026  *
00027  * ***** END GPL LICENSE BLOCK *****
00028  */
00029 
00035 #include <stdlib.h>
00036 #include <string.h>
00037 #include <ctype.h> /* ispunct */
00038 #include <sys/stat.h>
00039 #include <errno.h>
00040 
00041 #include "MEM_guardedalloc.h"
00042 
00043 #include "DNA_text_types.h"
00044 #include "DNA_userdef_types.h"
00045 
00046 #include "BLI_blenlib.h"
00047 #include "BLI_utildefines.h"
00048 
00049 #include "PIL_time.h"
00050 
00051 #include "BKE_context.h"
00052 #include "BKE_global.h"
00053 #include "BKE_library.h"
00054 #include "BKE_main.h"
00055 #include "BKE_report.h"
00056 #include "BKE_text.h"
00057 
00058 #include "WM_api.h"
00059 #include "WM_types.h"
00060 
00061 #include "ED_text.h"
00062 #include "ED_curve.h"
00063 #include "ED_screen.h"
00064 #include "UI_interface.h"
00065 #include "UI_resources.h"
00066 
00067 #include "RNA_access.h"
00068 #include "RNA_define.h"
00069 
00070 #ifdef WITH_PYTHON
00071 #include "BPY_extern.h"
00072 #endif
00073 
00074 #include "text_intern.h"
00075 
00076 /************************ poll ***************************/
00077 
00078 static int text_new_poll(bContext *UNUSED(C))
00079 {
00080         return 1;
00081 }
00082 
00083 static int text_edit_poll(bContext *C)
00084 {
00085         Text *text= CTX_data_edit_text(C);
00086 
00087         if(!text)
00088                 return 0;
00089 
00090         if(text->id.lib) {
00091                 // BKE_report(op->reports, RPT_ERROR, "Can't edit external libdata");
00092                 return 0;
00093         }
00094 
00095         return 1;
00096 }
00097 
00098 static int text_space_edit_poll(bContext *C)
00099 {
00100         SpaceText *st= CTX_wm_space_text(C);
00101         Text *text= CTX_data_edit_text(C);
00102 
00103         if(!st || !text)
00104                 return 0;
00105 
00106         if(text->id.lib) {
00107                 // BKE_report(op->reports, RPT_ERROR, "Can't edit external libdata");
00108                 return 0;
00109         }
00110 
00111         return 1;
00112 }
00113 
00114 static int text_region_edit_poll(bContext *C)
00115 {
00116         SpaceText *st= CTX_wm_space_text(C);
00117         Text *text= CTX_data_edit_text(C);
00118         ARegion *ar= CTX_wm_region(C);
00119 
00120         if(!st || !text)
00121                 return 0;
00122         
00123         if(!ar || ar->regiontype != RGN_TYPE_WINDOW)
00124                 return 0;
00125 
00126         if(text->id.lib) {
00127                 // BKE_report(op->reports, RPT_ERROR, "Can't edit external libdata");
00128                 return 0;
00129         }
00130 
00131         return 1;
00132 }
00133 
00134 /********************** updates *********************/
00135 
00136 void text_update_line_edited(TextLine *line)
00137 {
00138         if(!line)
00139                 return;
00140 
00141         /* we just free format here, and let it rebuild during draw */
00142         if(line->format) {
00143                 MEM_freeN(line->format);
00144                 line->format= NULL;
00145         }
00146 }
00147 
00148 void text_update_edited(Text *text)
00149 {
00150         TextLine *line;
00151 
00152         for(line=text->lines.first; line; line=line->next)
00153                 text_update_line_edited(line);
00154 }
00155 
00156 /******************* new operator *********************/
00157 
00158 static int new_exec(bContext *C, wmOperator *UNUSED(op))
00159 {
00160         SpaceText *st= CTX_wm_space_text(C);
00161         Text *text;
00162         PointerRNA ptr, idptr;
00163         PropertyRNA *prop;
00164 
00165         text= add_empty_text("Text");
00166 
00167         /* hook into UI */
00168         uiIDContextProperty(C, &ptr, &prop);
00169 
00170         if(prop) {
00171                 /* when creating new ID blocks, use is already 1, but RNA
00172                  * pointer se also increases user, so this compensates it */
00173                 /* doesnt always seem to happen... (ton) */
00174                 if(text->id.us>1)
00175                         text->id.us--;
00176 
00177                 RNA_id_pointer_create(&text->id, &idptr);
00178                 RNA_property_pointer_set(&ptr, prop, idptr);
00179                 RNA_property_update(C, &ptr, prop);
00180         }
00181         else if(st) {
00182                 st->text= text;
00183                 st->top= 0;
00184                 text_drawcache_tag_update(st, 1);
00185         }
00186 
00187         WM_event_add_notifier(C, NC_TEXT|NA_ADDED, text);
00188 
00189         return OPERATOR_FINISHED;
00190 }
00191 
00192 void TEXT_OT_new(wmOperatorType *ot)
00193 {
00194         /* identifiers */
00195         ot->name= "New";
00196         ot->idname= "TEXT_OT_new";
00197         ot->description= "Create a new text data block";
00198         
00199         /* api callbacks */
00200         ot->exec= new_exec;
00201         ot->poll= text_new_poll;
00202         
00203         /* flags */
00204         ot->flag= OPTYPE_UNDO;
00205 }
00206 
00207 /******************* open operator *********************/
00208 
00209 static void open_init(bContext *C, wmOperator *op)
00210 {
00211         PropertyPointerRNA *pprop;
00212 
00213         op->customdata= pprop= MEM_callocN(sizeof(PropertyPointerRNA), "OpenPropertyPointerRNA");
00214         uiIDContextProperty(C, &pprop->ptr, &pprop->prop);
00215 }
00216 
00217 static int open_cancel(bContext *UNUSED(C), wmOperator *op)
00218 {
00219         MEM_freeN(op->customdata);
00220         return OPERATOR_CANCELLED;
00221 }
00222 
00223 static int open_exec(bContext *C, wmOperator *op)
00224 {
00225         SpaceText *st= CTX_wm_space_text(C);
00226         Text *text;
00227         PropertyPointerRNA *pprop;
00228         PointerRNA idptr;
00229         char str[FILE_MAX];
00230         short internal = RNA_boolean_get(op->ptr, "internal");
00231 
00232         RNA_string_get(op->ptr, "filepath", str);
00233 
00234         text= add_text(str, G.main->name);
00235 
00236         if(!text) {
00237                 if(op->customdata) MEM_freeN(op->customdata);
00238                 return OPERATOR_CANCELLED;
00239         }
00240 
00241         if(!op->customdata)
00242                 open_init(C, op);
00243 
00244         /* hook into UI */
00245         pprop= op->customdata;
00246 
00247         if(pprop->prop) {
00248                 /* when creating new ID blocks, use is already 1, but RNA
00249                  * pointer se also increases user, so this compensates it */
00250                 text->id.us--;
00251 
00252                 RNA_id_pointer_create(&text->id, &idptr);
00253                 RNA_property_pointer_set(&pprop->ptr, pprop->prop, idptr);
00254                 RNA_property_update(C, &pprop->ptr, pprop->prop);
00255         }
00256         else if(st) {
00257                 st->text= text;
00258                 st->top= 0;
00259         }
00260         
00261         if (internal) {
00262                 if(text->name)
00263                         MEM_freeN(text->name);
00264                 
00265                 text->name = NULL;
00266         }
00267 
00268         text_drawcache_tag_update(st, 1);
00269         WM_event_add_notifier(C, NC_TEXT|NA_ADDED, text);
00270 
00271         MEM_freeN(op->customdata);
00272 
00273         return OPERATOR_FINISHED;
00274 }
00275 
00276 static int open_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
00277 {
00278         Text *text= CTX_data_edit_text(C);
00279         char *path= (text && text->name)? text->name: G.main->name;
00280 
00281         if(RNA_property_is_set(op->ptr, "filepath"))
00282                 return open_exec(C, op);
00283         
00284         open_init(C, op);
00285         RNA_string_set(op->ptr, "filepath", path);
00286         WM_event_add_fileselect(C, op); 
00287 
00288         return OPERATOR_RUNNING_MODAL;
00289 }
00290 
00291 void TEXT_OT_open(wmOperatorType *ot)
00292 {
00293         /* identifiers */
00294         ot->name= "Open Text Block";
00295         ot->idname= "TEXT_OT_open";
00296         ot->description= "Open a new text data block";
00297 
00298         /* api callbacks */
00299         ot->exec= open_exec;
00300         ot->invoke= open_invoke;
00301         ot->cancel= open_cancel;
00302         ot->poll= text_new_poll;
00303 
00304         /* flags */
00305         ot->flag= OPTYPE_UNDO;
00306         
00307         /* properties */
00308         WM_operator_properties_filesel(ot, FOLDERFILE|TEXTFILE|PYSCRIPTFILE, FILE_SPECIAL, FILE_OPENFILE, WM_FILESEL_FILEPATH);  //XXX TODO, relative_path
00309         RNA_def_boolean(ot->srna, "internal", 0, "Make internal", "Make text file internal after loading");
00310 }
00311 
00312 /******************* reload operator *********************/
00313 
00314 static int reload_exec(bContext *C, wmOperator *op)
00315 {
00316         Text *text= CTX_data_edit_text(C);
00317 
00318         if(!reopen_text(text)) {
00319                 BKE_report(op->reports, RPT_ERROR, "Could not reopen file");
00320                 return OPERATOR_CANCELLED;
00321         }
00322 
00323 #ifdef WITH_PYTHON
00324         if(text->compiled)
00325                 BPY_text_free_code(text);
00326 #endif
00327 
00328         text_update_edited(text);
00329         text_update_cursor_moved(C);
00330         text_drawcache_tag_update(CTX_wm_space_text(C), 1);
00331         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
00332 
00333         return OPERATOR_FINISHED;
00334 }
00335 
00336 void TEXT_OT_reload(wmOperatorType *ot)
00337 {
00338         /* identifiers */
00339         ot->name= "Reload";
00340         ot->idname= "TEXT_OT_reload";
00341         ot->description= "Reload active text data block from its file";
00342         
00343         /* api callbacks */
00344         ot->exec= reload_exec;
00345         ot->invoke= WM_operator_confirm;
00346         ot->poll= text_edit_poll;
00347 }
00348 
00349 /******************* delete operator *********************/
00350 
00351 static int text_unlink_poll(bContext *C)
00352 {
00353         /* it should be possible to unlink texts if they're lib-linked in... */
00354         return CTX_data_edit_text(C) != NULL;
00355 }
00356 
00357 static int unlink_exec(bContext *C, wmOperator *UNUSED(op))
00358 {
00359         Main *bmain= CTX_data_main(C);
00360         SpaceText *st= CTX_wm_space_text(C);
00361         Text *text= CTX_data_edit_text(C);
00362 
00363         /* make the previous text active, if its not there make the next text active */
00364         if(st) {
00365                 if(text->id.prev) {
00366                         st->text = text->id.prev;
00367                         text_update_cursor_moved(C);
00368                         WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
00369                 }
00370                 else if(text->id.next) {
00371                         st->text = text->id.next;
00372                         text_update_cursor_moved(C);
00373                         WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
00374                 }
00375         }
00376 
00377         unlink_text(bmain, text);
00378         free_libblock(&bmain->text, text);
00379 
00380         text_drawcache_tag_update(st, 1);
00381         WM_event_add_notifier(C, NC_TEXT|NA_REMOVED, NULL);
00382 
00383         return OPERATOR_FINISHED;
00384 }
00385 
00386 void TEXT_OT_unlink(wmOperatorType *ot)
00387 {
00388         /* identifiers */
00389         ot->name= "Unlink";
00390         ot->idname= "TEXT_OT_unlink";
00391         ot->description= "Unlink active text data block";
00392         
00393         /* api callbacks */
00394         ot->exec= unlink_exec;
00395         ot->invoke= WM_operator_confirm;
00396         ot->poll= text_unlink_poll;
00397         
00398         /* flags */
00399         ot->flag= OPTYPE_UNDO;
00400 }
00401 
00402 /******************* make internal operator *********************/
00403 
00404 static int make_internal_exec(bContext *C, wmOperator *UNUSED(op))
00405 {
00406         Text *text= CTX_data_edit_text(C);
00407 
00408         text->flags |= TXT_ISMEM | TXT_ISDIRTY;
00409 
00410         if(text->name) {
00411                 MEM_freeN(text->name);
00412                 text->name= NULL;
00413         }
00414 
00415         text_update_cursor_moved(C);
00416         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
00417 
00418         return OPERATOR_FINISHED;
00419 }
00420 
00421 void TEXT_OT_make_internal(wmOperatorType *ot)
00422 {
00423         /* identifiers */
00424         ot->name= "Make Internal";
00425         ot->idname= "TEXT_OT_make_internal";
00426         ot->description= "Make active text file internal";
00427 
00428         /* api callbacks */
00429         ot->exec= make_internal_exec;
00430         ot->poll= text_edit_poll;
00431         
00432         /* flags */
00433         ot->flag= OPTYPE_UNDO;
00434 }
00435 
00436 /******************* save operator *********************/
00437 
00438 static int save_poll(bContext *C)
00439 {
00440         Text *text= CTX_data_edit_text(C);
00441 
00442         if(!text_edit_poll(C))
00443                 return 0;
00444         
00445         return (text->name != NULL && !(text->flags & TXT_ISMEM));
00446 }
00447 
00448 static void txt_write_file(Text *text, ReportList *reports) 
00449 {
00450         FILE *fp;
00451         TextLine *tmp;
00452         struct stat st;
00453         char filepath[FILE_MAXDIR+FILE_MAXFILE];
00454         
00455         BLI_strncpy(filepath, text->name, FILE_MAXDIR+FILE_MAXFILE);
00456         BLI_path_abs(filepath, G.main->name);
00457         
00458         fp= fopen(filepath, "w");
00459         if(fp==NULL) {
00460                 BKE_reportf(reports, RPT_ERROR, "Unable to save \"%s\": %s.", filepath, errno ? strerror(errno) : "Unknown error writing file");
00461                 return;
00462         }
00463 
00464         tmp= text->lines.first;
00465         while(tmp) {
00466                 if(tmp->next) fprintf(fp, "%s\n", tmp->line);
00467                 else fprintf(fp, "%s", tmp->line);
00468                 
00469                 tmp= tmp->next;
00470         }
00471         
00472         fclose (fp);
00473 
00474         if(stat(filepath, &st) == 0) {
00475                 text->mtime= st.st_mtime;
00476         }
00477         else {
00478                 text->mtime= 0;
00479                 BKE_reportf(reports, RPT_WARNING, "Unable to stat \"%s\": %s.", filepath, errno ? strerror(errno) : "Unknown error starrng file");
00480         }
00481         
00482         if(text->flags & TXT_ISDIRTY)
00483                 text->flags ^= TXT_ISDIRTY;
00484 }
00485 
00486 static int save_exec(bContext *C, wmOperator *op)
00487 {
00488         Text *text= CTX_data_edit_text(C);
00489 
00490         txt_write_file(text, op->reports);
00491 
00492         text_update_cursor_moved(C);
00493         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
00494 
00495         return OPERATOR_FINISHED;
00496 }
00497 
00498 void TEXT_OT_save(wmOperatorType *ot)
00499 {
00500         /* identifiers */
00501         ot->name= "Save";
00502         ot->idname= "TEXT_OT_save";
00503         ot->description= "Save active text data block";
00504 
00505         /* api callbacks */
00506         ot->exec= save_exec;
00507         ot->poll= save_poll;
00508 }
00509 
00510 /******************* save as operator *********************/
00511 
00512 static int save_as_exec(bContext *C, wmOperator *op)
00513 {
00514         Text *text= CTX_data_edit_text(C);
00515         char str[FILE_MAX];
00516 
00517         if(!text)
00518                 return OPERATOR_CANCELLED;
00519 
00520         RNA_string_get(op->ptr, "filepath", str);
00521 
00522         if(text->name) MEM_freeN(text->name);
00523         text->name= BLI_strdup(str);
00524         text->flags &= ~TXT_ISMEM;
00525 
00526         txt_write_file(text, op->reports);
00527 
00528         text_update_cursor_moved(C);
00529         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
00530 
00531         return OPERATOR_FINISHED;
00532 }
00533 
00534 static int save_as_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
00535 {
00536         Text *text= CTX_data_edit_text(C);
00537         char *str;
00538 
00539         if(RNA_property_is_set(op->ptr, "filepath"))
00540                 return save_as_exec(C, op);
00541 
00542         if(text->name)
00543                 str= text->name;
00544         else if(text->flags & TXT_ISMEM)
00545                 str= text->id.name+2;
00546         else
00547                 str= G.main->name;
00548         
00549         RNA_string_set(op->ptr, "filepath", str);
00550         WM_event_add_fileselect(C, op); 
00551 
00552         return OPERATOR_RUNNING_MODAL;
00553 }
00554 
00555 void TEXT_OT_save_as(wmOperatorType *ot)
00556 {
00557         /* identifiers */
00558         ot->name= "Save As";
00559         ot->idname= "TEXT_OT_save_as";
00560         ot->description= "Save active text file with options";
00561         
00562         /* api callbacks */
00563         ot->exec= save_as_exec;
00564         ot->invoke= save_as_invoke;
00565         ot->poll= text_edit_poll;
00566 
00567         /* properties */
00568         WM_operator_properties_filesel(ot, FOLDERFILE|TEXTFILE|PYSCRIPTFILE, FILE_SPECIAL, FILE_SAVE, WM_FILESEL_FILEPATH);  //XXX TODO, relative_path
00569 }
00570 
00571 /******************* run script operator *********************/
00572 
00573 static int run_script_poll(bContext *C)
00574 {
00575         return (CTX_data_edit_text(C) != NULL);
00576 }
00577 
00578 static int run_script(bContext *C, ReportList *reports)
00579 {
00580 #ifdef WITH_PYTHON
00581         Text *text= CTX_data_edit_text(C);
00582         const short is_live= (reports == NULL);
00583 
00584         /* only for comparison */
00585         void *curl_prev= text->curl;
00586         int curc_prev= text->curc;
00587 
00588         if (BPY_text_exec(C, text, reports, !is_live)) {
00589                 if(is_live) {
00590                         /* for nice live updates */
00591                         WM_event_add_notifier(C, NC_WINDOW|NA_EDITED, NULL);
00592                 }
00593                 return OPERATOR_FINISHED;
00594         }
00595 
00596         /* Dont report error messages while live editing */
00597         if(!is_live) {
00598                 if(text->curl != curl_prev || curc_prev != text->curc) {
00599                         text_update_cursor_moved(C);
00600                         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
00601                 }
00602 
00603                 BKE_report(reports, RPT_ERROR, "Python script fail, look in the console for now...");
00604         }
00605 #else
00606         (void)C;
00607         (void)reports;
00608 #endif /* !WITH_PYTHON */
00609         return OPERATOR_CANCELLED;
00610 }
00611 
00612 static int run_script_exec(bContext *C, wmOperator *op)
00613 {
00614 #ifndef WITH_PYTHON
00615         (void)C; /* unused */
00616 
00617         BKE_report(op->reports, RPT_ERROR, "Python disabled in this build");
00618 
00619         return OPERATOR_CANCELLED;
00620 #else
00621         return run_script(C, op->reports);
00622 #endif
00623 }
00624 
00625 void TEXT_OT_run_script(wmOperatorType *ot)
00626 {
00627         /* identifiers */
00628         ot->name= "Run Script";
00629         ot->idname= "TEXT_OT_run_script";
00630         ot->description= "Run active script";
00631         
00632         /* api callbacks */
00633         ot->poll= run_script_poll;
00634         ot->exec= run_script_exec;
00635 
00636         /* flags */
00637         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00638 }
00639 
00640 /******************* refresh pyconstraints operator *********************/
00641 
00642 static int refresh_pyconstraints_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
00643 {
00644 #ifdef WITH_PYTHON
00645 #if 0
00646         Text *text= CTX_data_edit_text(C);
00647         Object *ob;
00648         bConstraint *con;
00649         short update;
00650         
00651         /* check all pyconstraints */
00652         for(ob= CTX_data_main(C)->object.first; ob; ob= ob->id.next) {
00653                 update = 0;
00654                 if(ob->type==OB_ARMATURE && ob->pose) {
00655                         bPoseChannel *pchan;
00656                         for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
00657                                 for(con = pchan->constraints.first; con; con= con->next) {
00658                                         if(con->type==CONSTRAINT_TYPE_PYTHON) {
00659                                                 bPythonConstraint *data = con->data;
00660                                                 if(data->text==text) BPY_pyconstraint_update(ob, con);
00661                                                 update = 1;
00662                                                 
00663                                         }
00664                                 }
00665                         }
00666                 }
00667                 for(con = ob->constraints.first; con; con= con->next) {
00668                         if(con->type==CONSTRAINT_TYPE_PYTHON) {
00669                                 bPythonConstraint *data = con->data;
00670                                 if(data->text==text) BPY_pyconstraint_update(ob, con);
00671                                 update = 1;
00672                         }
00673                 }
00674                 
00675                 if(update) {
00676                         DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
00677                 }
00678         }
00679 #endif
00680 #endif
00681 
00682         return OPERATOR_FINISHED;
00683 }
00684 
00685 void TEXT_OT_refresh_pyconstraints(wmOperatorType *ot)
00686 {
00687         /* identifiers */
00688         ot->name= "Refresh PyConstraints";
00689         ot->idname= "TEXT_OT_refresh_pyconstraints";
00690         ot->description= "Refresh all pyconstraints";
00691         
00692         /* api callbacks */
00693         ot->exec= refresh_pyconstraints_exec;
00694         ot->poll= text_edit_poll;
00695 }
00696 
00697 /******************* paste operator *********************/
00698 
00699 static char *txt_copy_selected(Text *text)
00700 {
00701         TextLine *tmp, *linef, *linel;
00702         char *buf= NULL;
00703         int charf, charl, length= 0;
00704         
00705         if(!text) return NULL;
00706         if(!text->curl) return NULL;
00707         if(!text->sell) return NULL;
00708 
00709         if(!txt_has_sel(text)) return NULL;
00710 
00711         if(text->curl==text->sell) {
00712                 linef= linel= text->curl;
00713                 
00714                 if(text->curc < text->selc) {
00715                         charf= text->curc;
00716                         charl= text->selc;
00717                 }
00718                 else{
00719                         charf= text->selc;
00720                         charl= text->curc;
00721                 }
00722         }
00723         else if(txt_get_span(text->curl, text->sell)<0) {
00724                 linef= text->sell;
00725                 linel= text->curl;
00726 
00727                 charf= text->selc;              
00728                 charl= text->curc;
00729         }
00730         else {
00731                 linef= text->curl;
00732                 linel= text->sell;
00733                 
00734                 charf= text->curc;
00735                 charl= text->selc;
00736         }
00737 
00738         if(linef == linel) {
00739                 length= charl-charf;
00740 
00741                 buf= MEM_callocN(length+1, "cut buffera");
00742                 
00743                 BLI_strncpy(buf, linef->line + charf, length+1);
00744         }
00745         else {
00746                 length+= linef->len - charf;
00747                 length+= charl;
00748                 length++; /* For the '\n' */
00749                 
00750                 tmp= linef->next;
00751                 while(tmp && tmp!= linel) {
00752                         length+= tmp->len+1;
00753                         tmp= tmp->next;
00754                 }
00755                 
00756                 buf= MEM_callocN(length+1, "cut bufferb");
00757                 
00758                 strncpy(buf, linef->line+ charf, linef->len-charf);
00759                 length= linef->len-charf;
00760                 
00761                 buf[length++]='\n';
00762                 
00763                 tmp= linef->next;
00764                 while(tmp && tmp!=linel) {
00765                         strncpy(buf+length, tmp->line, tmp->len);
00766                         length+= tmp->len;
00767                         
00768                         buf[length++]='\n';                     
00769                         
00770                         tmp= tmp->next;
00771                 }
00772                 strncpy(buf+length, linel->line, charl);
00773                 length+= charl;
00774                 
00775                 buf[length]=0;
00776         }
00777 
00778         return buf;
00779 }
00780 
00781 static int paste_exec(bContext *C, wmOperator *op)
00782 {
00783         Text *text= CTX_data_edit_text(C);
00784         char *buf;
00785         int selection= RNA_boolean_get(op->ptr, "selection");
00786 
00787         buf= WM_clipboard_text_get(selection);
00788 
00789         if(!buf)
00790                 return OPERATOR_CANCELLED;
00791 
00792         text_drawcache_tag_update(CTX_wm_space_text(C), 0);
00793 
00794         txt_insert_buf(text, buf);
00795         text_update_edited(text);
00796 
00797         MEM_freeN(buf);
00798 
00799         text_update_cursor_moved(C);
00800         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
00801 
00802         /* run the script while editing, evil but useful */
00803         if(CTX_wm_space_text(C)->live_edit)
00804                 run_script(C, NULL);
00805 
00806         return OPERATOR_FINISHED;
00807 }
00808 
00809 void TEXT_OT_paste(wmOperatorType *ot)
00810 {
00811         /* identifiers */
00812         ot->name= "Paste";
00813         ot->idname= "TEXT_OT_paste";
00814         ot->description= "Paste text from clipboard";
00815         
00816         /* api callbacks */
00817         ot->exec= paste_exec;
00818         ot->poll= text_edit_poll;
00819         
00820         /* properties */
00821         RNA_def_boolean(ot->srna, "selection", 0, "Selection", "Paste text selected elsewhere rather than copied, X11 only.");
00822 }
00823 
00824 /******************* copy operator *********************/
00825 
00826 static void txt_copy_clipboard(Text *text)
00827 {
00828         char *buf;
00829 
00830         buf= txt_copy_selected(text);
00831 
00832         if(buf) {
00833                 WM_clipboard_text_set(buf, 0);
00834                 MEM_freeN(buf);
00835         }
00836 }
00837 
00838 static int copy_exec(bContext *C, wmOperator *UNUSED(op))
00839 {
00840         Text *text= CTX_data_edit_text(C);
00841 
00842         txt_copy_clipboard(text);
00843 
00844         return OPERATOR_FINISHED;
00845 }
00846 
00847 void TEXT_OT_copy(wmOperatorType *ot)
00848 {
00849         /* identifiers */
00850         ot->name= "Copy";
00851         ot->idname= "TEXT_OT_copy";
00852         ot->description= "Copy selected text to clipboard";
00853 
00854         /* api callbacks */
00855         ot->exec= copy_exec;
00856         ot->poll= text_edit_poll;
00857 }
00858 
00859 /******************* cut operator *********************/
00860 
00861 static int cut_exec(bContext *C, wmOperator *UNUSED(op))
00862 {
00863         Text *text= CTX_data_edit_text(C);
00864 
00865         text_drawcache_tag_update(CTX_wm_space_text(C), 0);
00866 
00867         txt_copy_clipboard(text);
00868         txt_delete_selected(text);
00869 
00870         text_update_cursor_moved(C);
00871         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
00872 
00873         /* run the script while editing, evil but useful */
00874         if(CTX_wm_space_text(C)->live_edit)
00875                 run_script(C, NULL);
00876 
00877         return OPERATOR_FINISHED;
00878 }
00879 
00880 void TEXT_OT_cut(wmOperatorType *ot)
00881 {
00882         /* identifiers */
00883         ot->name= "Cut";
00884         ot->idname= "TEXT_OT_cut";
00885         ot->description= "Cut selected text to clipboard";
00886         
00887         /* api callbacks */
00888         ot->exec= cut_exec;
00889         ot->poll= text_edit_poll;
00890 }
00891 
00892 /******************* indent operator *********************/
00893 
00894 static int indent_exec(bContext *C, wmOperator *UNUSED(op))
00895 {
00896         Text *text= CTX_data_edit_text(C);
00897 
00898         text_drawcache_tag_update(CTX_wm_space_text(C), 0);
00899 
00900         if(txt_has_sel(text)) {
00901                 txt_order_cursors(text);
00902                 txt_indent(text);
00903         }
00904         else
00905                 txt_add_char(text, '\t');
00906 
00907         text_update_edited(text);
00908 
00909         text_update_cursor_moved(C);
00910         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
00911 
00912         return OPERATOR_FINISHED;
00913 }
00914 
00915 void TEXT_OT_indent(wmOperatorType *ot)
00916 {
00917         /* identifiers */
00918         ot->name= "Indent";
00919         ot->idname= "TEXT_OT_indent";
00920         ot->description= "Indent selected text";
00921         
00922         /* api callbacks */
00923         ot->exec= indent_exec;
00924         ot->poll= text_edit_poll;
00925 }
00926 
00927 /******************* unindent operator *********************/
00928 
00929 static int unindent_exec(bContext *C, wmOperator *UNUSED(op))
00930 {
00931         Text *text= CTX_data_edit_text(C);
00932 
00933         if(txt_has_sel(text)) {
00934                 text_drawcache_tag_update(CTX_wm_space_text(C), 0);
00935 
00936                 txt_order_cursors(text);
00937                 txt_unindent(text);
00938 
00939                 text_update_edited(text);
00940 
00941                 text_update_cursor_moved(C);
00942                 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
00943 
00944                 return OPERATOR_FINISHED;
00945         }
00946 
00947         return OPERATOR_CANCELLED;
00948 }
00949 
00950 void TEXT_OT_unindent(wmOperatorType *ot)
00951 {
00952         /* identifiers */
00953         ot->name= "Unindent";
00954         ot->idname= "TEXT_OT_unindent";
00955         ot->description= "Unindent selected text";
00956         
00957         /* api callbacks */
00958         ot->exec= unindent_exec;
00959         ot->poll= text_edit_poll;
00960 }
00961 
00962 /******************* line break operator *********************/
00963 
00964 static int line_break_exec(bContext *C, wmOperator *UNUSED(op))
00965 {
00966         SpaceText *st= CTX_wm_space_text(C);
00967         Text *text= CTX_data_edit_text(C);
00968         int a, curts;
00969         int space = (text->flags & TXT_TABSTOSPACES) ? st->tabnumber : 1;
00970 
00971         text_drawcache_tag_update(st, 0);
00972 
00973         // double check tabs/spaces before splitting the line
00974         curts= setcurr_tab_spaces(text, space);
00975         txt_split_curline(text);
00976 
00977         for(a=0; a < curts; a++) {
00978                 if (text->flags & TXT_TABSTOSPACES) {
00979                         txt_add_char(text, ' ');
00980                 } else {
00981                         txt_add_char(text, '\t');
00982                 }
00983         }
00984 
00985         if(text->curl) {
00986                 if(text->curl->prev)
00987                         text_update_line_edited(text->curl->prev);
00988                 text_update_line_edited(text->curl);
00989         }
00990 
00991         text_update_cursor_moved(C);
00992         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
00993 
00994         return OPERATOR_CANCELLED;
00995 }
00996 
00997 void TEXT_OT_line_break(wmOperatorType *ot)
00998 {
00999         /* identifiers */
01000         ot->name= "Line Break";
01001         ot->idname= "TEXT_OT_line_break";
01002         ot->description= "Insert line break at cursor position";
01003         
01004         /* api callbacks */
01005         ot->exec= line_break_exec;
01006         ot->poll= text_edit_poll;
01007 }
01008 
01009 /******************* comment operator *********************/
01010 
01011 static int comment_exec(bContext *C, wmOperator *UNUSED(op))
01012 {
01013         Text *text= CTX_data_edit_text(C);
01014 
01015         if(txt_has_sel(text)) {
01016                 text_drawcache_tag_update(CTX_wm_space_text(C), 0);
01017 
01018                 txt_order_cursors(text);
01019                 txt_comment(text);
01020                 text_update_edited(text);
01021 
01022                 text_update_cursor_moved(C);
01023                 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
01024                 return OPERATOR_FINISHED;
01025         }
01026 
01027         return OPERATOR_CANCELLED;
01028 }
01029 
01030 void TEXT_OT_comment(wmOperatorType *ot)
01031 {
01032         /* identifiers */
01033         ot->name= "Comment";
01034         ot->idname= "TEXT_OT_comment";
01035         ot->description= "Convert selected text to comment";
01036         
01037         /* api callbacks */
01038         ot->exec= comment_exec;
01039         ot->poll= text_edit_poll;
01040 }
01041 
01042 /******************* uncomment operator *********************/
01043 
01044 static int uncomment_exec(bContext *C, wmOperator *UNUSED(op))
01045 {
01046         Text *text= CTX_data_edit_text(C);
01047 
01048         if(txt_has_sel(text)) {
01049                 text_drawcache_tag_update(CTX_wm_space_text(C), 0);
01050 
01051                 txt_order_cursors(text);
01052                 txt_uncomment(text);
01053                 text_update_edited(text);
01054 
01055                 text_update_cursor_moved(C);
01056                 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
01057 
01058                 return OPERATOR_FINISHED;
01059         }
01060 
01061         return OPERATOR_CANCELLED;
01062 }
01063 
01064 void TEXT_OT_uncomment(wmOperatorType *ot)
01065 {
01066         /* identifiers */
01067         ot->name= "Uncomment";
01068         ot->idname= "TEXT_OT_uncomment";
01069         ot->description= "Convert selected comment to text";
01070         
01071         /* api callbacks */
01072         ot->exec= uncomment_exec;
01073         ot->poll= text_edit_poll;
01074 }
01075 
01076 /******************* convert whitespace operator *********************/
01077 
01078 enum { TO_SPACES, TO_TABS };
01079 static EnumPropertyItem whitespace_type_items[]= {
01080         {TO_SPACES, "SPACES", 0, "To Spaces", NULL},
01081         {TO_TABS, "TABS", 0, "To Tabs", NULL},
01082         {0, NULL, 0, NULL, NULL}};
01083 
01084 static int convert_whitespace_exec(bContext *C, wmOperator *op)
01085 {
01086         SpaceText *st= CTX_wm_space_text(C);
01087         Text *text= CTX_data_edit_text(C);
01088         TextLine *tmp;
01089         FlattenString fs;
01090         size_t a, j;
01091         char *text_check_line, *new_line;
01092         int extra, number; //unknown for now
01093         int type= RNA_enum_get(op->ptr, "type");
01094         
01095         tmp = text->lines.first;
01096         
01097         //first convert to all space, this make it a lot easier to convert to tabs because there is no mixtures of ' ' && '\t'
01098         while(tmp) {
01099                 text_check_line = tmp->line;
01100                 number = flatten_string(st, &fs, text_check_line)+1;
01101                 flatten_string_free(&fs);
01102                 new_line = MEM_callocN(number, "Converted_Line");
01103                 j = 0;
01104                 for(a=0; a < strlen(text_check_line); a++) { //foreach char in line
01105                         if(text_check_line[a] == '\t') { //checking for tabs
01106                                 //get the number of spaces this tabs is showing
01107                                 //i dont like doing it this way but will look into it later
01108                                 new_line[j] = '\0';
01109                                 number = flatten_string(st, &fs, new_line);
01110                                 flatten_string_free(&fs);
01111                                 new_line[j] = '\t';
01112                                 new_line[j+1] = '\0';
01113                                 number = flatten_string(st, &fs, new_line)-number;
01114                                 flatten_string_free(&fs);
01115 
01116                                 for(extra = 0; extra < number; extra++) {
01117                                         new_line[j] = ' ';
01118                                         j++;
01119                                 }
01120                         }
01121                         else {
01122                                 new_line[j] = text_check_line[a];
01123                                 ++j;
01124                         }
01125                 }
01126                 new_line[j] = '\0';
01127                 // put new_line in the tmp->line spot still need to try and set the curc correctly
01128                 if(tmp->line) MEM_freeN(tmp->line);
01129                 if(tmp->format) MEM_freeN(tmp->format);
01130                 
01131                 tmp->line = new_line;
01132                 tmp->len = strlen(new_line);
01133                 tmp->format = NULL;
01134                 tmp = tmp->next;
01135         }
01136         
01137         if(type == TO_TABS) // Converting to tabs
01138         {       //start over from the begining
01139                 tmp = text->lines.first;
01140                 
01141                 while(tmp) {
01142                         text_check_line = tmp->line;
01143                         extra = 0;
01144                         for(a = 0; a < strlen(text_check_line); a++) {
01145                                 number = 0;
01146                                 for(j = 0; j < (size_t)st->tabnumber; j++) {
01147                                         if((a+j) <= strlen(text_check_line)) { //check to make sure we are not pass the end of the line
01148                                                 if(text_check_line[a+j] != ' ') {
01149                                                         number = 1;
01150                                                 }
01151                                         }
01152                                 }
01153                                 if(!number) { //found all number of space to equal a tab
01154                                         a = a+(st->tabnumber-1);
01155                                         extra = extra+1;
01156                                 }
01157                         }
01158                         
01159                         if( extra > 0 ) { //got tabs make malloc and do what you have to do
01160                                 new_line = MEM_callocN(strlen(text_check_line)-(((st->tabnumber*extra)-extra)-1), "Converted_Line");
01161                                 extra = 0; //reuse vars
01162                                 for(a = 0; a < strlen(text_check_line); a++) {
01163                                         number = 0;
01164                                         for(j = 0; j < (size_t)st->tabnumber; j++) {
01165                                                 if((a+j) <= strlen(text_check_line)) { //check to make sure we are not pass the end of the line
01166                                                         if(text_check_line[a+j] != ' ') {
01167                                                                 number = 1;
01168                                                         }
01169                                                 }
01170                                         }
01171 
01172                                         if(!number) { //found all number of space to equal a tab
01173                                                 new_line[extra] = '\t';
01174                                                 a = a+(st->tabnumber-1);
01175                                                 ++extra;
01176                                                 
01177                                         }
01178                                         else { //not adding a tab
01179                                                 new_line[extra] = text_check_line[a];
01180                                                 ++extra;
01181                                         }
01182                                 }
01183                                 new_line[extra] = '\0';
01184                                 // put new_line in the tmp->line spot still need to try and set the curc correctly
01185                                 if(tmp->line) MEM_freeN(tmp->line);
01186                                 if(tmp->format) MEM_freeN(tmp->format);
01187                                 
01188                                 tmp->line = new_line;
01189                                 tmp->len = strlen(new_line);
01190                                 tmp->format = NULL;
01191                         }
01192                         tmp = tmp->next;
01193                 }
01194         }
01195 
01196         text_update_edited(text);
01197         text_update_cursor_moved(C);
01198         text_drawcache_tag_update(st, 1);
01199         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
01200 
01201         return OPERATOR_FINISHED;
01202 }
01203 
01204 void TEXT_OT_convert_whitespace(wmOperatorType *ot)
01205 {
01206         /* identifiers */
01207         ot->name= "Convert Whitespace";
01208         ot->idname= "TEXT_OT_convert_whitespace";
01209         ot->description= "Convert whitespaces by type";
01210         
01211         /* api callbacks */
01212         ot->exec= convert_whitespace_exec;
01213         ot->poll= text_edit_poll;
01214 
01215         /* properties */
01216         RNA_def_enum(ot->srna, "type", whitespace_type_items, TO_SPACES, "type", "Type of whitespace to convert to.");
01217 }
01218 
01219 /******************* select all operator *********************/
01220 
01221 static int select_all_exec(bContext *C, wmOperator *UNUSED(op))
01222 {
01223         Text *text= CTX_data_edit_text(C);
01224 
01225         txt_sel_all(text);
01226 
01227         text_update_cursor_moved(C);
01228         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
01229 
01230         return OPERATOR_FINISHED;
01231 }
01232 
01233 void TEXT_OT_select_all(wmOperatorType *ot)
01234 {
01235         /* identifiers */
01236         ot->name= "Select All";
01237         ot->idname= "TEXT_OT_select_all";
01238         ot->description= "Select all text";
01239         
01240         /* api callbacks */
01241         ot->exec= select_all_exec;
01242         ot->poll= text_edit_poll;
01243 }
01244 
01245 /******************* select line operator *********************/
01246 
01247 static int select_line_exec(bContext *C, wmOperator *UNUSED(op))
01248 {
01249         Text *text= CTX_data_edit_text(C);
01250 
01251         txt_sel_line(text);
01252 
01253         text_update_cursor_moved(C);
01254         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
01255 
01256         return OPERATOR_FINISHED;
01257 }
01258 
01259 void TEXT_OT_select_line(wmOperatorType *ot)
01260 {
01261         /* identifiers */
01262         ot->name= "Select Line";
01263         ot->idname= "TEXT_OT_select_line";
01264         ot->description= "Select text by line";
01265         
01266         /* api callbacks */
01267         ot->exec= select_line_exec;
01268         ot->poll= text_edit_poll;
01269 }
01270 
01271 /******************* select word operator *********************/
01272 
01273 static int select_word_exec(bContext *C, wmOperator *UNUSED(op))
01274 {
01275         Text *text= CTX_data_edit_text(C);
01276 
01277         txt_jump_left(text, 0);
01278         txt_jump_right(text, 1);
01279 
01280         text_update_cursor_moved(C);
01281         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
01282 
01283         return OPERATOR_FINISHED;
01284 }
01285 
01286 void TEXT_OT_select_word(wmOperatorType *ot)
01287 {
01288         /* identifiers */
01289         ot->name= "Select Word";
01290         ot->idname= "TEXT_OT_select_word";
01291         ot->description= "Select word under cursor";
01292 
01293         /* api callbacks */
01294         ot->exec= select_word_exec;
01295         ot->poll= text_edit_poll;
01296 }
01297 
01298 /******************* previous marker operator *********************/
01299 
01300 static int previous_marker_exec(bContext *C, wmOperator *UNUSED(op))
01301 {
01302         Text *text= CTX_data_edit_text(C);
01303         TextMarker *mrk;
01304         int lineno;
01305 
01306         lineno= txt_get_span(text->lines.first, text->curl);
01307         mrk= text->markers.last;
01308         while(mrk && (mrk->lineno>lineno || (mrk->lineno==lineno && mrk->end > text->curc)))
01309                 mrk= mrk->prev;
01310         if(!mrk) mrk= text->markers.last;
01311         if(mrk) {
01312                 txt_move_to(text, mrk->lineno, mrk->start, 0);
01313                 txt_move_to(text, mrk->lineno, mrk->end, 1);
01314         }
01315 
01316         text_update_cursor_moved(C);
01317         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
01318 
01319         return OPERATOR_FINISHED;
01320 }
01321 
01322 void TEXT_OT_previous_marker(wmOperatorType *ot)
01323 {
01324         /* identifiers */
01325         ot->name= "Previous Marker";
01326         ot->idname= "TEXT_OT_previous_marker";
01327         ot->description= "Move to previous marker";
01328         
01329         /* api callbacks */
01330         ot->exec= previous_marker_exec;
01331         ot->poll= text_edit_poll;
01332 }
01333 
01334 /******************* next marker operator *********************/
01335 
01336 static int next_marker_exec(bContext *C, wmOperator *UNUSED(op))
01337 {
01338         Text *text= CTX_data_edit_text(C);
01339         TextMarker *mrk;
01340         int lineno;
01341 
01342         lineno= txt_get_span(text->lines.first, text->curl);
01343         mrk= text->markers.first;
01344         while(mrk && (mrk->lineno<lineno || (mrk->lineno==lineno && mrk->start <= text->curc)))
01345                 mrk= mrk->next;
01346         if(!mrk) mrk= text->markers.first;
01347         if(mrk) {
01348                 txt_move_to(text, mrk->lineno, mrk->start, 0);
01349                 txt_move_to(text, mrk->lineno, mrk->end, 1);
01350         }
01351 
01352         text_update_cursor_moved(C);
01353         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
01354 
01355         return OPERATOR_FINISHED;
01356 }
01357 
01358 void TEXT_OT_next_marker(wmOperatorType *ot)
01359 {
01360         /* identifiers */
01361         ot->name= "Next Marker";
01362         ot->idname= "TEXT_OT_next_marker";
01363         ot->description= "Move to next marker";
01364         
01365         /* api callbacks */
01366         ot->exec= next_marker_exec;
01367         ot->poll= text_edit_poll;
01368 }
01369 
01370 /******************* clear all markers operator *********************/
01371 
01372 static int clear_all_markers_exec(bContext *C, wmOperator *UNUSED(op))
01373 {
01374         Text *text= CTX_data_edit_text(C);
01375 
01376         txt_clear_markers(text, 0, 0);
01377 
01378         text_update_cursor_moved(C);
01379         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
01380 
01381         return OPERATOR_FINISHED;
01382 }
01383 
01384 void TEXT_OT_markers_clear(wmOperatorType *ot)
01385 {
01386         /* identifiers */
01387         ot->name= "Clear All Markers";
01388         ot->idname= "TEXT_OT_markers_clear";
01389         ot->description= "Clear all markers";
01390         
01391         /* api callbacks */
01392         ot->exec= clear_all_markers_exec;
01393         ot->poll= text_edit_poll;
01394 }
01395 
01396 /************************ move operator ************************/
01397 
01398 static EnumPropertyItem move_type_items[]= {
01399         {LINE_BEGIN, "LINE_BEGIN", 0, "Line Begin", ""},
01400         {LINE_END, "LINE_END", 0, "Line End", ""},
01401         {FILE_TOP, "FILE_TOP", 0, "File Top", ""},
01402         {FILE_BOTTOM, "FILE_BOTTOM", 0, "File Bottom", ""},
01403         {PREV_CHAR, "PREVIOUS_CHARACTER", 0, "Previous Character", ""},
01404         {NEXT_CHAR, "NEXT_CHARACTER", 0, "Next Character", ""},
01405         {PREV_WORD, "PREVIOUS_WORD", 0, "Previous Word", ""},
01406         {NEXT_WORD, "NEXT_WORD", 0, "Next Word", ""},
01407         {PREV_LINE, "PREVIOUS_LINE", 0, "Previous Line", ""},
01408         {NEXT_LINE, "NEXT_LINE", 0, "Next Line", ""},
01409         {PREV_PAGE, "PREVIOUS_PAGE", 0, "Previous Page", ""},
01410         {NEXT_PAGE, "NEXT_PAGE", 0, "Next Page", ""},
01411         {0, NULL, 0, NULL, NULL}};
01412 
01413 /* get cursor position in line by relative wrapped line and column positions */
01414 static int text_get_cursor_rel(SpaceText* st, ARegion *ar, TextLine *linein, int rell, int relc)
01415 {
01416         int i, j, start, end, max, chop, curs, loop, endj, found, selc;
01417         char ch;
01418 
01419         max= wrap_width(st, ar);
01420 
01421         selc= start= endj= curs= found= 0;
01422         end= max;
01423         chop= loop= 1;
01424 
01425         for(i=0, j=0; loop; j++) {
01426                 int chars;
01427                 /* Mimic replacement of tabs */
01428                 ch= linein->line[j];
01429                 if(ch=='\t') {
01430                         chars= st->tabnumber-i%st->tabnumber;
01431                         ch= ' ';
01432                 }
01433                 else chars= 1;
01434 
01435                 while(chars--) {
01436                         if(rell==0 && i-start==relc) {
01437                                 /* current position could be wrapped to next line */
01438                                 /* this should be checked when end of current line would be reached */
01439                                 selc= j;
01440                                 found= 1;
01441                         }
01442                         else if(i-end==relc) {
01443                                 curs= j;
01444                         }
01445                         if(i-start>=max) {
01446                                 if(found) {
01447                                         /* exact cursor position was found, check if it's */
01448                                         /* still on needed line (hasn't been wrapped) */
01449                                         if(selc>endj && !chop) selc= endj;
01450                                         loop= 0;
01451                                         break;
01452                                 }
01453 
01454                                 if(chop) endj= j;
01455 
01456                                 start= end;
01457                                 end += max;
01458                                 chop= 1;
01459                                 rell--;
01460 
01461                                 if(rell==0 && i-start>=relc) {
01462                                         selc= curs;
01463                                         loop= 0;
01464                                         break;
01465                                 }
01466                         }
01467                         else if (ch=='\0') {
01468                                 if(!found) selc= linein->len;
01469                                 loop= 0;
01470                                 break;
01471                         }
01472                         else if(ch==' ' || ch=='-') {
01473                                 if(found) {
01474                                         loop= 0;
01475                                         break;
01476                                 }
01477 
01478                                 if(rell==0 && i-start>=relc) {
01479                                         selc= curs;
01480                                         loop= 0;
01481                                         break;
01482                                 }
01483                                 end= i+1;
01484                                 endj= j;
01485                                 chop= 0;
01486                         }
01487                         i++;
01488                 }
01489         }
01490 
01491         return selc;
01492 }
01493 
01494 static int cursor_skip_find_line(SpaceText* st, ARegion *ar,
01495         int lines, TextLine **linep, int *charp, int *rell, int *relc)
01496 {
01497         int offl, offc, visible_lines;
01498 
01499         wrap_offset_in_line(st, ar, *linep, *charp, &offl, &offc);
01500         *relc= text_get_char_pos(st, (*linep)->line, *charp) + offc;
01501         *rell= lines;
01502 
01503         /* handle current line */
01504         if(lines>0) {
01505                 visible_lines= text_get_visible_lines(st, ar, (*linep)->line);
01506 
01507                 if(*rell-visible_lines+offl>=0) {
01508                         if(!(*linep)->next) {
01509                                 if(offl < visible_lines-1) {
01510                                         *rell= visible_lines-1;
01511                                         return 1;
01512                                 }
01513 
01514                                 *charp= (*linep)->len;
01515                                 return 0;
01516                         }
01517 
01518                         *rell-= visible_lines-offl;
01519                         *linep=(*linep)->next;
01520                 } else {
01521                         *rell+= offl;
01522                         return 1;
01523                 }
01524         } else {
01525                 if(*rell+offl<=0) {
01526                         if(!(*linep)->prev) {
01527                                 if(offl) {
01528                                         *rell= 0;
01529                                         return 1;
01530                                 }
01531 
01532                                 *charp= 0;
01533                                 return 0;
01534                         }
01535 
01536                         *rell+= offl;
01537                         *linep=(*linep)->prev;
01538                 } else {
01539                         *rell+= offl;
01540                         return 1;
01541                 }
01542         }
01543 
01544         /* skip lines and find destination line and offsets */
01545         while(*linep) {
01546                 visible_lines= text_get_visible_lines(st, ar, (*linep)->line);
01547 
01548                 if(lines<0) { /* moving top */
01549                         if(*rell+visible_lines >= 0) {
01550                                 *rell+= visible_lines;
01551                                 break;
01552                         }
01553 
01554                         if(!(*linep)->prev) {
01555                                 *rell= 0;
01556                                 break;
01557                         }
01558 
01559                         *rell+= visible_lines;
01560                         *linep=(*linep)->prev;
01561                 } else { /* moving bottom */
01562                         if(*rell-visible_lines < 0) break;
01563 
01564                         if(!(*linep)->next) {
01565                                 *rell= visible_lines-1;
01566                                 break;
01567                         }
01568 
01569                         *rell-= visible_lines;
01570                         *linep=(*linep)->next;
01571                 }
01572         }
01573 
01574         return 1;
01575 }
01576 
01577 static void wrap_move_bol(SpaceText *st, ARegion *ar, short sel)
01578 {
01579         Text *text= st->text;
01580         TextLine **linep;
01581         int *charp;
01582         int oldl, oldc, i, j, max, start, end, endj, chop, loop;
01583         char ch;
01584 
01585         text_update_character_width(st);
01586 
01587         if (sel) linep= &text->sell, charp= &text->selc;
01588         else linep= &text->curl, charp= &text->curc;
01589 
01590         oldc= *charp;
01591         oldl= txt_get_span(text->lines.first, *linep);
01592 
01593         max= wrap_width(st, ar);
01594 
01595         start= endj= 0;
01596         end= max;
01597         chop= loop= 1;
01598         *charp= 0;
01599 
01600         for(i=0, j=0; loop; j++) {
01601                 int chars;
01602                 /* Mimic replacement of tabs */
01603                 ch= (*linep)->line[j];
01604                 if(ch=='\t') {
01605                         chars= st->tabnumber-i%st->tabnumber;
01606                         ch= ' ';
01607                 }
01608                 else chars= 1;
01609 
01610                 while(chars--) {
01611                         if(i-start>=max) {
01612                                 *charp= endj;
01613 
01614                                 if(j>=oldc) {
01615                                         if(ch=='\0') *charp= start;
01616                                         loop= 0;
01617                                         break;
01618                                 }
01619 
01620                                 if(chop) endj= j;
01621 
01622                                 start= end;
01623                                 end += max;
01624                                 chop= 1;
01625                         }
01626                         else if(ch==' ' || ch=='-' || ch=='\0') {
01627                                 if(j>=oldc) {
01628                                         *charp= start;
01629                                         loop= 0;
01630                                         break;
01631                                 }
01632 
01633                                 end= i+1;
01634                                 endj= j+1;
01635                                 chop= 0;
01636                         }
01637                         i++;
01638                 }
01639         }
01640 
01641         if (!sel) txt_pop_sel(text);
01642         txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, oldl, oldc, oldl, *charp);
01643 }
01644 
01645 static void wrap_move_eol(SpaceText *st, ARegion *ar, short sel)
01646 {
01647         Text *text= st->text;
01648         TextLine **linep;
01649         int *charp;
01650         int oldl, oldc, i, j, max, start, end, endj, chop, loop;
01651         char ch;
01652 
01653         text_update_character_width(st);
01654 
01655         if (sel) linep= &text->sell, charp= &text->selc;
01656         else linep= &text->curl, charp= &text->curc;
01657 
01658         oldc= *charp;
01659         oldl= txt_get_span(text->lines.first, *linep);
01660 
01661         max= wrap_width(st, ar);
01662 
01663         start= endj= 0;
01664         end= max;
01665         chop= loop= 1;
01666         *charp= 0;
01667 
01668         for(i=0, j=0; loop; j++) {
01669                 int chars;
01670                 /* Mimic replacement of tabs */
01671                 ch= (*linep)->line[j];
01672                 if(ch=='\t') {
01673                         chars= st->tabnumber-i%st->tabnumber;
01674                         ch= ' ';
01675                 }
01676                 else chars= 1;
01677 
01678                 while(chars--) {
01679                         if(i-start>=max) {
01680                                 if(chop) endj= j-1;
01681 
01682                                 if(endj>=oldc) {
01683                                         if(ch=='\0') *charp= (*linep)->len;
01684                                         else *charp= endj;
01685                                         loop= 0;
01686                                         break;
01687                                 }
01688 
01689                                 start= end;
01690                                 end += max;
01691                                 chop= 1;
01692                         } else if(ch=='\0') {
01693                                 *charp= (*linep)->len;
01694                                 loop= 0;
01695                                 break;
01696                         } else if(ch==' ' || ch=='-') {
01697                                 end= i+1;
01698                                 endj= j;
01699                                 chop= 0;
01700                         }
01701                         i++;
01702                 }
01703         }
01704 
01705         if (!sel) txt_pop_sel(text);
01706         txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, oldl, oldc, oldl, *charp);
01707 }
01708 
01709 static void wrap_move_up(SpaceText *st, ARegion *ar, short sel)
01710 {
01711         Text *text= st->text;
01712         TextLine **linep;
01713         int *charp;
01714         int oldl, oldc, offl, offc, col, newl;
01715 
01716         text_update_character_width(st);
01717 
01718         if (sel) linep= &text->sell, charp= &text->selc;
01719         else linep= &text->curl, charp= &text->curc;
01720 
01721         /* store previous position */
01722         oldc= *charp;
01723         newl= oldl= txt_get_span(text->lines.first, *linep);
01724 
01725         wrap_offset_in_line(st, ar, *linep, *charp, &offl, &offc);
01726         col= text_get_char_pos(st, (*linep)->line, *charp) + offc;
01727         if(offl) {
01728                 *charp= text_get_cursor_rel(st, ar, *linep, offl-1, col);
01729                 newl= BLI_findindex(&text->lines, linep);
01730         } else {
01731                 if((*linep)->prev) {
01732                         int visible_lines;
01733 
01734                         *linep= (*linep)->prev;
01735                         visible_lines= text_get_visible_lines(st, ar, (*linep)->line);
01736                         *charp= text_get_cursor_rel(st, ar, *linep, visible_lines-1, col);
01737                         newl--;
01738                 } else *charp= 0;
01739         }
01740 
01741         if (!sel) txt_pop_sel(text);
01742         txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, oldl, oldc, newl, *charp);
01743 }
01744 
01745 static void wrap_move_down(SpaceText *st, ARegion *ar, short sel)
01746 {
01747         Text *text= st->text;
01748         TextLine **linep;
01749         int *charp;
01750         int oldl, oldc, offl, offc, col, newl, visible_lines;
01751 
01752         text_update_character_width(st);
01753 
01754         if (sel) linep= &text->sell, charp= &text->selc;
01755         else linep= &text->curl, charp= &text->curc;
01756 
01757         /* store previous position */
01758         oldc= *charp;
01759         newl= oldl= txt_get_span(text->lines.first, *linep);
01760 
01761         wrap_offset_in_line(st, ar, *linep, *charp, &offl, &offc);
01762         col= text_get_char_pos(st, (*linep)->line, *charp) + offc;
01763         visible_lines= text_get_visible_lines(st, ar, (*linep)->line);
01764         if(offl<visible_lines-1) {
01765                 *charp= text_get_cursor_rel(st, ar, *linep, offl+1, col);
01766                 newl= BLI_findindex(&text->lines, linep);
01767         } else {
01768                 if((*linep)->next) {
01769                         *linep= (*linep)->next;
01770                         *charp= text_get_cursor_rel(st, ar, *linep, 0, col);
01771                         newl++;
01772                 } else *charp= (*linep)->len;
01773         }
01774 
01775         if (!sel) txt_pop_sel(text);
01776         txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, oldl, oldc, newl, *charp);
01777 }
01778 
01779 /* Moves the cursor vertically by the specified number of lines.
01780  If the destination line is shorter than the current cursor position, the
01781  cursor will be positioned at the end of this line.
01782 
01783  This is to replace screen_skip for PageUp/Down operations.
01784  */
01785 static void cursor_skip(SpaceText* st, ARegion *ar, Text *text, int lines, int sel)
01786 {
01787         TextLine **linep;
01788         int oldl, oldc, *charp;
01789         
01790         if (sel) linep= &text->sell, charp= &text->selc;
01791         else linep= &text->curl, charp= &text->curc;
01792         oldl= txt_get_span(text->lines.first, *linep);
01793         oldc= *charp;
01794 
01795         if(st && ar && st->wordwrap) {
01796                 int rell, relc;
01797 
01798                 /* find line and offsets inside it needed to set cursor position */
01799                 if(cursor_skip_find_line(st, ar, lines, linep, charp, &rell, &relc))
01800                   *charp= text_get_cursor_rel (st, ar, *linep, rell, relc);
01801         } else {
01802                 while (lines>0 && (*linep)->next) {
01803                         *linep= (*linep)->next;
01804                         lines--;
01805                 }
01806                 while (lines<0 && (*linep)->prev) {
01807                         *linep= (*linep)->prev;
01808                         lines++;
01809                 }
01810         }
01811 
01812         if (*charp > (*linep)->len) *charp= (*linep)->len;
01813 
01814         if (!sel) txt_pop_sel(text);
01815         txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, oldl, oldc, txt_get_span(text->lines.first, *linep), *charp);
01816 }
01817 
01818 static int move_cursor(bContext *C, int type, int select)
01819 {
01820         SpaceText *st= CTX_wm_space_text(C);
01821         Text *text= CTX_data_edit_text(C);
01822         ARegion *ar= CTX_wm_region(C);
01823 
01824         /* ensure we have the right region, it's optional */
01825         if(ar && ar->regiontype != RGN_TYPE_WINDOW)
01826                 ar= NULL;
01827 
01828         switch(type) {
01829                 case LINE_BEGIN:
01830                         if(st && st->wordwrap && ar) wrap_move_bol(st, ar, select);
01831                         else txt_move_bol(text, select);
01832                         break;
01833                         
01834                 case LINE_END:
01835                         if(st && st->wordwrap && ar) wrap_move_eol(st, ar, select);
01836                         else txt_move_eol(text, select);
01837                         break;
01838 
01839                 case FILE_TOP:
01840                         txt_move_bof(text, select);
01841                         break;
01842                         
01843                 case FILE_BOTTOM:
01844                         txt_move_eof(text, select);
01845                         break;
01846 
01847                 case PREV_WORD:
01848                         txt_jump_left(text, select);
01849                         break;
01850 
01851                 case NEXT_WORD:
01852                         txt_jump_right(text, select);
01853                         break;
01854 
01855                 case PREV_CHAR:
01856                         txt_move_left(text, select);
01857                         break;
01858 
01859                 case NEXT_CHAR: 
01860                         txt_move_right(text, select);
01861                         break;
01862 
01863                 case PREV_LINE:
01864                         if(st && st->wordwrap && ar) wrap_move_up(st, ar, select);
01865                         else txt_move_up(text, select);
01866                         break;
01867                         
01868                 case NEXT_LINE:
01869                         if(st && st->wordwrap && ar) wrap_move_down(st, ar, select);
01870                         else txt_move_down(text, select);
01871                         break;
01872 
01873                 case PREV_PAGE:
01874                         if(st) cursor_skip(st, ar, st->text, -st->viewlines, select);
01875                         else cursor_skip(NULL, NULL, text, -10, select);
01876                         break;
01877 
01878                 case NEXT_PAGE:
01879                         if(st) cursor_skip(st, ar, st->text, st->viewlines, select);
01880                         else cursor_skip(NULL, NULL, text, 10, select);
01881                         break;
01882         }
01883 
01884         text_update_cursor_moved(C);
01885         WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
01886 
01887         return OPERATOR_FINISHED;
01888 }
01889 
01890 static int move_exec(bContext *C, wmOperator *op)
01891 {
01892         int type= RNA_enum_get(op->ptr, "type");
01893 
01894         return move_cursor(C, type, 0);
01895 }
01896 
01897 void TEXT_OT_move(wmOperatorType *ot)
01898 {
01899         /* identifiers */
01900         ot->name= "Move Cursor";
01901         ot->idname= "TEXT_OT_move";
01902         ot->description= "Move cursor to position type";
01903         
01904         /* api callbacks */
01905         ot->exec= move_exec;
01906         ot->poll= text_edit_poll;
01907 
01908         /* properties */
01909         RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to.");
01910 }
01911 
01912 /******************* move select operator ********************/
01913 
01914 static int move_select_exec(bContext *C, wmOperator *op)
01915 {
01916         int type= RNA_enum_get(op->ptr, "type");
01917 
01918         return move_cursor(C, type, 1);
01919 }
01920 
01921 void TEXT_OT_move_select(wmOperatorType *ot)
01922 {
01923         /* identifiers */
01924         ot->name= "Move Select";
01925         ot->idname= "TEXT_OT_move_select";
01926         ot->description= "Make selection from current cursor position to new cursor position type";
01927         
01928         /* api callbacks */
01929         ot->exec= move_select_exec;
01930         ot->poll= text_space_edit_poll;
01931 
01932         /* properties */
01933         RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to, to make a selection.");
01934 }
01935 
01936 /******************* jump operator *********************/
01937 
01938 static int jump_exec(bContext *C, wmOperator *op)
01939 {
01940         Text *text= CTX_data_edit_text(C);
01941         int line= RNA_int_get(op->ptr, "line");
01942         short nlines= txt_get_span(text->lines.first, text->lines.last)+1;
01943 
01944         if(line < 1)
01945                 txt_move_toline(text, 1, 0);
01946         else if(line > nlines)
01947                 txt_move_toline(text, nlines-1, 0);
01948         else
01949                 txt_move_toline(text, line-1, 0);
01950 
01951         text_update_cursor_moved(C);
01952         WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
01953 
01954         return OPERATOR_FINISHED;
01955 }
01956 
01957 static int jump_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
01958 {
01959         return WM_operator_props_dialog_popup(C,op,200,100);
01960 
01961 }
01962 
01963 void TEXT_OT_jump(wmOperatorType *ot)
01964 {
01965         /* identifiers */
01966         ot->name= "Jump";
01967         ot->idname= "TEXT_OT_jump";
01968         ot->description= "Jump cursor to line";
01969         
01970         /* api callbacks */
01971         ot->invoke= jump_invoke;
01972         ot->exec= jump_exec;
01973         ot->poll= text_edit_poll;
01974 
01975         /* properties */
01976         RNA_def_int(ot->srna, "line", 1, 1, INT_MAX, "Line", "Line number to jump to.", 1, 10000);
01977 }
01978 
01979 /******************* delete operator **********************/
01980 
01981 static EnumPropertyItem delete_type_items[]= {
01982         {DEL_NEXT_CHAR, "NEXT_CHARACTER", 0, "Next Character", ""},
01983         {DEL_PREV_CHAR, "PREVIOUS_CHARACTER", 0, "Previous Character", ""},
01984         {DEL_NEXT_WORD, "NEXT_WORD", 0, "Next Word", ""},
01985         {DEL_PREV_WORD, "PREVIOUS_WORD", 0, "Previous Word", ""},
01986         {0, NULL, 0, NULL, NULL}};
01987 
01988 static int delete_exec(bContext *C, wmOperator *op)
01989 {
01990         Text *text= CTX_data_edit_text(C);
01991         int type= RNA_enum_get(op->ptr, "type");
01992 
01993         text_drawcache_tag_update(CTX_wm_space_text(C), 0);
01994 
01995         if(type == DEL_PREV_WORD)
01996                 txt_backspace_word(text);
01997         else if(type == DEL_PREV_CHAR)
01998                 txt_backspace_char(text);
01999         else if(type == DEL_NEXT_WORD)
02000                 txt_delete_word(text);
02001         else if(type == DEL_NEXT_CHAR)
02002                 txt_delete_char(text);
02003 
02004         text_update_line_edited(text->curl);
02005 
02006         text_update_cursor_moved(C);
02007         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
02008 
02009         /* run the script while editing, evil but useful */
02010         if(CTX_wm_space_text(C)->live_edit)
02011                 run_script(C, NULL);
02012         
02013         return OPERATOR_FINISHED;
02014 }
02015 
02016 void TEXT_OT_delete(wmOperatorType *ot)
02017 {
02018         /* identifiers */
02019         ot->name= "Delete";
02020         ot->idname= "TEXT_OT_delete";
02021         ot->description= "Delete text by cursor position";
02022         
02023         /* api callbacks */
02024         ot->exec= delete_exec;
02025         ot->poll= text_edit_poll;
02026 
02027         /* properties */
02028         RNA_def_enum(ot->srna, "type", delete_type_items, DEL_NEXT_CHAR, "Type", "Which part of the text to delete.");
02029 }
02030 
02031 /******************* toggle overwrite operator **********************/
02032 
02033 static int toggle_overwrite_exec(bContext *C, wmOperator *UNUSED(op))
02034 {
02035         SpaceText *st= CTX_wm_space_text(C);
02036 
02037         st->overwrite= !st->overwrite;
02038 
02039         WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
02040 
02041         return OPERATOR_FINISHED;
02042 }
02043 
02044 void TEXT_OT_overwrite_toggle(wmOperatorType *ot)
02045 {
02046         /* identifiers */
02047         ot->name= "Toggle Overwrite";
02048         ot->idname= "TEXT_OT_overwrite_toggle";
02049         ot->description= "Toggle overwrite while typing";
02050         
02051         /* api callbacks */
02052         ot->exec= toggle_overwrite_exec;
02053         ot->poll= text_space_edit_poll;
02054 }
02055 
02056 /******************* scroll operator **********************/
02057 
02058 /* Moves the view vertically by the specified number of lines */
02059 static void screen_skip(SpaceText *st, ARegion *ar, int lines)
02060 {
02061         int last;
02062 
02063         st->top += lines;
02064 
02065         last= text_get_total_lines(st, ar);
02066         last= last - (st->viewlines/2);
02067         
02068         if(st->top>last) st->top= last;
02069         if(st->top<0) st->top= 0;
02070 }
02071 
02072 /* quick enum for tsc->zone (scroller handles) */
02073 enum {
02074         SCROLLHANDLE_BAR,
02075         SCROLLHANDLE_MIN_OUTSIDE,
02076         SCROLLHANDLE_MAX_OUTSIDE
02077 };
02078 
02079 typedef struct TextScroll {
02080         short old[2];
02081         short delta[2];
02082 
02083         int first;
02084         int scrollbar;
02085 
02086         int zone;
02087 } TextScroll;
02088 
02089 static int text_scroll_poll(bContext *C)
02090 {
02091         /* it should be possible to still scroll linked texts to read them, even if they can't be edited... */
02092         return CTX_data_edit_text(C) != NULL;
02093 }
02094 
02095 static int scroll_exec(bContext *C, wmOperator *op)
02096 {
02097         SpaceText *st= CTX_wm_space_text(C);
02098         ARegion *ar= CTX_wm_region(C);
02099 
02100         int lines= RNA_int_get(op->ptr, "lines");
02101 
02102         if(lines == 0)
02103                 return OPERATOR_CANCELLED;
02104 
02105         screen_skip(st, ar, lines*U.wheellinescroll);
02106 
02107         ED_area_tag_redraw(CTX_wm_area(C));
02108 
02109         return OPERATOR_FINISHED;
02110 }
02111 
02112 static void scroll_apply(bContext *C, wmOperator *op, wmEvent *event)
02113 {
02114         SpaceText *st= CTX_wm_space_text(C);
02115         ARegion *ar= CTX_wm_region(C);
02116         TextScroll *tsc= op->customdata;
02117         int mval[2]= {event->x, event->y};
02118         short txtdelta[2] = {0, 0};
02119 
02120         text_update_character_width(st);
02121 
02122         if(tsc->first) {
02123                 tsc->old[0]= mval[0];
02124                 tsc->old[1]= mval[1];
02125                 tsc->first= 0;
02126         }
02127 
02128         tsc->delta[0]+= mval[0] - tsc->old[0];
02129         tsc->delta[1]+= mval[1] - tsc->old[1];
02130 
02131         if(!tsc->scrollbar) {
02132                 txtdelta[0]= -tsc->delta[0]/st->cwidth;
02133                 txtdelta[1]= tsc->delta[1]/st->lheight;
02134 
02135                 tsc->delta[0]%= st->cwidth;
02136                 tsc->delta[1]%= st->lheight;
02137         }
02138         else {
02139                 txtdelta[1]= -tsc->delta[1]*st->pix_per_line;
02140                 tsc->delta[1]+= txtdelta[1]/st->pix_per_line;
02141         }
02142 
02143         if(txtdelta[0] || txtdelta[1]) {
02144                 screen_skip(st, ar, txtdelta[1]);
02145 
02146                 if(st->wordwrap) {
02147                         st->left= 0;
02148                 }
02149                 else {
02150                         st->left+= txtdelta[0];
02151                         if(st->left<0) st->left= 0;
02152                 }
02153 
02154                 ED_area_tag_redraw(CTX_wm_area(C));
02155         }
02156 
02157         tsc->old[0]= mval[0];
02158         tsc->old[1]= mval[1];
02159 }
02160 
02161 static void scroll_exit(bContext *C, wmOperator *op)
02162 {
02163         SpaceText *st= CTX_wm_space_text(C);
02164 
02165         st->flags &= ~ST_SCROLL_SELECT;
02166         MEM_freeN(op->customdata);
02167 }
02168 
02169 static int scroll_modal(bContext *C, wmOperator *op, wmEvent *event)
02170 {
02171         TextScroll *tsc= op->customdata;
02172         SpaceText *st= CTX_wm_space_text(C);
02173         ARegion *ar= CTX_wm_region(C);
02174 
02175         switch(event->type) {
02176                 case MOUSEMOVE:
02177                         if(tsc->zone == SCROLLHANDLE_BAR)
02178                                 scroll_apply(C, op, event);
02179                         break;
02180                 case LEFTMOUSE:
02181                 case RIGHTMOUSE:
02182                 case MIDDLEMOUSE:
02183                         if(ELEM(tsc->zone, SCROLLHANDLE_MIN_OUTSIDE, SCROLLHANDLE_MAX_OUTSIDE)) {
02184                                 int last;
02185 
02186                                 st->top+= st->viewlines * (tsc->zone==SCROLLHANDLE_MIN_OUTSIDE ? 1 : -1);
02187 
02188                                 last= text_get_total_lines(st, ar);
02189                                 last= last - (st->viewlines/2);
02190 
02191                                 CLAMP(st->top, 0, last);
02192 
02193                                 ED_area_tag_redraw(CTX_wm_area(C));
02194                         }
02195                         scroll_exit(C, op);
02196                         return OPERATOR_FINISHED;
02197         }
02198 
02199         return OPERATOR_RUNNING_MODAL;
02200 }
02201 
02202 static int scroll_cancel(bContext *C, wmOperator *op)
02203 {
02204         scroll_exit(C, op);
02205 
02206         return OPERATOR_CANCELLED;
02207 }
02208 
02209 static int scroll_invoke(bContext *C, wmOperator *op, wmEvent *event)
02210 {
02211         SpaceText *st= CTX_wm_space_text(C);
02212         TextScroll *tsc;
02213         
02214         if(RNA_property_is_set(op->ptr, "lines"))
02215                 return scroll_exec(C, op);
02216         
02217         tsc= MEM_callocN(sizeof(TextScroll), "TextScroll");
02218         tsc->first= 1;
02219         tsc->zone= SCROLLHANDLE_BAR;
02220         op->customdata= tsc;
02221         
02222         st->flags|= ST_SCROLL_SELECT;
02223         
02224         if (event->type == MOUSEPAN) {
02225                 text_update_character_width(st);
02226                 
02227                 tsc->old[0] = event->x;
02228                 tsc->old[1] = event->x;
02229                 /* Sensitivity of scroll set to 4pix per line/char */
02230                 tsc->delta[0] = (event->x - event->prevx)*st->cwidth/4;
02231                 tsc->delta[1] = (event->y - event->prevy)*st->lheight/4;
02232                 tsc->first = 0;
02233                 tsc->scrollbar = 0;
02234                 scroll_apply(C, op, event);
02235                 scroll_exit(C, op);
02236                 return OPERATOR_FINISHED;
02237         }       
02238         
02239         WM_event_add_modal_handler(C, op);
02240         
02241         return OPERATOR_RUNNING_MODAL;
02242 }
02243 
02244 void TEXT_OT_scroll(wmOperatorType *ot)
02245 {
02246         /* identifiers */
02247         ot->name= "Scroll";
02248         /*don't really see the difference between this and
02249           scroll_bar. Both do basically the same thing (aside 
02250           from keymaps).*/
02251         ot->idname= "TEXT_OT_scroll";
02252         ot->description= "Scroll text screen";
02253         
02254         /* api callbacks */
02255         ot->exec= scroll_exec;
02256         ot->invoke= scroll_invoke;
02257         ot->modal= scroll_modal;
02258         ot->cancel= scroll_cancel;
02259         ot->poll= text_scroll_poll;
02260 
02261         /* flags */
02262         ot->flag= OPTYPE_BLOCKING|OPTYPE_GRAB_POINTER;
02263 
02264         /* properties */
02265         RNA_def_int(ot->srna, "lines", 1, INT_MIN, INT_MAX, "Lines", "Number of lines to scroll.", -100, 100);
02266 }
02267 
02268 /******************** scroll bar operator *******************/
02269 
02270 static int text_region_scroll_poll(bContext *C)
02271 {
02272         /* same as text_region_edit_poll except it works on libdata too */
02273         SpaceText *st= CTX_wm_space_text(C);
02274         Text *text= CTX_data_edit_text(C);
02275         ARegion *ar= CTX_wm_region(C);
02276 
02277         if(!st || !text)
02278                 return 0;
02279         
02280         if(!ar || ar->regiontype != RGN_TYPE_WINDOW)
02281                 return 0;
02282         
02283         return 1;
02284 }
02285 
02286 static int scroll_bar_invoke(bContext *C, wmOperator *op, wmEvent *event)
02287 {
02288         SpaceText *st= CTX_wm_space_text(C);
02289         ARegion *ar= CTX_wm_region(C);
02290         TextScroll *tsc;
02291         const int *mval= event->mval;
02292         int zone= -1;
02293 
02294         if(RNA_property_is_set(op->ptr, "lines"))
02295                 return scroll_exec(C, op);
02296         
02297         /* verify we are in the right zone */
02298         if(mval[0]>st->txtbar.xmin && mval[0]<st->txtbar.xmax) {
02299                 if(mval[1]>=st->txtbar.ymin && mval[1]<=st->txtbar.ymax) {
02300                         /* mouse inside scroll handle */
02301                         zone = SCROLLHANDLE_BAR;
02302                 }
02303                 else if(mval[1]>TXT_SCROLL_SPACE && mval[1]<ar->winy-TXT_SCROLL_SPACE) {
02304                         if(mval[1]<st->txtbar.ymin) zone= SCROLLHANDLE_MIN_OUTSIDE;
02305                         else zone= SCROLLHANDLE_MAX_OUTSIDE;
02306                 }
02307         }
02308 
02309         if(zone == -1) {
02310                 /* we are outside slider - nothing to do */
02311                 return OPERATOR_PASS_THROUGH;
02312         }
02313 
02314         tsc= MEM_callocN(sizeof(TextScroll), "TextScroll");
02315         tsc->first= 1;
02316         tsc->scrollbar= 1;
02317         tsc->zone= zone;
02318         op->customdata= tsc;
02319         
02320         st->flags|= ST_SCROLL_SELECT;
02321 
02322         WM_event_add_modal_handler(C, op);
02323 
02324         return OPERATOR_RUNNING_MODAL;
02325 }
02326 
02327 void TEXT_OT_scroll_bar(wmOperatorType *ot)
02328 {
02329         /* identifiers */
02330         ot->name= "Scrollbar";
02331         /*don't really see the difference between this and
02332           scroll. Both do basically the same thing (aside 
02333           from keymaps).*/
02334         ot->idname= "TEXT_OT_scroll_bar";
02335         ot->description= "Scroll text screen";
02336         
02337         /* api callbacks */
02338         ot->invoke= scroll_bar_invoke;
02339         ot->modal= scroll_modal;
02340         ot->cancel= scroll_cancel;
02341         ot->poll= text_region_scroll_poll;
02342 
02343         /* flags */
02344         ot->flag= OPTYPE_BLOCKING;
02345 
02346         /* properties */
02347         RNA_def_int(ot->srna, "lines", 1, INT_MIN, INT_MAX, "Lines", "Number of lines to scroll.", -100, 100);
02348 }
02349 
02350 /******************* set selection operator **********************/
02351 
02352 typedef struct SetSelection {
02353         int selecting;
02354         int selc, sell;
02355         short old[2];
02356 } SetSelection;
02357 
02358 static void set_cursor_to_pos(SpaceText *st, ARegion *ar, int x, int y, int sel) 
02359 {
02360         FlattenString fs;
02361         Text *text= st->text;
02362         TextLine **linep;
02363         int *charp;
02364         int w;
02365 
02366         text_update_character_width(st);
02367 
02368         if(sel) { linep= &text->sell; charp= &text->selc; } 
02369         else { linep= &text->curl; charp= &text->curc; }
02370         
02371         y= (ar->winy - 2 - y)/st->lheight;
02372 
02373         if(st->showlinenrs)
02374                 x-= TXT_OFFSET+TEXTXLOC;
02375         else
02376                 x-= TXT_OFFSET;
02377 
02378         if(x<0) x= 0;
02379         x = (x/st->cwidth) + st->left;
02380         
02381         if(st->wordwrap) {
02382                 int i, j, endj, curs, max, chop, start, end, loop, found;
02383                 char ch;
02384 
02385                 /* Point to first visible line */
02386                 *linep= text->lines.first;
02387                 i= st->top;
02388                 while(i>0 && *linep) {
02389                         int lines= text_get_visible_lines(st, ar, (*linep)->line);
02390 
02391                         if (i-lines<0) {
02392                                 y+= i;
02393                                 break;
02394                         } else {
02395                                 *linep= (*linep)->next;
02396                                 i-= lines;
02397                         }
02398                 }
02399 
02400                 max= wrap_width(st, ar);
02401 
02402                 loop= 1;
02403                 found= 0;
02404                 while(loop && *linep) {
02405                         start= 0;
02406                         end= max;
02407                         chop= 1;
02408                         curs= 0;
02409                         endj= 0;
02410                         for(i=0, j=0; loop; j++) {
02411                                 int chars;
02412 
02413                                 /* Mimic replacement of tabs */
02414                                 ch= (*linep)->line[j];
02415                                 if(ch=='\t') {
02416                                         chars= st->tabnumber-i%st->tabnumber;
02417                                         ch= ' ';
02418                                 }
02419                                 else
02420                                         chars= 1;
02421 
02422                                 while(chars--) {
02423                                         /* Gone too far, go back to last wrap point */
02424                                         if(y<0) {
02425                                                 *charp= endj;
02426                                                 loop= 0;
02427                                                 break;
02428                                         /* Exactly at the cursor */
02429                                         }
02430                                         else if(y==0 && i-start==x) {
02431                                                 /* current position could be wrapped to next line */
02432                                                 /* this should be checked when end of current line would be reached */
02433                                                 *charp= curs= j;
02434                                                 found= 1;
02435                                         /* Prepare curs for next wrap */
02436                                         }
02437                                         else if(i-end==x) {
02438                                                 curs= j;
02439                                         }
02440                                         if(i-start>=max) {
02441                                                 if(found) {
02442                                                         /* exact cursor position was found, check if it's */
02443                                                         /* still on needed line (hasn't been wrapped) */
02444                                                         if(*charp>endj && !chop && ch!='\0') (*charp)= endj;
02445                                                         loop= 0;
02446                                                         break;
02447                                                 }
02448 
02449                                                 if(chop) endj= j;
02450                                                 start= end;
02451                                                 end += max;
02452 
02453                                                 if(j<(*linep)->len)
02454                                                         y--;
02455 
02456                                                 chop= 1;
02457                                                 if(y==0 && i-start>=x) {
02458                                                         *charp= curs;
02459                                                         loop= 0;
02460                                                         break;
02461                                                 }
02462                                         }
02463                                         else if(ch==' ' || ch=='-' || ch=='\0') {
02464                                                 if(found) {
02465                                                         loop= 0;
02466                                                         break;
02467                                                 }
02468 
02469                                                 if(y==0 && i-start>=x) {
02470                                                         *charp= curs;
02471                                                         loop= 0;
02472                                                         break;
02473                                                 }
02474                                                 end = i+1;
02475                                                 endj = j;
02476                                                 chop= 0;
02477                                         }
02478                                         i++;
02479                                 }
02480                                 if(ch=='\0') break;
02481                         }
02482                         if(!loop || found) break;
02483 
02484                         if(!(*linep)->next) {
02485                                 *charp= (*linep)->len;
02486                                 break;
02487                         }
02488 
02489                         /* On correct line but didn't meet cursor, must be at end */
02490                         if(y==0) {
02491                                 *charp= (*linep)->len;
02492                                 break;
02493                         }
02494                         *linep= (*linep)->next;
02495                         y--;
02496                 }
02497 
02498         }
02499         else {
02500                 y-= txt_get_span(text->lines.first, *linep) - st->top;
02501                 
02502                 if(y>0) {
02503                         while(y-- != 0) if((*linep)->next) *linep= (*linep)->next;
02504                 }
02505                 else if(y<0) {
02506                         while(y++ != 0) if((*linep)->prev) *linep= (*linep)->prev;
02507                 }
02508 
02509                 
02510                 w= flatten_string(st, &fs, (*linep)->line);
02511                 if(x<w) *charp= fs.accum[x];
02512                 else *charp= (*linep)->len;
02513                 flatten_string_free(&fs);
02514         }
02515         if(!sel) txt_pop_sel(text);
02516 }
02517 
02518 static void set_cursor_apply(bContext *C, wmOperator *op, wmEvent *event)
02519 {
02520         SpaceText *st= CTX_wm_space_text(C);
02521         ARegion *ar= CTX_wm_region(C);
02522         SetSelection *ssel= op->customdata;
02523 
02524         if(event->mval[1]<0 || event->mval[1]>ar->winy) {
02525                 int d= (ssel->old[1]-event->mval[1])*st->pix_per_line;
02526                 if(d) screen_skip(st, ar, d);
02527 
02528                 set_cursor_to_pos(st, ar, event->mval[0], event->mval[1]<0?0:ar->winy, 1);
02529 
02530                 text_update_cursor_moved(C);
02531                 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
02532         } 
02533         else if(!st->wordwrap && (event->mval[0]<0 || event->mval[0]>ar->winx)) {
02534                 if(event->mval[0]>ar->winx) st->left++;
02535                 else if(event->mval[0]<0 && st->left>0) st->left--;
02536                 
02537                 set_cursor_to_pos(st, ar, event->mval[0], event->mval[1], 1);
02538                 
02539                 text_update_cursor_moved(C);
02540                 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
02541                 // XXX PIL_sleep_ms(10);
02542         } 
02543         else {
02544                 set_cursor_to_pos(st, ar, event->mval[0], event->mval[1], 1);
02545 
02546                 text_update_cursor_moved(C);
02547                 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
02548 
02549                 ssel->old[0]= event->mval[0];
02550                 ssel->old[1]= event->mval[1];
02551         } 
02552 }
02553 
02554 static void set_cursor_exit(bContext *C, wmOperator *op)
02555 {
02556         SpaceText *st= CTX_wm_space_text(C);
02557         Text *text= st->text;
02558         SetSelection *ssel= op->customdata;
02559         int linep2, charp2;
02560         char *buffer;
02561 
02562         if(txt_has_sel(text)) {
02563                 buffer = txt_sel_to_buf(text);
02564                 WM_clipboard_text_set(buffer, 1);
02565                 MEM_freeN(buffer);
02566         }
02567 
02568         linep2= txt_get_span(st->text->lines.first, st->text->sell);
02569         charp2= st->text->selc;
02570                 
02571         if(ssel->sell!=linep2 || ssel->selc!=charp2)
02572                 txt_undo_add_toop(st->text, UNDO_STO, ssel->sell, ssel->selc, linep2, charp2);
02573 
02574         text_update_cursor_moved(C);
02575         WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
02576 
02577         MEM_freeN(ssel);
02578 }
02579 
02580 static int set_selection_invoke(bContext *C, wmOperator *op, wmEvent *event)
02581 {
02582         SpaceText *st= CTX_wm_space_text(C);
02583         SetSelection *ssel;
02584 
02585         op->customdata= MEM_callocN(sizeof(SetSelection), "SetCursor");
02586         ssel= op->customdata;
02587         ssel->selecting= RNA_boolean_get(op->ptr, "select");
02588 
02589         ssel->old[0]= event->mval[0];
02590         ssel->old[1]= event->mval[1];
02591 
02592         ssel->sell= txt_get_span(st->text->lines.first, st->text->sell);
02593         ssel->selc= st->text->selc;
02594 
02595         WM_event_add_modal_handler(C, op);
02596 
02597         set_cursor_apply(C, op, event);
02598 
02599         return OPERATOR_RUNNING_MODAL;
02600 }
02601 
02602 static int set_selection_modal(bContext *C, wmOperator *op, wmEvent *event)
02603 {
02604         switch(event->type) {
02605                 case LEFTMOUSE:
02606                 case MIDDLEMOUSE:
02607                 case RIGHTMOUSE:
02608                         set_cursor_exit(C, op);
02609                         return OPERATOR_FINISHED;
02610                 case MOUSEMOVE:
02611                         set_cursor_apply(C, op, event);
02612                         break;
02613         }
02614 
02615         return OPERATOR_RUNNING_MODAL;
02616 }
02617 
02618 static int set_selection_cancel(bContext *C, wmOperator *op)
02619 {
02620         set_cursor_exit(C, op);
02621         return OPERATOR_FINISHED;
02622 }
02623 
02624 void TEXT_OT_selection_set(wmOperatorType *ot)
02625 {
02626         /* identifiers */
02627         ot->name= "Set Selection";
02628         ot->idname= "TEXT_OT_selection_set";
02629         ot->description= "Set cursor selection";
02630 
02631         /* api callbacks */
02632         ot->invoke= set_selection_invoke;
02633         ot->modal= set_selection_modal;
02634         ot->cancel= set_selection_cancel;
02635         ot->poll= text_region_edit_poll;
02636 
02637         /* properties */
02638         RNA_def_boolean(ot->srna, "select", 0, "Select", "Set selection end rather than cursor.");
02639 }
02640 
02641 /******************* set cursor operator **********************/
02642 
02643 static int set_cursor_exec(bContext *C, wmOperator *op)
02644 {
02645         SpaceText *st= CTX_wm_space_text(C);
02646         Text *text= st->text;
02647         ARegion *ar= CTX_wm_region(C);
02648         int x= RNA_int_get(op->ptr, "x");
02649         int y= RNA_int_get(op->ptr, "y");
02650         int oldl, oldc;
02651 
02652         oldl= txt_get_span(text->lines.first, text->curl);
02653         oldc= text->curc;
02654 
02655         set_cursor_to_pos(st, ar, x, y, 0);
02656 
02657         txt_undo_add_toop(text, UNDO_CTO, oldl, oldc, txt_get_span(text->lines.first, text->curl), text->curc);
02658 
02659         text_update_cursor_moved(C);
02660         WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
02661 
02662         return OPERATOR_PASS_THROUGH;
02663 }
02664 
02665 static int set_cursor_invoke(bContext *C, wmOperator *op, wmEvent *event)
02666 {
02667         SpaceText *st= CTX_wm_space_text(C);
02668 
02669         if(event->mval[0]>=st->txtbar.xmin)
02670                 return OPERATOR_PASS_THROUGH;
02671 
02672         RNA_int_set(op->ptr, "x", event->mval[0]);
02673         RNA_int_set(op->ptr, "y", event->mval[1]);
02674 
02675         return set_cursor_exec(C, op);
02676 }
02677 
02678 void TEXT_OT_cursor_set(wmOperatorType *ot)
02679 {
02680         /* identifiers */
02681         ot->name= "Set Cursor";
02682         ot->idname= "TEXT_OT_cursor_set";
02683         ot->description= "Set cursor position";
02684 
02685         /* api callbacks */
02686         ot->invoke= set_cursor_invoke;
02687         ot->exec= set_cursor_exec;
02688         ot->poll= text_region_edit_poll;
02689 
02690         /* properties */
02691         RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
02692         RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
02693 }
02694 
02695 /******************* line number operator **********************/
02696 
02697 static int line_number_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
02698 {
02699         SpaceText *st= CTX_wm_space_text(C);
02700         Text *text= CTX_data_edit_text(C);
02701         ARegion *ar= CTX_wm_region(C);
02702         const int *mval= event->mval;
02703         double time;
02704         static int jump_to= 0;
02705         static double last_jump= 0;
02706 
02707         text_update_character_width(st);
02708 
02709         if(!st->showlinenrs)
02710                 return OPERATOR_PASS_THROUGH;
02711 
02712         if(!(mval[0]>2 && mval[0]<(TXT_OFFSET + TEXTXLOC) && mval[1]>2 && mval[1]<ar->winy-2))
02713                 return OPERATOR_PASS_THROUGH;
02714 
02715         if(!(event->ascii>='0' && event->ascii<='9'))
02716                 return OPERATOR_PASS_THROUGH;
02717 
02718         time = PIL_check_seconds_timer();
02719         if(last_jump < time-1)
02720                 jump_to= 0;
02721 
02722         jump_to *= 10;
02723         jump_to += (int)(event->ascii-'0');
02724 
02725         txt_move_toline(text, jump_to-1, 0);
02726         last_jump= time;
02727 
02728         text_update_cursor_moved(C);
02729         WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
02730 
02731         return OPERATOR_FINISHED;
02732 }
02733 
02734 void TEXT_OT_line_number(wmOperatorType *ot)
02735 {
02736         /* identifiers */
02737         ot->name= "Line Number";
02738         ot->idname= "TEXT_OT_line_number";
02739         ot->description= "The current line number";
02740         
02741         /* api callbacks */
02742         ot->invoke= line_number_invoke;
02743         ot->poll= text_region_edit_poll;
02744 }
02745 
02746 /******************* insert operator **********************/
02747 
02748 static int insert_exec(bContext *C, wmOperator *op)
02749 {
02750         SpaceText *st= CTX_wm_space_text(C);
02751         Text *text= CTX_data_edit_text(C);
02752         char *str;
02753         int done = 0, i;
02754 
02755         text_drawcache_tag_update(st, 0);
02756 
02757         str= RNA_string_get_alloc(op->ptr, "text", NULL, 0);
02758 
02759         if(st && st->overwrite) {
02760                 for(i=0; str[i]; i++) {
02761                         done |= txt_replace_char(text, str[i]);
02762                 }
02763         } else {
02764                 for(i=0; str[i]; i++) {
02765                         done |= txt_add_char(text, str[i]);
02766                 }
02767         }
02768 
02769         MEM_freeN(str);
02770         
02771         if(!done)
02772                 return OPERATOR_CANCELLED;
02773 
02774         text_update_line_edited(text->curl);
02775 
02776         text_update_cursor_moved(C);
02777         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
02778 
02779         return OPERATOR_FINISHED;
02780 }
02781 
02782 static int insert_invoke(bContext *C, wmOperator *op, wmEvent *event)
02783 {
02784         int ret;
02785 
02786         // if(!RNA_property_is_set(op->ptr, "text")) { /* always set from keymap XXX */
02787         if(!RNA_string_length(op->ptr, "text")) {
02788                 /* if alt/ctrl/super are pressed pass through */
02789                 if(event->ctrl || event->oskey) {
02790                         return OPERATOR_PASS_THROUGH;
02791                 }
02792                 else {
02793                         char str[2];
02794                         str[0]= event->ascii;
02795                         str[1]= '\0';
02796                         RNA_string_set(op->ptr, "text", str);
02797                 }
02798         }
02799 
02800         ret = insert_exec(C, op);
02801         
02802         /* run the script while editing, evil but useful */
02803         if(ret==OPERATOR_FINISHED && CTX_wm_space_text(C)->live_edit)
02804                 run_script(C, NULL);
02805 
02806         return ret;
02807 }
02808 
02809 void TEXT_OT_insert(wmOperatorType *ot)
02810 {
02811         /* identifiers */
02812         ot->name= "Insert";
02813         ot->idname= "TEXT_OT_insert";
02814         ot->description= "Insert text at cursor position";
02815         
02816         /* api callbacks */
02817         ot->exec= insert_exec;
02818         ot->invoke= insert_invoke;
02819         ot->poll= text_edit_poll;
02820 
02821         /* properties */
02822         RNA_def_string(ot->srna, "text", "", 0, "Text", "Text to insert at the cursor position.");
02823 }
02824 
02825 /******************* find operator *********************/
02826 
02827 /* mode */
02828 #define TEXT_FIND               0
02829 #define TEXT_REPLACE    1
02830 #define TEXT_MARK_ALL   2
02831 
02832 static int find_and_replace(bContext *C, wmOperator *op, short mode)
02833 {
02834         Main *bmain= CTX_data_main(C);
02835         SpaceText *st= CTX_wm_space_text(C);
02836         Text *start= NULL, *text= st->text;
02837         int flags, first= 1;
02838         int found = 0;
02839         char *tmp;
02840 
02841         if(!st->findstr[0] || (mode == TEXT_REPLACE && !st->replacestr[0]))
02842                 return OPERATOR_CANCELLED;
02843 
02844         flags= st->flags;
02845         if(flags & ST_FIND_ALL)
02846                 flags ^= ST_FIND_WRAP;
02847 
02848         do {
02849                 int proceed= 0;
02850 
02851                 if(first) {
02852                         if(text->markers.first)
02853                                 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
02854 
02855                         txt_clear_markers(text, TMARK_GRP_FINDALL, 0);
02856                 }
02857 
02858                 first= 0;
02859                 
02860                 /* Replace current */
02861                 if(mode!=TEXT_FIND && txt_has_sel(text)) {
02862                         tmp= txt_sel_to_buf(text);
02863 
02864                         if(flags & ST_MATCH_CASE) proceed= strcmp(st->findstr, tmp)==0;
02865                         else proceed= BLI_strcasecmp(st->findstr, tmp)==0;
02866 
02867                         if(proceed) {
02868                                 if(mode==TEXT_REPLACE) {
02869                                         txt_insert_buf(text, st->replacestr);
02870                                         if(text->curl && text->curl->format) {
02871                                                 MEM_freeN(text->curl->format);
02872                                                 text->curl->format= NULL;
02873                                         }
02874                                         text_update_cursor_moved(C);
02875                                         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
02876                                         text_drawcache_tag_update(CTX_wm_space_text(C), 1);
02877                                 }
02878                                 else if(mode==TEXT_MARK_ALL) {
02879                                         unsigned char color[4];
02880                                         UI_GetThemeColor4ubv(TH_SHADE2, color);
02881 
02882                                         if(txt_find_marker(text, text->curl, text->selc, TMARK_GRP_FINDALL, 0)) {
02883                                                 if(tmp) MEM_freeN(tmp), tmp=NULL;
02884                                                 break;
02885                                         }
02886 
02887                                         txt_add_marker(text, text->curl, text->curc, text->selc, color, TMARK_GRP_FINDALL, TMARK_EDITALL);
02888                                         text_update_cursor_moved(C);
02889                                         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
02890                                 }
02891                         }
02892                         MEM_freeN(tmp);
02893                         tmp= NULL;
02894                 }
02895 
02896                 /* Find next */
02897                 if(txt_find_string(text, st->findstr, flags & ST_FIND_WRAP, flags & ST_MATCH_CASE)) {
02898                         text_update_cursor_moved(C);
02899                         WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
02900                 }
02901                 else if(flags & ST_FIND_ALL) {
02902                         if(text==start) break;
02903                         if(!start) start= text;
02904                         if(text->id.next)
02905                                 text= st->text= text->id.next;
02906                         else
02907                                 text= st->text= bmain->text.first;
02908                         txt_move_toline(text, 0, 0);
02909                         text_update_cursor_moved(C);
02910                         WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
02911                         first= 1;
02912                 }
02913                 else {
02914                         if(!found && !proceed) BKE_reportf(op->reports, RPT_ERROR, "Text not found: %s", st->findstr);
02915                         break;
02916                 }
02917                 found = 1;
02918         } while(mode==TEXT_MARK_ALL);
02919 
02920         return OPERATOR_FINISHED;
02921 }
02922 
02923 static int find_exec(bContext *C, wmOperator *op)
02924 {
02925         return find_and_replace(C, op, TEXT_FIND);
02926 }
02927 
02928 void TEXT_OT_find(wmOperatorType *ot)
02929 {
02930         /* identifiers */
02931         ot->name= "Find";
02932         ot->idname= "TEXT_OT_find";
02933         ot->description= "Find specified text";
02934         
02935         /* api callbacks */
02936         ot->exec= find_exec;
02937         ot->poll= text_space_edit_poll;
02938 }
02939 
02940 /******************* replace operator *********************/
02941 
02942 static int replace_exec(bContext *C, wmOperator *op)
02943 {
02944         return find_and_replace(C, op, TEXT_REPLACE);
02945 }
02946 
02947 void TEXT_OT_replace(wmOperatorType *ot)
02948 {
02949         /* identifiers */
02950         ot->name= "Replace";
02951         ot->idname= "TEXT_OT_replace";
02952         ot->description= "Replace text with the specified text";
02953 
02954         /* api callbacks */
02955         ot->exec= replace_exec;
02956         ot->poll= text_space_edit_poll;
02957 }
02958 
02959 /******************* mark all operator *********************/
02960 
02961 static int mark_all_exec(bContext *C, wmOperator *op)
02962 {
02963         return find_and_replace(C, op, TEXT_MARK_ALL);
02964 }
02965 
02966 void TEXT_OT_mark_all(wmOperatorType *ot)
02967 {
02968         /* identifiers */
02969         ot->name= "Mark All";
02970         ot->idname= "TEXT_OT_mark_all";
02971         ot->description= "Mark all specified text";
02972         
02973         /* api callbacks */
02974         ot->exec= mark_all_exec;
02975         ot->poll= text_space_edit_poll;
02976 }
02977 
02978 /******************* find set selected *********************/
02979 
02980 static int find_set_selected_exec(bContext *C, wmOperator *op)
02981 {
02982         SpaceText *st= CTX_wm_space_text(C);
02983         Text *text= CTX_data_edit_text(C);
02984         char *tmp;
02985 
02986         tmp= txt_sel_to_buf(text);
02987         BLI_strncpy(st->findstr, tmp, ST_MAX_FIND_STR);
02988         MEM_freeN(tmp);
02989 
02990         if(!st->findstr[0])
02991                 return OPERATOR_FINISHED;
02992 
02993         return find_and_replace(C, op, TEXT_FIND);
02994 }
02995 
02996 void TEXT_OT_find_set_selected(wmOperatorType *ot)
02997 {
02998         /* identifiers */
02999         ot->name= "Find Set Selected";
03000         ot->idname= "TEXT_OT_find_set_selected";
03001         ot->description= "Find specified text and set as selected";
03002         
03003         /* api callbacks */
03004         ot->exec= find_set_selected_exec;
03005         ot->poll= text_space_edit_poll;
03006 }
03007 
03008 /******************* replace set selected *********************/
03009 
03010 static int replace_set_selected_exec(bContext *C, wmOperator *UNUSED(op))
03011 {
03012         SpaceText *st= CTX_wm_space_text(C);
03013         Text *text= CTX_data_edit_text(C);
03014         char *tmp;
03015 
03016         tmp= txt_sel_to_buf(text);
03017         BLI_strncpy(st->replacestr, tmp, ST_MAX_FIND_STR);
03018         MEM_freeN(tmp);
03019 
03020         return OPERATOR_FINISHED;
03021 }
03022 
03023 void TEXT_OT_replace_set_selected(wmOperatorType *ot)
03024 {
03025         /* identifiers */
03026         ot->name= "Replace Set Selected";
03027         ot->idname= "TEXT_OT_replace_set_selected";
03028         ot->description= "Replace text with specified text and set as selected";
03029         
03030         /* api callbacks */
03031         ot->exec= replace_set_selected_exec;
03032         ot->poll= text_space_edit_poll;
03033 }
03034 
03035 /****************** resolve conflict operator ******************/
03036 
03037 enum { RESOLVE_IGNORE, RESOLVE_RELOAD, RESOLVE_SAVE, RESOLVE_MAKE_INTERNAL };
03038 static EnumPropertyItem resolution_items[]= {
03039         {RESOLVE_IGNORE, "IGNORE", 0, "Ignore", ""},
03040         {RESOLVE_RELOAD, "RELOAD", 0, "Reload", ""},
03041         {RESOLVE_SAVE, "SAVE", 0, "Save", ""},
03042         {RESOLVE_MAKE_INTERNAL, "MAKE_INTERNAL", 0, "Make Internal", ""},
03043         {0, NULL, 0, NULL, NULL}};
03044 
03045 /* returns 0 if file on disk is the same or Text is in memory only
03046    returns 1 if file has been modified on disk since last local edit
03047    returns 2 if file on disk has been deleted
03048    -1 is returned if an error occurs */
03049 
03050 int text_file_modified(Text *text)
03051 {
03052         struct stat st;
03053         int result;
03054         char file[FILE_MAXDIR+FILE_MAXFILE];
03055 
03056         if(!text || !text->name)
03057                 return 0;
03058 
03059         BLI_strncpy(file, text->name, FILE_MAXDIR+FILE_MAXFILE);
03060         BLI_path_abs(file, G.main->name);
03061 
03062         if(!BLI_exists(file))
03063                 return 2;
03064 
03065         result = stat(file, &st);
03066         
03067         if(result == -1)
03068                 return -1;
03069 
03070         if((st.st_mode & S_IFMT) != S_IFREG)
03071                 return -1;
03072 
03073         if(st.st_mtime > text->mtime)
03074                 return 1;
03075 
03076         return 0;
03077 }
03078 
03079 static void text_ignore_modified(Text *text)
03080 {
03081         struct stat st;
03082         int result;
03083         char file[FILE_MAXDIR+FILE_MAXFILE];
03084 
03085         if(!text || !text->name) return;
03086 
03087         BLI_strncpy(file, text->name, FILE_MAXDIR+FILE_MAXFILE);
03088         BLI_path_abs(file, G.main->name);
03089 
03090         if(!BLI_exists(file)) return;
03091 
03092         result = stat(file, &st);
03093         
03094         if(result == -1 || (st.st_mode & S_IFMT) != S_IFREG)
03095                 return;
03096 
03097         text->mtime= st.st_mtime;
03098 }
03099 
03100 static int resolve_conflict_exec(bContext *C, wmOperator *op)
03101 {
03102         Text *text= CTX_data_edit_text(C);
03103         int resolution= RNA_enum_get(op->ptr, "resolution");
03104 
03105         switch(resolution) {
03106                 case RESOLVE_RELOAD:
03107                         return reload_exec(C, op);
03108                 case RESOLVE_SAVE:
03109                         return save_exec(C, op);
03110                 case RESOLVE_MAKE_INTERNAL:
03111                         return make_internal_exec(C, op);
03112                 case RESOLVE_IGNORE:
03113                         text_ignore_modified(text);
03114                         return OPERATOR_FINISHED;
03115         }
03116 
03117         return OPERATOR_CANCELLED;
03118 }
03119 
03120 static int resolve_conflict_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
03121 {
03122         Text *text= CTX_data_edit_text(C);
03123         uiPopupMenu *pup;
03124         uiLayout *layout;
03125 
03126         switch(text_file_modified(text)) {
03127                 case 1:
03128                         if(text->flags & TXT_ISDIRTY) {
03129                                 /* modified locally and externally, ahhh. offer more possibilites. */
03130                                 pup= uiPupMenuBegin(C, "File Modified Outside and Inside Blender", ICON_NONE);
03131                                 layout= uiPupMenuLayout(pup);
03132                                 uiItemEnumO(layout, op->type->idname, "Reload from disk (ignore local changes)", 0, "resolution", RESOLVE_RELOAD);
03133                                 uiItemEnumO(layout, op->type->idname, "Save to disk (ignore outside changes)", 0, "resolution", RESOLVE_SAVE);
03134                                 uiItemEnumO(layout, op->type->idname, "Make text internal (separate copy)", 0, "resolution", RESOLVE_MAKE_INTERNAL);
03135                                 uiPupMenuEnd(C, pup);
03136                         }
03137                         else {
03138                                 pup= uiPupMenuBegin(C, "File Modified Outside Blender", ICON_NONE);
03139                                 layout= uiPupMenuLayout(pup);
03140                                 uiItemEnumO(layout, op->type->idname, "Reload from disk", 0, "resolution", RESOLVE_RELOAD);
03141                                 uiItemEnumO(layout, op->type->idname, "Make text internal (separate copy)", 0, "resolution", RESOLVE_MAKE_INTERNAL);
03142                                 uiItemEnumO(layout, op->type->idname, "Ignore", 0, "resolution", RESOLVE_IGNORE);
03143                                 uiPupMenuEnd(C, pup);
03144                         }
03145                         break;
03146                 case 2:
03147                         pup= uiPupMenuBegin(C, "File Deleted Outside Blender", ICON_NONE);
03148                         layout= uiPupMenuLayout(pup);
03149                         uiItemEnumO(layout, op->type->idname, "Make text internal", 0, "resolution", RESOLVE_MAKE_INTERNAL);
03150                         uiItemEnumO(layout, op->type->idname, "Recreate file", 0, "resolution", RESOLVE_SAVE);
03151                         uiPupMenuEnd(C, pup);
03152                         break;
03153         }
03154 
03155         return OPERATOR_CANCELLED;
03156 }
03157 
03158 void TEXT_OT_resolve_conflict(wmOperatorType *ot)
03159 {
03160         /* identifiers */
03161         ot->name= "Resolve Conflict";
03162         ot->idname= "TEXT_OT_resolve_conflict";
03163         ot->description= "When external text is out of sync, resolve the conflict";
03164 
03165         /* api callbacks */
03166         ot->exec= resolve_conflict_exec;
03167         ot->invoke= resolve_conflict_invoke;
03168         ot->poll= save_poll;
03169 
03170         /* properties */
03171         RNA_def_enum(ot->srna, "resolution", resolution_items, RESOLVE_IGNORE, "Resolution", "How to solve conflict due to different in internal and external text.");
03172 }
03173 
03174 /********************** to 3d object operator *****************/
03175 
03176 static int to_3d_object_exec(bContext *C, wmOperator *op)
03177 {
03178         Text *text= CTX_data_edit_text(C);
03179         int split_lines= RNA_boolean_get(op->ptr, "split_lines");
03180 
03181         ED_text_to_object(C, text, split_lines);
03182 
03183         return OPERATOR_FINISHED;
03184 }
03185 
03186 void TEXT_OT_to_3d_object(wmOperatorType *ot)
03187 {
03188         /* identifiers */
03189         ot->name= "To 3D Object";
03190         ot->idname= "TEXT_OT_to_3d_object";
03191         ot->description= "Create 3d text object from active text data block";
03192         
03193         /* api callbacks */
03194         ot->exec= to_3d_object_exec;
03195         ot->poll= text_edit_poll;
03196         
03197         /* flags */
03198         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
03199 
03200         /* properties */
03201         RNA_def_boolean(ot->srna, "split_lines", 0, "Split Lines", "Create one object per line in the text.");
03202 }
03203 
03204 
03205 /************************ undo ******************************/
03206 
03207 void ED_text_undo_step(bContext *C, int step)
03208 {
03209         Text *text= CTX_data_edit_text(C);
03210 
03211         if(!text)
03212                 return;
03213 
03214         if(step==1)
03215                 txt_do_undo(text);
03216         else if(step==-1)
03217                 txt_do_redo(text);
03218 
03219         text_update_edited(text);
03220 
03221         text_update_cursor_moved(C);
03222         text_drawcache_tag_update(CTX_wm_space_text(C), 1);
03223         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
03224 }
03225