Blender V2.61 - r43446

text_ops.c

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