Blender V2.61 - r43446
|
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