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