PageRenderTime 72ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/blender-2.63a/source/blender/editors/space_text/text_ops.c

#
C | 3311 lines | 2349 code | 695 blank | 267 comment | 537 complexity | 765ffcbecfc5dca33ebc2eb8a9886fc7 MD5 | raw file
Possible License(s): GPL-3.0, GPL-2.0, BSD-3-Clause, LGPL-3.0, BSD-2-Clause, Apache-2.0, AGPL-1.0
  1. /*
  2. * ***** BEGIN GPL LICENSE BLOCK *****
  3. *
  4. * This program is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU General Public License
  6. * as published by the Free Software Foundation; either version 2
  7. * of the License, or (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software Foundation,
  16. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  17. *
  18. * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
  19. * All rights reserved.
  20. *
  21. * The Original Code is: all of this file.
  22. *
  23. * Contributor(s): none yet.
  24. *
  25. * ***** END GPL LICENSE BLOCK *****
  26. */
  27. /** \file blender/editors/space_text/text_ops.c
  28. * \ingroup sptext
  29. */
  30. #include <stdlib.h>
  31. #include <string.h>
  32. #include <ctype.h> /* ispunct */
  33. #include <sys/stat.h>
  34. #include <errno.h>
  35. #include "MEM_guardedalloc.h"
  36. #include "DNA_text_types.h"
  37. #include "DNA_userdef_types.h"
  38. #include "BLI_blenlib.h"
  39. #include "BLI_utildefines.h"
  40. #include "PIL_time.h"
  41. #include "BKE_context.h"
  42. #include "BKE_global.h"
  43. #include "BKE_library.h"
  44. #include "BKE_main.h"
  45. #include "BKE_report.h"
  46. #include "BKE_text.h"
  47. #include "WM_api.h"
  48. #include "WM_types.h"
  49. #include "ED_text.h"
  50. #include "ED_curve.h"
  51. #include "ED_screen.h"
  52. #include "UI_interface.h"
  53. #include "UI_resources.h"
  54. #include "RNA_access.h"
  55. #include "RNA_define.h"
  56. #ifdef WITH_PYTHON
  57. #include "BPY_extern.h"
  58. #endif
  59. #include "text_intern.h"
  60. /************************ poll ***************************/
  61. BLI_INLINE int text_pixel_x_to_index(SpaceText *st, const int x)
  62. {
  63. /* add half the char width so mouse cursor selection is inbetween letters */
  64. return (x + (st->cwidth / 2)) / st->cwidth;
  65. }
  66. static int text_new_poll(bContext *UNUSED(C))
  67. {
  68. return 1;
  69. }
  70. static int text_edit_poll(bContext *C)
  71. {
  72. Text *text = CTX_data_edit_text(C);
  73. if (!text)
  74. return 0;
  75. if (text->id.lib) {
  76. // BKE_report(op->reports, RPT_ERROR, "Can't edit external libdata");
  77. return 0;
  78. }
  79. return 1;
  80. }
  81. static int text_space_edit_poll(bContext *C)
  82. {
  83. SpaceText *st = CTX_wm_space_text(C);
  84. Text *text = CTX_data_edit_text(C);
  85. if (!st || !text)
  86. return 0;
  87. if (text->id.lib) {
  88. // BKE_report(op->reports, RPT_ERROR, "Can't edit external libdata");
  89. return 0;
  90. }
  91. return 1;
  92. }
  93. static int text_region_edit_poll(bContext *C)
  94. {
  95. SpaceText *st = CTX_wm_space_text(C);
  96. Text *text = CTX_data_edit_text(C);
  97. ARegion *ar = CTX_wm_region(C);
  98. if (!st || !text)
  99. return 0;
  100. if (!ar || ar->regiontype != RGN_TYPE_WINDOW)
  101. return 0;
  102. if (text->id.lib) {
  103. // BKE_report(op->reports, RPT_ERROR, "Can't edit external libdata");
  104. return 0;
  105. }
  106. return 1;
  107. }
  108. /********************** updates *********************/
  109. void text_update_line_edited(TextLine *line)
  110. {
  111. if (!line)
  112. return;
  113. /* we just free format here, and let it rebuild during draw */
  114. if (line->format) {
  115. MEM_freeN(line->format);
  116. line->format = NULL;
  117. }
  118. }
  119. void text_update_edited(Text *text)
  120. {
  121. TextLine *line;
  122. for (line = text->lines.first; line; line = line->next)
  123. text_update_line_edited(line);
  124. }
  125. /******************* new operator *********************/
  126. static int text_new_exec(bContext *C, wmOperator *UNUSED(op))
  127. {
  128. SpaceText *st = CTX_wm_space_text(C);
  129. Text *text;
  130. PointerRNA ptr, idptr;
  131. PropertyRNA *prop;
  132. text = add_empty_text("Text");
  133. /* hook into UI */
  134. uiIDContextProperty(C, &ptr, &prop);
  135. if (prop) {
  136. /* when creating new ID blocks, use is already 1, but RNA
  137. * pointer se also increases user, so this compensates it */
  138. /* doesnt always seem to happen... (ton) */
  139. if (text->id.us > 1)
  140. text->id.us--;
  141. RNA_id_pointer_create(&text->id, &idptr);
  142. RNA_property_pointer_set(&ptr, prop, idptr);
  143. RNA_property_update(C, &ptr, prop);
  144. }
  145. else if (st) {
  146. st->text = text;
  147. st->top = 0;
  148. text_drawcache_tag_update(st, 1);
  149. }
  150. WM_event_add_notifier(C, NC_TEXT | NA_ADDED, text);
  151. return OPERATOR_FINISHED;
  152. }
  153. void TEXT_OT_new(wmOperatorType *ot)
  154. {
  155. /* identifiers */
  156. ot->name = "Create Text Block";
  157. ot->idname = "TEXT_OT_new";
  158. ot->description = "Create a new text data block";
  159. /* api callbacks */
  160. ot->exec = text_new_exec;
  161. ot->poll = text_new_poll;
  162. /* flags */
  163. ot->flag = OPTYPE_UNDO;
  164. }
  165. /******************* open operator *********************/
  166. static void text_open_init(bContext *C, wmOperator *op)
  167. {
  168. PropertyPointerRNA *pprop;
  169. op->customdata = pprop = MEM_callocN(sizeof(PropertyPointerRNA), "OpenPropertyPointerRNA");
  170. uiIDContextProperty(C, &pprop->ptr, &pprop->prop);
  171. }
  172. static int text_open_cancel(bContext *UNUSED(C), wmOperator *op)
  173. {
  174. MEM_freeN(op->customdata);
  175. return OPERATOR_CANCELLED;
  176. }
  177. static int text_open_exec(bContext *C, wmOperator *op)
  178. {
  179. SpaceText *st = CTX_wm_space_text(C);
  180. Text *text;
  181. PropertyPointerRNA *pprop;
  182. PointerRNA idptr;
  183. char str[FILE_MAX];
  184. short internal = RNA_boolean_get(op->ptr, "internal");
  185. RNA_string_get(op->ptr, "filepath", str);
  186. text = add_text(str, G.main->name);
  187. if (!text) {
  188. if (op->customdata) MEM_freeN(op->customdata);
  189. return OPERATOR_CANCELLED;
  190. }
  191. if (!op->customdata)
  192. text_open_init(C, op);
  193. /* hook into UI */
  194. pprop = op->customdata;
  195. if (pprop->prop) {
  196. /* when creating new ID blocks, use is already 1, but RNA
  197. * pointer se also increases user, so this compensates it */
  198. text->id.us--;
  199. RNA_id_pointer_create(&text->id, &idptr);
  200. RNA_property_pointer_set(&pprop->ptr, pprop->prop, idptr);
  201. RNA_property_update(C, &pprop->ptr, pprop->prop);
  202. }
  203. else if (st) {
  204. st->text = text;
  205. st->top = 0;
  206. }
  207. if (internal) {
  208. if (text->name)
  209. MEM_freeN(text->name);
  210. text->name = NULL;
  211. }
  212. text_drawcache_tag_update(st, 1);
  213. WM_event_add_notifier(C, NC_TEXT | NA_ADDED, text);
  214. MEM_freeN(op->customdata);
  215. return OPERATOR_FINISHED;
  216. }
  217. static int text_open_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
  218. {
  219. Text *text = CTX_data_edit_text(C);
  220. char *path = (text && text->name) ? text->name : G.main->name;
  221. if (RNA_struct_property_is_set(op->ptr, "filepath"))
  222. return text_open_exec(C, op);
  223. text_open_init(C, op);
  224. RNA_string_set(op->ptr, "filepath", path);
  225. WM_event_add_fileselect(C, op);
  226. return OPERATOR_RUNNING_MODAL;
  227. }
  228. void TEXT_OT_open(wmOperatorType *ot)
  229. {
  230. /* identifiers */
  231. ot->name = "Open Text Block";
  232. ot->idname = "TEXT_OT_open";
  233. ot->description = "Open a new text data block";
  234. /* api callbacks */
  235. ot->exec = text_open_exec;
  236. ot->invoke = text_open_invoke;
  237. ot->cancel = text_open_cancel;
  238. ot->poll = text_new_poll;
  239. /* flags */
  240. ot->flag = OPTYPE_UNDO;
  241. /* properties */
  242. WM_operator_properties_filesel(ot, FOLDERFILE | TEXTFILE | PYSCRIPTFILE, FILE_SPECIAL, FILE_OPENFILE, WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY); //XXX TODO, relative_path
  243. RNA_def_boolean(ot->srna, "internal", 0, "Make internal", "Make text file internal after loading");
  244. }
  245. /******************* reload operator *********************/
  246. static int text_reload_exec(bContext *C, wmOperator *op)
  247. {
  248. Text *text = CTX_data_edit_text(C);
  249. if (!reopen_text(text)) {
  250. BKE_report(op->reports, RPT_ERROR, "Could not reopen file");
  251. return OPERATOR_CANCELLED;
  252. }
  253. #ifdef WITH_PYTHON
  254. if (text->compiled)
  255. BPY_text_free_code(text);
  256. #endif
  257. text_update_edited(text);
  258. text_update_cursor_moved(C);
  259. text_drawcache_tag_update(CTX_wm_space_text(C), 1);
  260. WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
  261. return OPERATOR_FINISHED;
  262. }
  263. void TEXT_OT_reload(wmOperatorType *ot)
  264. {
  265. /* identifiers */
  266. ot->name = "Reload";
  267. ot->idname = "TEXT_OT_reload";
  268. ot->description = "Reload active text data block from its file";
  269. /* api callbacks */
  270. ot->exec = text_reload_exec;
  271. ot->invoke = WM_operator_confirm;
  272. ot->poll = text_edit_poll;
  273. }
  274. /******************* delete operator *********************/
  275. static int text_unlink_poll(bContext *C)
  276. {
  277. /* it should be possible to unlink texts if they're lib-linked in... */
  278. return CTX_data_edit_text(C) != NULL;
  279. }
  280. static int text_unlink_exec(bContext *C, wmOperator *UNUSED(op))
  281. {
  282. Main *bmain = CTX_data_main(C);
  283. SpaceText *st = CTX_wm_space_text(C);
  284. Text *text = CTX_data_edit_text(C);
  285. /* make the previous text active, if its not there make the next text active */
  286. if (st) {
  287. if (text->id.prev) {
  288. st->text = text->id.prev;
  289. text_update_cursor_moved(C);
  290. WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, st->text);
  291. }
  292. else if (text->id.next) {
  293. st->text = text->id.next;
  294. text_update_cursor_moved(C);
  295. WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, st->text);
  296. }
  297. }
  298. unlink_text(bmain, text);
  299. free_libblock(&bmain->text, text);
  300. text_drawcache_tag_update(st, 1);
  301. WM_event_add_notifier(C, NC_TEXT | NA_REMOVED, NULL);
  302. return OPERATOR_FINISHED;
  303. }
  304. void TEXT_OT_unlink(wmOperatorType *ot)
  305. {
  306. /* identifiers */
  307. ot->name = "Unlink";
  308. ot->idname = "TEXT_OT_unlink";
  309. ot->description = "Unlink active text data block";
  310. /* api callbacks */
  311. ot->exec = text_unlink_exec;
  312. ot->invoke = WM_operator_confirm;
  313. ot->poll = text_unlink_poll;
  314. /* flags */
  315. ot->flag = OPTYPE_UNDO;
  316. }
  317. /******************* make internal operator *********************/
  318. static int text_make_internal_exec(bContext *C, wmOperator *UNUSED(op))
  319. {
  320. Text *text = CTX_data_edit_text(C);
  321. text->flags |= TXT_ISMEM | TXT_ISDIRTY;
  322. if (text->name) {
  323. MEM_freeN(text->name);
  324. text->name = NULL;
  325. }
  326. text_update_cursor_moved(C);
  327. WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
  328. return OPERATOR_FINISHED;
  329. }
  330. void TEXT_OT_make_internal(wmOperatorType *ot)
  331. {
  332. /* identifiers */
  333. ot->name = "Make Internal";
  334. ot->idname = "TEXT_OT_make_internal";
  335. ot->description = "Make active text file internal";
  336. /* api callbacks */
  337. ot->exec = text_make_internal_exec;
  338. ot->poll = text_edit_poll;
  339. /* flags */
  340. ot->flag = OPTYPE_UNDO;
  341. }
  342. /******************* save operator *********************/
  343. static int text_save_poll(bContext *C)
  344. {
  345. Text *text = CTX_data_edit_text(C);
  346. if (!text_edit_poll(C))
  347. return 0;
  348. return (text->name != NULL && !(text->flags & TXT_ISMEM));
  349. }
  350. static void txt_write_file(Text *text, ReportList *reports)
  351. {
  352. FILE *fp;
  353. TextLine *tmp;
  354. struct stat st;
  355. char filepath[FILE_MAX];
  356. BLI_strncpy(filepath, text->name, FILE_MAX);
  357. BLI_path_abs(filepath, G.main->name);
  358. fp = BLI_fopen(filepath, "w");
  359. if (fp == NULL) {
  360. BKE_reportf(reports, RPT_ERROR, "Unable to save \"%s\": %s", filepath, errno ? strerror(errno) : "Unknown error writing file");
  361. return;
  362. }
  363. tmp = text->lines.first;
  364. while (tmp) {
  365. if (tmp->next) fprintf(fp, "%s\n", tmp->line);
  366. else fprintf(fp, "%s", tmp->line);
  367. tmp = tmp->next;
  368. }
  369. fclose(fp);
  370. if (stat(filepath, &st) == 0) {
  371. text->mtime = st.st_mtime;
  372. }
  373. else {
  374. text->mtime = 0;
  375. BKE_reportf(reports, RPT_WARNING, "Unable to stat \"%s\": %s", filepath, errno ? strerror(errno) : "Unknown error starrng file");
  376. }
  377. if (text->flags & TXT_ISDIRTY)
  378. text->flags ^= TXT_ISDIRTY;
  379. }
  380. static int text_save_exec(bContext *C, wmOperator *op)
  381. {
  382. Text *text = CTX_data_edit_text(C);
  383. txt_write_file(text, op->reports);
  384. text_update_cursor_moved(C);
  385. WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
  386. return OPERATOR_FINISHED;
  387. }
  388. void TEXT_OT_save(wmOperatorType *ot)
  389. {
  390. /* identifiers */
  391. ot->name = "Save";
  392. ot->idname = "TEXT_OT_save";
  393. ot->description = "Save active text data block";
  394. /* api callbacks */
  395. ot->exec = text_save_exec;
  396. ot->poll = text_save_poll;
  397. }
  398. /******************* save as operator *********************/
  399. static int text_save_as_exec(bContext *C, wmOperator *op)
  400. {
  401. Text *text = CTX_data_edit_text(C);
  402. char str[FILE_MAX];
  403. if (!text)
  404. return OPERATOR_CANCELLED;
  405. RNA_string_get(op->ptr, "filepath", str);
  406. if (text->name) MEM_freeN(text->name);
  407. text->name = BLI_strdup(str);
  408. text->flags &= ~TXT_ISMEM;
  409. txt_write_file(text, op->reports);
  410. text_update_cursor_moved(C);
  411. WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
  412. return OPERATOR_FINISHED;
  413. }
  414. static int text_save_as_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
  415. {
  416. Text *text = CTX_data_edit_text(C);
  417. char *str;
  418. if (RNA_struct_property_is_set(op->ptr, "filepath"))
  419. return text_save_as_exec(C, op);
  420. if (text->name)
  421. str = text->name;
  422. else if (text->flags & TXT_ISMEM)
  423. str = text->id.name + 2;
  424. else
  425. str = G.main->name;
  426. RNA_string_set(op->ptr, "filepath", str);
  427. WM_event_add_fileselect(C, op);
  428. return OPERATOR_RUNNING_MODAL;
  429. }
  430. void TEXT_OT_save_as(wmOperatorType *ot)
  431. {
  432. /* identifiers */
  433. ot->name = "Save As";
  434. ot->idname = "TEXT_OT_save_as";
  435. ot->description = "Save active text file with options";
  436. /* api callbacks */
  437. ot->exec = text_save_as_exec;
  438. ot->invoke = text_save_as_invoke;
  439. ot->poll = text_edit_poll;
  440. /* properties */
  441. WM_operator_properties_filesel(ot, FOLDERFILE | TEXTFILE | PYSCRIPTFILE, FILE_SPECIAL, FILE_SAVE, WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY); //XXX TODO, relative_path
  442. }
  443. /******************* run script operator *********************/
  444. static int text_run_script_poll(bContext *C)
  445. {
  446. return (CTX_data_edit_text(C) != NULL);
  447. }
  448. static int text_run_script(bContext *C, ReportList *reports)
  449. {
  450. #ifdef WITH_PYTHON
  451. Text *text = CTX_data_edit_text(C);
  452. const short is_live = (reports == NULL);
  453. /* only for comparison */
  454. void *curl_prev = text->curl;
  455. int curc_prev = text->curc;
  456. if (BPY_text_exec(C, text, reports, !is_live)) {
  457. if (is_live) {
  458. /* for nice live updates */
  459. WM_event_add_notifier(C, NC_WINDOW | NA_EDITED, NULL);
  460. }
  461. return OPERATOR_FINISHED;
  462. }
  463. /* Don't report error messages while live editing */
  464. if (!is_live) {
  465. if (text->curl != curl_prev || curc_prev != text->curc) {
  466. text_update_cursor_moved(C);
  467. WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
  468. }
  469. BKE_report(reports, RPT_ERROR, "Python script fail, look in the console for now...");
  470. }
  471. #else
  472. (void)C;
  473. (void)reports;
  474. #endif /* !WITH_PYTHON */
  475. return OPERATOR_CANCELLED;
  476. }
  477. static int text_run_script_exec(bContext *C, wmOperator *op)
  478. {
  479. #ifndef WITH_PYTHON
  480. (void)C; /* unused */
  481. BKE_report(op->reports, RPT_ERROR, "Python disabled in this build");
  482. return OPERATOR_CANCELLED;
  483. #else
  484. return text_run_script(C, op->reports);
  485. #endif
  486. }
  487. void TEXT_OT_run_script(wmOperatorType *ot)
  488. {
  489. /* identifiers */
  490. ot->name = "Run Script";
  491. ot->idname = "TEXT_OT_run_script";
  492. ot->description = "Run active script";
  493. /* api callbacks */
  494. ot->poll = text_run_script_poll;
  495. ot->exec = text_run_script_exec;
  496. /* flags */
  497. ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
  498. }
  499. /******************* refresh pyconstraints operator *********************/
  500. static int text_refresh_pyconstraints_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
  501. {
  502. #ifdef WITH_PYTHON
  503. #if 0
  504. Text *text = CTX_data_edit_text(C);
  505. Object *ob;
  506. bConstraint *con;
  507. short update;
  508. /* check all pyconstraints */
  509. for (ob = CTX_data_main(C)->object.first; ob; ob = ob->id.next) {
  510. update = 0;
  511. if (ob->type == OB_ARMATURE && ob->pose) {
  512. bPoseChannel *pchan;
  513. for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
  514. for (con = pchan->constraints.first; con; con = con->next) {
  515. if (con->type == CONSTRAINT_TYPE_PYTHON) {
  516. bPythonConstraint *data = con->data;
  517. if (data->text == text) BPY_pyconstraint_update(ob, con);
  518. update = 1;
  519. }
  520. }
  521. }
  522. }
  523. for (con = ob->constraints.first; con; con = con->next) {
  524. if (con->type == CONSTRAINT_TYPE_PYTHON) {
  525. bPythonConstraint *data = con->data;
  526. if (data->text == text) BPY_pyconstraint_update(ob, con);
  527. update = 1;
  528. }
  529. }
  530. if (update) {
  531. DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
  532. }
  533. }
  534. #endif
  535. #endif
  536. return OPERATOR_FINISHED;
  537. }
  538. void TEXT_OT_refresh_pyconstraints(wmOperatorType *ot)
  539. {
  540. /* identifiers */
  541. ot->name = "Refresh PyConstraints";
  542. ot->idname = "TEXT_OT_refresh_pyconstraints";
  543. ot->description = "Refresh all pyconstraints";
  544. /* api callbacks */
  545. ot->exec = text_refresh_pyconstraints_exec;
  546. ot->poll = text_edit_poll;
  547. }
  548. /******************* paste operator *********************/
  549. static char *txt_copy_selected(Text *text)
  550. {
  551. TextLine *tmp, *linef, *linel;
  552. char *buf = NULL;
  553. int charf, charl, length = 0;
  554. if (!text) return NULL;
  555. if (!text->curl) return NULL;
  556. if (!text->sell) return NULL;
  557. if (!txt_has_sel(text)) return NULL;
  558. if (text->curl == text->sell) {
  559. linef = linel = text->curl;
  560. if (text->curc < text->selc) {
  561. charf = text->curc;
  562. charl = text->selc;
  563. }
  564. else {
  565. charf = text->selc;
  566. charl = text->curc;
  567. }
  568. }
  569. else if (txt_get_span(text->curl, text->sell) < 0) {
  570. linef = text->sell;
  571. linel = text->curl;
  572. charf = text->selc;
  573. charl = text->curc;
  574. }
  575. else {
  576. linef = text->curl;
  577. linel = text->sell;
  578. charf = text->curc;
  579. charl = text->selc;
  580. }
  581. if (linef == linel) {
  582. length = charl - charf;
  583. buf = MEM_callocN(length + 1, "cut buffera");
  584. BLI_strncpy(buf, linef->line + charf, length + 1);
  585. }
  586. else {
  587. length += linef->len - charf;
  588. length += charl;
  589. length++; /* For the '\n' */
  590. tmp = linef->next;
  591. while (tmp && tmp != linel) {
  592. length += tmp->len + 1;
  593. tmp = tmp->next;
  594. }
  595. buf = MEM_callocN(length + 1, "cut bufferb");
  596. strncpy(buf, linef->line + charf, linef->len - charf);
  597. length = linef->len - charf;
  598. buf[length++] = '\n';
  599. tmp = linef->next;
  600. while (tmp && tmp != linel) {
  601. strncpy(buf + length, tmp->line, tmp->len);
  602. length += tmp->len;
  603. buf[length++] = '\n';
  604. tmp = tmp->next;
  605. }
  606. strncpy(buf + length, linel->line, charl);
  607. length += charl;
  608. buf[length] = 0;
  609. }
  610. return buf;
  611. }
  612. static int text_paste_exec(bContext *C, wmOperator *op)
  613. {
  614. Text *text = CTX_data_edit_text(C);
  615. char *buf;
  616. int selection = RNA_boolean_get(op->ptr, "selection");
  617. buf = WM_clipboard_text_get(selection);
  618. if (!buf)
  619. return OPERATOR_CANCELLED;
  620. text_drawcache_tag_update(CTX_wm_space_text(C), 0);
  621. txt_insert_buf(text, buf);
  622. text_update_edited(text);
  623. MEM_freeN(buf);
  624. text_update_cursor_moved(C);
  625. WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
  626. /* run the script while editing, evil but useful */
  627. if (CTX_wm_space_text(C)->live_edit)
  628. text_run_script(C, NULL);
  629. return OPERATOR_FINISHED;
  630. }
  631. void TEXT_OT_paste(wmOperatorType *ot)
  632. {
  633. /* identifiers */
  634. ot->name = "Paste";
  635. ot->idname = "TEXT_OT_paste";
  636. ot->description = "Paste text from clipboard";
  637. /* api callbacks */
  638. ot->exec = text_paste_exec;
  639. ot->poll = text_edit_poll;
  640. /* properties */
  641. RNA_def_boolean(ot->srna, "selection", 0, "Selection", "Paste text selected elsewhere rather than copied (X11 only)");
  642. }
  643. /******************* copy operator *********************/
  644. static void txt_copy_clipboard(Text *text)
  645. {
  646. char *buf;
  647. buf = txt_copy_selected(text);
  648. if (buf) {
  649. WM_clipboard_text_set(buf, 0);
  650. MEM_freeN(buf);
  651. }
  652. }
  653. static int text_copy_exec(bContext *C, wmOperator *UNUSED(op))
  654. {
  655. Text *text = CTX_data_edit_text(C);
  656. txt_copy_clipboard(text);
  657. return OPERATOR_FINISHED;
  658. }
  659. void TEXT_OT_copy(wmOperatorType *ot)
  660. {
  661. /* identifiers */
  662. ot->name = "Copy";
  663. ot->idname = "TEXT_OT_copy";
  664. ot->description = "Copy selected text to clipboard";
  665. /* api callbacks */
  666. ot->exec = text_copy_exec;
  667. ot->poll = text_edit_poll;
  668. }
  669. /******************* cut operator *********************/
  670. static int text_cut_exec(bContext *C, wmOperator *UNUSED(op))
  671. {
  672. Text *text = CTX_data_edit_text(C);
  673. text_drawcache_tag_update(CTX_wm_space_text(C), 0);
  674. txt_copy_clipboard(text);
  675. txt_delete_selected(text);
  676. text_update_cursor_moved(C);
  677. WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
  678. /* run the script while editing, evil but useful */
  679. if (CTX_wm_space_text(C)->live_edit)
  680. text_run_script(C, NULL);
  681. return OPERATOR_FINISHED;
  682. }
  683. void TEXT_OT_cut(wmOperatorType *ot)
  684. {
  685. /* identifiers */
  686. ot->name = "Cut";
  687. ot->idname = "TEXT_OT_cut";
  688. ot->description = "Cut selected text to clipboard";
  689. /* api callbacks */
  690. ot->exec = text_cut_exec;
  691. ot->poll = text_edit_poll;
  692. }
  693. /******************* indent operator *********************/
  694. static int text_indent_exec(bContext *C, wmOperator *UNUSED(op))
  695. {
  696. Text *text = CTX_data_edit_text(C);
  697. text_drawcache_tag_update(CTX_wm_space_text(C), 0);
  698. if (txt_has_sel(text)) {
  699. txt_order_cursors(text);
  700. txt_indent(text);
  701. }
  702. else
  703. txt_add_char(text, '\t');
  704. text_update_edited(text);
  705. text_update_cursor_moved(C);
  706. WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
  707. return OPERATOR_FINISHED;
  708. }
  709. void TEXT_OT_indent(wmOperatorType *ot)
  710. {
  711. /* identifiers */
  712. ot->name = "Indent";
  713. ot->idname = "TEXT_OT_indent";
  714. ot->description = "Indent selected text";
  715. /* api callbacks */
  716. ot->exec = text_indent_exec;
  717. ot->poll = text_edit_poll;
  718. }
  719. /******************* unindent operator *********************/
  720. static int text_unindent_exec(bContext *C, wmOperator *UNUSED(op))
  721. {
  722. Text *text = CTX_data_edit_text(C);
  723. if (txt_has_sel(text)) {
  724. text_drawcache_tag_update(CTX_wm_space_text(C), 0);
  725. txt_order_cursors(text);
  726. txt_unindent(text);
  727. text_update_edited(text);
  728. text_update_cursor_moved(C);
  729. WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
  730. return OPERATOR_FINISHED;
  731. }
  732. return OPERATOR_CANCELLED;
  733. }
  734. void TEXT_OT_unindent(wmOperatorType *ot)
  735. {
  736. /* identifiers */
  737. ot->name = "Unindent";
  738. ot->idname = "TEXT_OT_unindent";
  739. ot->description = "Unindent selected text";
  740. /* api callbacks */
  741. ot->exec = text_unindent_exec;
  742. ot->poll = text_edit_poll;
  743. }
  744. /******************* line break operator *********************/
  745. static int text_line_break_exec(bContext *C, wmOperator *UNUSED(op))
  746. {
  747. SpaceText *st = CTX_wm_space_text(C);
  748. Text *text = CTX_data_edit_text(C);
  749. int a, curts;
  750. int space = (text->flags & TXT_TABSTOSPACES) ? st->tabnumber : 1;
  751. text_drawcache_tag_update(st, 0);
  752. // double check tabs/spaces before splitting the line
  753. curts = setcurr_tab_spaces(text, space);
  754. txt_split_curline(text);
  755. for (a = 0; a < curts; a++) {
  756. if (text->flags & TXT_TABSTOSPACES) {
  757. txt_add_char(text, ' ');
  758. }
  759. else {
  760. txt_add_char(text, '\t');
  761. }
  762. }
  763. if (text->curl) {
  764. if (text->curl->prev)
  765. text_update_line_edited(text->curl->prev);
  766. text_update_line_edited(text->curl);
  767. }
  768. text_update_cursor_moved(C);
  769. WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
  770. return OPERATOR_CANCELLED;
  771. }
  772. void TEXT_OT_line_break(wmOperatorType *ot)
  773. {
  774. /* identifiers */
  775. ot->name = "Line Break";
  776. ot->idname = "TEXT_OT_line_break";
  777. ot->description = "Insert line break at cursor position";
  778. /* api callbacks */
  779. ot->exec = text_line_break_exec;
  780. ot->poll = text_edit_poll;
  781. }
  782. /******************* comment operator *********************/
  783. static int text_comment_exec(bContext *C, wmOperator *UNUSED(op))
  784. {
  785. Text *text = CTX_data_edit_text(C);
  786. if (txt_has_sel(text)) {
  787. text_drawcache_tag_update(CTX_wm_space_text(C), 0);
  788. txt_order_cursors(text);
  789. txt_comment(text);
  790. text_update_edited(text);
  791. text_update_cursor_moved(C);
  792. WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
  793. return OPERATOR_FINISHED;
  794. }
  795. return OPERATOR_CANCELLED;
  796. }
  797. void TEXT_OT_comment(wmOperatorType *ot)
  798. {
  799. /* identifiers */
  800. ot->name = "Comment";
  801. ot->idname = "TEXT_OT_comment";
  802. ot->description = "Convert selected text to comment";
  803. /* api callbacks */
  804. ot->exec = text_comment_exec;
  805. ot->poll = text_edit_poll;
  806. }
  807. /******************* uncomment operator *********************/
  808. static int text_uncomment_exec(bContext *C, wmOperator *UNUSED(op))
  809. {
  810. Text *text = CTX_data_edit_text(C);
  811. if (txt_has_sel(text)) {
  812. text_drawcache_tag_update(CTX_wm_space_text(C), 0);
  813. txt_order_cursors(text);
  814. txt_uncomment(text);
  815. text_update_edited(text);
  816. text_update_cursor_moved(C);
  817. WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
  818. return OPERATOR_FINISHED;
  819. }
  820. return OPERATOR_CANCELLED;
  821. }
  822. void TEXT_OT_uncomment(wmOperatorType *ot)
  823. {
  824. /* identifiers */
  825. ot->name = "Uncomment";
  826. ot->idname = "TEXT_OT_uncomment";
  827. ot->description = "Convert selected comment to text";
  828. /* api callbacks */
  829. ot->exec = text_uncomment_exec;
  830. ot->poll = text_edit_poll;
  831. }
  832. /******************* convert whitespace operator *********************/
  833. enum { TO_SPACES, TO_TABS };
  834. static EnumPropertyItem whitespace_type_items[] = {
  835. {TO_SPACES, "SPACES", 0, "To Spaces", NULL},
  836. {TO_TABS, "TABS", 0, "To Tabs", NULL},
  837. {0, NULL, 0, NULL, NULL}};
  838. static int text_convert_whitespace_exec(bContext *C, wmOperator *op)
  839. {
  840. SpaceText *st = CTX_wm_space_text(C);
  841. Text *text = CTX_data_edit_text(C);
  842. TextLine *tmp;
  843. FlattenString fs;
  844. size_t a, j;
  845. char *text_check_line, *new_line;
  846. int extra, number; //unknown for now
  847. int type = RNA_enum_get(op->ptr, "type");
  848. tmp = text->lines.first;
  849. //first convert to all space, this make it a lot easier to convert to tabs because there is no mixtures of ' ' && '\t'
  850. while (tmp) {
  851. text_check_line = tmp->line;
  852. number = flatten_string(st, &fs, text_check_line) + 1;
  853. flatten_string_free(&fs);
  854. new_line = MEM_callocN(number, "Converted_Line");
  855. j = 0;
  856. for (a = 0; a < strlen(text_check_line); a++) { //foreach char in line
  857. if (text_check_line[a] == '\t') { //checking for tabs
  858. //get the number of spaces this tabs is showing
  859. //i don't like doing it this way but will look into it later
  860. new_line[j] = '\0';
  861. number = flatten_string(st, &fs, new_line);
  862. flatten_string_free(&fs);
  863. new_line[j] = '\t';
  864. new_line[j + 1] = '\0';
  865. number = flatten_string(st, &fs, new_line) - number;
  866. flatten_string_free(&fs);
  867. for (extra = 0; extra < number; extra++) {
  868. new_line[j] = ' ';
  869. j++;
  870. }
  871. }
  872. else {
  873. new_line[j] = text_check_line[a];
  874. ++j;
  875. }
  876. }
  877. new_line[j] = '\0';
  878. // put new_line in the tmp->line spot still need to try and set the curc correctly
  879. if (tmp->line) MEM_freeN(tmp->line);
  880. if (tmp->format) MEM_freeN(tmp->format);
  881. tmp->line = new_line;
  882. tmp->len = strlen(new_line);
  883. tmp->format = NULL;
  884. tmp = tmp->next;
  885. }
  886. if (type == TO_TABS) { // Converting to tabs
  887. //start over from the beginning
  888. tmp = text->lines.first;
  889. while (tmp) {
  890. text_check_line = tmp->line;
  891. extra = 0;
  892. for (a = 0; a < strlen(text_check_line); a++) {
  893. number = 0;
  894. for (j = 0; j < (size_t)st->tabnumber; j++) {
  895. if ((a + j) <= strlen(text_check_line)) { //check to make sure we are not pass the end of the line
  896. if (text_check_line[a + j] != ' ') {
  897. number = 1;
  898. }
  899. }
  900. }
  901. if (!number) { //found all number of space to equal a tab
  902. a = a + (st->tabnumber - 1);
  903. extra = extra + 1;
  904. }
  905. }
  906. if (extra > 0) { //got tabs make malloc and do what you have to do
  907. new_line = MEM_callocN(strlen(text_check_line) - (((st->tabnumber * extra) - extra) - 1), "Converted_Line");
  908. extra = 0; //reuse vars
  909. for (a = 0; a < strlen(text_check_line); a++) {
  910. number = 0;
  911. for (j = 0; j < (size_t)st->tabnumber; j++) {
  912. if ((a + j) <= strlen(text_check_line)) { //check to make sure we are not pass the end of the line
  913. if (text_check_line[a + j] != ' ') {
  914. number = 1;
  915. }
  916. }
  917. }
  918. if (!number) { //found all number of space to equal a tab
  919. new_line[extra] = '\t';
  920. a = a + (st->tabnumber - 1);
  921. ++extra;
  922. }
  923. else { //not adding a tab
  924. new_line[extra] = text_check_line[a];
  925. ++extra;
  926. }
  927. }
  928. new_line[extra] = '\0';
  929. // put new_line in the tmp->line spot still need to try and set the curc correctly
  930. if (tmp->line) MEM_freeN(tmp->line);
  931. if (tmp->format) MEM_freeN(tmp->format);
  932. tmp->line = new_line;
  933. tmp->len = strlen(new_line);
  934. tmp->format = NULL;
  935. }
  936. tmp = tmp->next;
  937. }
  938. }
  939. text_update_edited(text);
  940. text_update_cursor_moved(C);
  941. text_drawcache_tag_update(st, 1);
  942. WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
  943. return OPERATOR_FINISHED;
  944. }
  945. void TEXT_OT_convert_whitespace(wmOperatorType *ot)
  946. {
  947. /* identifiers */
  948. ot->name = "Convert Whitespace";
  949. ot->idname = "TEXT_OT_convert_whitespace";
  950. ot->description = "Convert whitespaces by type";
  951. /* api callbacks */
  952. ot->exec = text_convert_whitespace_exec;
  953. ot->poll = text_edit_poll;
  954. /* properties */
  955. RNA_def_enum(ot->srna, "type", whitespace_type_items, TO_SPACES, "Type", "Type of whitespace to convert to");
  956. }
  957. /******************* select all operator *********************/
  958. static int text_select_all_exec(bContext *C, wmOperator *UNUSED(op))
  959. {
  960. Text *text = CTX_data_edit_text(C);
  961. txt_sel_all(text);
  962. text_update_cursor_moved(C);
  963. WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
  964. return OPERATOR_FINISHED;
  965. }
  966. void TEXT_OT_select_all(wmOperatorType *ot)
  967. {
  968. /* identifiers */
  969. ot->name = "Select All";
  970. ot->idname = "TEXT_OT_select_all";
  971. ot->description = "Select all text";
  972. /* api callbacks */
  973. ot->exec = text_select_all_exec;
  974. ot->poll = text_edit_poll;
  975. }
  976. /******************* select line operator *********************/
  977. static int text_select_line_exec(bContext *C, wmOperator *UNUSED(op))
  978. {
  979. Text *text = CTX_data_edit_text(C);
  980. txt_sel_line(text);
  981. text_update_cursor_moved(C);
  982. WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
  983. return OPERATOR_FINISHED;
  984. }
  985. void TEXT_OT_select_line(wmOperatorType *ot)
  986. {
  987. /* identifiers */
  988. ot->name = "Select Line";
  989. ot->idname = "TEXT_OT_select_line";
  990. ot->description = "Select text by line";
  991. /* api callbacks */
  992. ot->exec = text_select_line_exec;
  993. ot->poll = text_edit_poll;
  994. }
  995. /******************* select word operator *********************/
  996. static int text_select_word_exec(bContext *C, wmOperator *UNUSED(op))
  997. {
  998. Text *text = CTX_data_edit_text(C);
  999. txt_jump_left(text, 0);
  1000. txt_jump_right(text, 1);
  1001. text_update_cursor_moved(C);
  1002. WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
  1003. return OPERATOR_FINISHED;
  1004. }
  1005. void TEXT_OT_select_word(wmOperatorType *ot)
  1006. {
  1007. /* identifiers */
  1008. ot->name = "Select Word";
  1009. ot->idname = "TEXT_OT_select_word";
  1010. ot->description = "Select word under cursor";
  1011. /* api callbacks */
  1012. ot->exec = text_select_word_exec;
  1013. ot->poll = text_edit_poll;
  1014. }
  1015. /******************* previous marker operator *********************/
  1016. static int text_previous_marker_exec(bContext *C, wmOperator *UNUSED(op))
  1017. {
  1018. Text *text = CTX_data_edit_text(C);
  1019. TextMarker *mrk;
  1020. int lineno;
  1021. lineno = txt_get_span(text->lines.first, text->curl);
  1022. mrk = text->markers.last;
  1023. while (mrk && (mrk->lineno > lineno || (mrk->lineno == lineno && mrk->end > text->curc)))
  1024. mrk = mrk->prev;
  1025. if (!mrk) mrk = text->markers.last;
  1026. if (mrk) {
  1027. txt_move_to(text, mrk->lineno, mrk->start, 0);
  1028. txt_move_to(text, mrk->lineno, mrk->end, 1);
  1029. }
  1030. text_update_cursor_moved(C);
  1031. WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
  1032. return OPERATOR_FINISHED;
  1033. }
  1034. void TEXT_OT_previous_marker(wmOperatorType *ot)
  1035. {
  1036. /* identifiers */
  1037. ot->name = "Previous Marker";
  1038. ot->idname = "TEXT_OT_previous_marker";
  1039. ot->description = "Move to previous marker";
  1040. /* api callbacks */
  1041. ot->exec = text_previous_marker_exec;
  1042. ot->poll = text_edit_poll;
  1043. }
  1044. /******************* next marker operator *********************/
  1045. static int text_next_marker_exec(bContext *C, wmOperator *UNUSED(op))
  1046. {
  1047. Text *text = CTX_data_edit_text(C);
  1048. TextMarker *mrk;
  1049. int lineno;
  1050. lineno = txt_get_span(text->lines.first, text->curl);
  1051. mrk = text->markers.first;
  1052. while (mrk && (mrk->lineno < lineno || (mrk->lineno == lineno && mrk->start <= text->curc)))
  1053. mrk = mrk->next;
  1054. if (!mrk) mrk = text->markers.first;
  1055. if (mrk) {
  1056. txt_move_to(text, mrk->lineno, mrk->start, 0);
  1057. txt_move_to(text, mrk->lineno, mrk->end, 1);
  1058. }
  1059. text_update_cursor_moved(C);
  1060. WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
  1061. return OPERATOR_FINISHED;
  1062. }
  1063. void TEXT_OT_next_marker(wmOperatorType *ot)
  1064. {
  1065. /* identifiers */
  1066. ot->name = "Next Marker";
  1067. ot->idname = "TEXT_OT_next_marker";
  1068. ot->description = "Move to next marker";
  1069. /* api callbacks */
  1070. ot->exec = text_next_marker_exec;
  1071. ot->poll = text_edit_poll;
  1072. }
  1073. /******************* clear all markers operator *********************/
  1074. static int text_clear_all_markers_exec(bContext *C, wmOperator *UNUSED(op))
  1075. {
  1076. Text *text = CTX_data_edit_text(C);
  1077. txt_clear_markers(text, 0, 0);
  1078. text_update_cursor_moved(C);
  1079. WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
  1080. return OPERATOR_FINISHED;
  1081. }
  1082. void TEXT_OT_markers_clear(wmOperatorType *ot)
  1083. {
  1084. /* identifiers */
  1085. ot->name = "Clear All Markers";
  1086. ot->idname = "TEXT_OT_markers_clear";
  1087. ot->description = "Clear all markers";
  1088. /* api callbacks */
  1089. ot->exec = text_clear_all_markers_exec;
  1090. ot->poll = text_edit_poll;
  1091. }
  1092. /************************ move operator ************************/
  1093. static EnumPropertyItem move_type_items[] = {
  1094. {LINE_BEGIN, "LINE_BEGIN", 0, "Line Begin", ""},
  1095. {LINE_END, "LINE_END", 0, "Line End", ""},
  1096. {FILE_TOP, "FILE_TOP", 0, "File Top", ""},
  1097. {FILE_BOTTOM, "FILE_BOTTOM", 0, "File Bottom", ""},
  1098. {PREV_CHAR, "PREVIOUS_CHARACTER", 0, "Previous Character", ""},
  1099. {NEXT_CHAR, "NEXT_CHARACTER", 0, "Next Character", ""},
  1100. {PREV_WORD, "PREVIOUS_WORD", 0, "Previous Word", ""},
  1101. {NEXT_WORD, "NEXT_WORD", 0, "Next Word", ""},
  1102. {PREV_LINE, "PREVIOUS_LINE", 0, "Previous Line", ""},
  1103. {NEXT_LINE, "NEXT_LINE", 0, "Next Line", ""},
  1104. {PREV_PAGE, "PREVIOUS_PAGE", 0, "Previous Page", ""},
  1105. {NEXT_PAGE, "NEXT_PAGE", 0, "Next Page", ""},
  1106. {0, NULL, 0, NULL, NULL}};
  1107. /* get cursor position in line by relative wrapped line and column positions */
  1108. static int text_get_cursor_rel(SpaceText *st, ARegion *ar, TextLine *linein, int rell, int relc)
  1109. {
  1110. int i, j, start, end, max, chop, curs, loop, endj, found, selc;
  1111. char ch;
  1112. max = wrap_width(st, ar);
  1113. selc = start = endj = curs = found = 0;
  1114. end = max;
  1115. chop = loop = 1;
  1116. for (i = 0, j = 0; loop; j += BLI_str_utf8_size(linein->line + j)) {
  1117. int chars;
  1118. /* Mimic replacement of tabs */
  1119. ch = linein->line[j];
  1120. if (ch == '\t') {
  1121. chars = st->tabnumber - i % st->tabnumber;
  1122. ch = ' ';
  1123. }
  1124. else chars = 1;
  1125. while (chars--) {
  1126. if (rell == 0 && i - start == relc) {
  1127. /* current position could be wrapped to next line */
  1128. /* this should be checked when end of current line would be reached */
  1129. selc = j;
  1130. found = 1;
  1131. }
  1132. else if (i - end == relc) {
  1133. curs = j;
  1134. }
  1135. if (i - start >= max) {
  1136. if (found) {
  1137. /* exact cursor position was found, check if it's */
  1138. /* still on needed line (hasn't been wrapped) */
  1139. if (selc > endj && !chop) selc = endj;
  1140. loop = 0;
  1141. break;
  1142. }
  1143. if (chop) endj = j;
  1144. start = end;
  1145. end += max;
  1146. chop = 1;
  1147. rell--;
  1148. if (rell == 0 && i - start >= relc) {
  1149. selc = curs;
  1150. loop = 0;
  1151. break;
  1152. }
  1153. }
  1154. else if (ch == '\0') {
  1155. if (!found) selc = linein->len;
  1156. loop = 0;
  1157. break;
  1158. }
  1159. else if (ch == ' ' || ch == '-') {
  1160. if (found) {
  1161. loop = 0;
  1162. break;
  1163. }
  1164. if (rell == 0 && i - start >= relc) {
  1165. selc = curs;
  1166. loop = 0;
  1167. break;
  1168. }
  1169. end = i + 1;
  1170. endj = j;
  1171. chop = 0;
  1172. }
  1173. i++;
  1174. }
  1175. }
  1176. return selc;
  1177. }
  1178. static int cursor_skip_find_line(SpaceText *st, ARegion *ar,
  1179. int lines, TextLine **linep, int *charp, int *rell, int *relc)
  1180. {
  1181. int offl, offc, visible_lines;
  1182. wrap_offset_in_line(st, ar, *linep, *charp, &offl, &offc);
  1183. *relc = text_get_char_pos(st, (*linep)->line, *charp) + offc;
  1184. *rell = lines;
  1185. /* handle current line */
  1186. if (lines > 0) {
  1187. visible_lines = text_get_visible_lines(st, ar, (*linep)->line);
  1188. if (*rell - visible_lines + offl >= 0) {
  1189. if (!(*linep)->next) {
  1190. if (offl < visible_lines - 1) {
  1191. *rell = visible_lines - 1;
  1192. return 1;
  1193. }
  1194. *charp = (*linep)->len;
  1195. return 0;
  1196. }
  1197. *rell -= visible_lines - offl;
  1198. *linep = (*linep)->next;
  1199. }
  1200. else {
  1201. *rell += offl;
  1202. return 1;
  1203. }
  1204. }
  1205. else {
  1206. if (*rell + offl <= 0) {
  1207. if (!(*linep)->prev) {
  1208. if (offl) {
  1209. *rell = 0;
  1210. return 1;
  1211. }
  1212. *charp = 0;
  1213. return 0;
  1214. }
  1215. *rell += offl;
  1216. *linep = (*linep)->prev;
  1217. }
  1218. else {
  1219. *rell += offl;
  1220. return 1;
  1221. }
  1222. }
  1223. /* skip lines and find destination line and offsets */
  1224. while (*linep) {
  1225. visible_lines = text_get_visible_lines(st, ar, (*linep)->line);
  1226. if (lines < 0) { /* moving top */
  1227. if (*rell + visible_lines >= 0) {
  1228. *rell += visible_lines;
  1229. break;
  1230. }
  1231. if (!(*linep)->prev) {
  1232. *rell = 0;
  1233. break;
  1234. }
  1235. *rell += visible_lines;
  1236. *linep = (*linep)->prev;
  1237. }
  1238. else { /* moving bottom */
  1239. if (*rell - visible_lines < 0) break;
  1240. if (!(*linep)->next) {
  1241. *rell = visible_lines - 1;
  1242. break;
  1243. }
  1244. *rell -= visible_lines;
  1245. *linep = (*linep)->next;
  1246. }
  1247. }
  1248. return 1;
  1249. }
  1250. static void txt_wrap_move_bol(SpaceText *st, ARegion *ar, short sel)
  1251. {
  1252. Text *text = st->text;
  1253. TextLine **linep;
  1254. int *charp;
  1255. int oldl, oldc, i, j, max, start, end, endj, chop, loop;
  1256. char ch;
  1257. text_update_character_width(st);
  1258. if (sel) linep = &text->sell, charp = &text->selc;
  1259. else linep = &text->curl, charp = &text->curc;
  1260. oldc = *charp;
  1261. oldl = txt_get_span(text->lines.first, *linep);
  1262. max = wrap_width(st, ar);
  1263. start = endj = 0;
  1264. end = max;
  1265. chop = loop = 1;
  1266. *charp = 0;
  1267. for (i = 0, j = 0; loop; j += BLI_str_utf8_size((*linep)->line + j)) {
  1268. int chars;
  1269. /* Mimic replacement of tabs */
  1270. ch = (*linep)->line[j];
  1271. if (ch == '\t') {
  1272. chars = st->tabnumber - i % st->tabnumber;
  1273. ch = ' ';
  1274. }
  1275. else chars = 1;
  1276. while (chars--) {
  1277. if (i - start >= max) {
  1278. *charp = endj;
  1279. if (j >= oldc) {
  1280. if (ch == '\0') *charp = txt_utf8_index_to_offset((*linep)->line, start);
  1281. loop = 0;
  1282. break;
  1283. }
  1284. if (chop) endj = j;
  1285. start = end;
  1286. end += max;
  1287. chop = 1;
  1288. }
  1289. else if (ch == ' ' || ch == '-' || ch == '\0') {
  1290. if (j >= oldc) {
  1291. *charp = txt_utf8_index_to_offset((*linep)->line, start);
  1292. loop = 0;
  1293. break;
  1294. }
  1295. end = i + 1;
  1296. endj = j + 1;
  1297. chop = 0;
  1298. }
  1299. i++;
  1300. }
  1301. }
  1302. if (!sel) txt_pop_sel(text);
  1303. txt_undo_add_toop(text, sel ? UNDO_STO : UNDO_CTO, oldl, oldc, oldl, *charp);
  1304. }
  1305. static void txt_wrap_move_eol(SpaceText *st, ARegion *ar, short sel)
  1306. {
  1307. Text *text = st->text;
  1308. TextLine **linep;
  1309. int *charp;
  1310. int oldl, oldc, i, j, max, start, end, endj, chop, loop;
  1311. char ch;
  1312. text_update_character_width(st);
  1313. if (sel) linep = &text->sell, charp = &text->selc;
  1314. else linep = &text->curl, charp = &text->curc;
  1315. oldc = *charp;
  1316. oldl = txt_get_span(text->lines.first, *linep);
  1317. max = wrap_width(st, ar);
  1318. start = endj = 0;
  1319. end = max;
  1320. chop = loop = 1;
  1321. *charp = 0;
  1322. for (i = 0, j = 0; loop; j += BLI_str_utf8_size((*linep)->line + j)) {
  1323. int chars;
  1324. /* Mimic replacement of tabs */
  1325. ch = (*linep)->line[j];
  1326. if (ch == '\t') {
  1327. chars = st->tabnumber - i % st->tabnumber;
  1328. ch = ' ';
  1329. }
  1330. else chars = 1;
  1331. while (chars--) {
  1332. if (i - start >= max) {
  1333. if (chop) endj = BLI_str_prev_char_utf8((*linep)->line + j) - (*linep)->line;
  1334. if (endj >= oldc) {
  1335. if (ch == '\0') *charp = (*linep)->len;
  1336. else *charp = endj;
  1337. loop = 0;
  1338. break;
  1339. }
  1340. start = end;
  1341. end += max;
  1342. chop = 1;
  1343. }
  1344. else if (ch == '\0') {
  1345. *charp = (*linep)->len;
  1346. loop = 0;
  1347. break;
  1348. }
  1349. else if (ch == ' ' || ch == '-') {
  1350. end = i + 1;
  1351. endj = j;
  1352. chop = 0;
  1353. }
  1354. i++;
  1355. }
  1356. }
  1357. if (!sel) txt_pop_sel(text);
  1358. txt_undo_add_toop(text, sel ? UNDO_STO : UNDO_CTO, oldl, oldc, oldl, *charp);
  1359. }
  1360. static void txt_wrap_move_up(SpaceText *st, ARegion *ar, short sel)
  1361. {
  1362. Text *text = st->text;
  1363. TextLine **linep;
  1364. int *charp;
  1365. int oldl, oldc, offl, offc, col, newl;
  1366. text_update_character_width(st);
  1367. if (sel) linep = &text->sell, charp = &text->selc;
  1368. else linep = &text->curl, charp = &text->curc;
  1369. /* store previous position */
  1370. oldc = *charp;
  1371. newl = oldl = txt_get_span(text->lines.first, *linep);
  1372. wrap_offset_in_line(st, ar, *linep, *charp, &offl, &offc);
  1373. col = text_get_char_pos(st, (*linep)->line, *charp) + offc;
  1374. if (offl) {
  1375. *charp = text_get_cursor_rel(st, ar, *linep, offl - 1, col);
  1376. newl = BLI_findindex(&text->lines, linep);
  1377. }
  1378. else {
  1379. if ((*linep)->prev) {
  1380. int visible_lines;
  1381. *linep = (*linep)->prev;
  1382. visible_lines = text_get_visible_lines(st, ar, (*linep)->line);
  1383. *charp = text_get_cursor_rel(st, ar, *linep, visible_lines - 1, col);
  1384. newl--;
  1385. }
  1386. else *charp = 0;
  1387. }
  1388. if (!sel) txt_pop_sel(text);
  1389. txt_undo_add_toop(text, sel ? UNDO_STO : UNDO_CTO, oldl, oldc, newl, *charp);
  1390. }
  1391. static void txt_wrap_move_down(SpaceText *st, ARegion *ar, short sel)
  1392. {
  1393. Text *text = st->text;
  1394. TextLine **linep;
  1395. int *charp;
  1396. int oldl, oldc, offl, offc, col, newl, visible_lines;
  1397. text_update_character_width(st);
  1398. if (sel) linep = &text->sell, charp = &text->selc;
  1399. else linep = &text->curl, charp = &text->curc;
  1400. /* store previous position */
  1401. oldc = *charp;
  1402. newl = oldl = txt_get_span(text->lines.first, *linep);
  1403. wrap_offset_in_line(st, ar, *linep, *charp, &offl, &offc);
  1404. col = text_get_char_pos(st, (*linep)->line, *charp) + offc;
  1405. visible_lines = text_get_visible_lines(st, ar, (*linep)->line);
  1406. if (offl < visible_lines - 1) {
  1407. *charp = text_get_cursor_rel(st, ar, *linep, offl + 1, col);
  1408. newl = BLI_findindex(&text->lines, linep);
  1409. }
  1410. else {
  1411. if ((*linep)->next) {
  1412. *linep = (*linep)->next;
  1413. *charp = text_get_cursor_rel(st, ar, *linep, 0, col);
  1414. newl++;
  1415. }
  1416. else *charp = (*linep)->len;
  1417. }
  1418. if (!sel) txt_pop_sel(text);
  1419. txt_undo_add_toop(text, sel ? UNDO_STO : UNDO_CTO, oldl, oldc, newl, *charp);
  1420. }
  1421. /* Moves the cursor vertically by the specified number of lines.
  1422. * If the destination line is shorter than the current cursor position, the
  1423. * cursor will be positioned at the end of this line.
  1424. *
  1425. * This is to replace screen_skip for PageUp/Down operations.
  1426. */
  1427. static void cursor_skip(SpaceText *st, ARegion *ar, Text *text, int lines, int sel)
  1428. {
  1429. TextLine **linep;
  1430. int oldl, oldc, *charp;
  1431. if (sel) linep = &text->sell, charp = &text->selc;
  1432. else linep = &text->curl, charp = &text->curc;
  1433. oldl = txt_get_span(text->lines.first, *linep);
  1434. oldc = *charp;
  1435. if (st && ar && st->wordwrap) {
  1436. int rell, relc;
  1437. /* find line and offsets inside it needed to set cursor position */
  1438. if (cursor_skip_find_line(st, ar, lines, linep, charp, &rell, &relc))
  1439. *charp = text_get_cursor_rel(st, ar, *linep, rell, relc);
  1440. }
  1441. else {
  1442. while (lines > 0 && (*linep)->next) {
  1443. *linep = (*linep)->next;
  1444. lines--;
  1445. }
  1446. while (lines < 0 && (*linep)->prev) {
  1447. *linep = (*linep)->prev;
  1448. lines++;
  1449. }
  1450. }
  1451. if (*charp > (*linep)->len) *charp = (*linep)->len;
  1452. if (!sel) txt_pop_sel(text);
  1453. txt_undo_add_toop(text, sel ? UNDO_STO : UNDO_CTO, oldl, oldc, txt_get_span(text->lines.first, *linep), *charp);
  1454. }
  1455. static int text_move_cursor(bContext *C, int type, int select)
  1456. {
  1457. SpaceText *st = CTX_wm_space_text(C);
  1458. Text *text = CTX_data_edit_text(C);
  1459. ARegion *ar = CTX_wm_region(C);
  1460. /* ensure we have the right region, it's optional */
  1461. if (ar && ar->regiontype != RGN_TYPE_WINDOW)
  1462. ar = NULL;
  1463. switch (type) {
  1464. case LINE_BEGIN:
  1465. if (st && st->wordwrap && ar) txt_wrap_move_bol(st, ar, select);
  1466. else txt_move_bol(text, select);
  1467. break;
  1468. case LINE_END:
  1469. if (st && st->wordwrap && ar) txt_wrap_move_eol(st, ar, select);
  1470. else txt_move_eol(text, select);
  1471. break;
  1472. case FILE_TOP:
  1473. txt_move_bof(text, select);
  1474. break;
  1475. case FILE_BOTTOM:
  1476. txt_move_eof(text, select);
  1477. break;
  1478. case PREV_WORD:
  1479. txt_jump_left(text, select);
  1480. break;
  1481. case NEXT_WORD:
  1482. txt_jump_right(text, select);
  1483. break;
  1484. case PREV_CHAR:
  1485. txt_move_left(text, select);
  1486. break;
  1487. case NEXT_CHAR:
  1488. txt_move_right(text, select);
  1489. break;
  1490. case PREV_LINE:
  1491. if (st && st->wordwrap && ar) txt_wrap_move_up(st, ar, select);
  1492. else txt_move_up(text, select);
  1493. break;
  1494. case NEXT_LINE:
  1495. if (st && st->wordwrap && ar) txt_wrap_move_down(st, ar, select);
  1496. else txt_move_down(text, select);
  1497. break;
  1498. case PREV_PAGE:
  1499. if (st) cursor_skip(st, ar, st->text, -st->viewlines, select);
  1500. else cursor_skip(NULL, NULL, text, -10, select);
  1501. break;
  1502. case NEXT_PAGE:
  1503. if (st) cursor_skip(st, ar, st->text, st->viewlines, select);
  1504. else cursor_skip(NULL, NULL, text, 10, select);
  1505. break;
  1506. }
  1507. text_update_cursor_moved(C);
  1508. WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, text);
  1509. return OPERATOR_FINISHED;
  1510. }
  1511. static int text_move_exec(bContext *C, wmOperator *op)
  1512. {
  1513. int type = RNA_enum_get(op->ptr, "type");
  1514. return text_move_cursor(C, type, 0);
  1515. }
  1516. void TEXT_OT_move(wmOperatorType *ot)
  1517. {
  1518. /* identifiers */
  1519. ot->name = "Move Cursor";
  1520. ot->idname = "TEXT_OT_move";
  1521. ot->description = "Move cursor to position type";
  1522. /* api callbacks */
  1523. ot->exec = text_move_exec;
  1524. ot->poll = text_edit_poll;
  1525. /* properties */
  1526. RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to");
  1527. }
  1528. /******************* move select operator ********************/
  1529. static int text_move_select_exec(bContext *C, wmOperator *op)
  1530. {
  1531. int type = RNA_enum_get(op->ptr, "type");
  1532. return text_move_cursor(C, type, 1);
  1533. }
  1534. void TEXT_OT_move_select(wmOperatorType *ot)
  1535. {
  1536. /* identifiers */
  1537. ot->name = "Move Select";
  1538. ot->idname = "TEXT_OT_move_select";
  1539. ot->description = "Make selection from current cursor position to new cursor position type";
  1540. /* api callbacks */
  1541. ot->exec = text_move_select_exec;
  1542. ot->poll = text_space_edit_poll;
  1543. /* properties */
  1544. RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to, to make a selection");
  1545. }
  1546. /******************* jump operator *********************/
  1547. static int text_jump_exec(bContext *C, wmOperator *op)
  1548. {
  1549. Text *text = CTX_data_edit_text(C);
  1550. int line = RNA_int_get(op->ptr, "line");
  1551. short nlines = txt_get_span(text->lines.first, text->lines.last) + 1;
  1552. if (line < 1)
  1553. txt_move_toline(text, 1, 0);
  1554. else if (line > nlines)
  1555. txt_move_toline(text, nlines - 1, 0);
  1556. else
  1557. txt_move_toline(text, line - 1, 0);
  1558. text_update_cursor_moved(C);
  1559. WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, text);
  1560. return OPERATOR_FINISHED;
  1561. }
  1562. static int text_jump_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
  1563. {
  1564. return WM_operator_props_dialog_popup(C, op, 200, 100);
  1565. }
  1566. void TEXT_OT_jump(wmOperatorType *ot)
  1567. {
  1568. /* identifiers */
  1569. ot->name = "Jump";
  1570. ot->idname = "TEXT_OT_jump";
  1571. ot->description = "Jump cursor to line";
  1572. /* api callbacks */
  1573. ot->invoke = text_jump_invoke;
  1574. ot->exec = text_jump_exec;
  1575. ot->poll = text_edit_poll;
  1576. /* properties */
  1577. RNA_def_int(ot->srna, "line", 1, 1, INT_MAX, "Line", "Line number to jump to", 1, 10000);
  1578. }
  1579. /******************* delete operator **********************/
  1580. static EnumPropertyItem delete_type_items[] = {
  1581. {DEL_NEXT_CHAR, "NEXT_CHARACTER", 0, "Next Character", ""},
  1582. {DEL_PREV_CHAR, "PREVIOUS_CHARACTER", 0, "Previous Character", ""},
  1583. {DEL_NEXT_WORD, "NEXT_WORD", 0, "Next Word", ""},
  1584. {DEL_PREV_WORD, "PREVIOUS_WORD", 0, "Previous Word", ""},
  1585. {0, NULL, 0, NULL, NULL}};
  1586. static int text_delete_exec(bContext *C, wmOperator *op)
  1587. {
  1588. Text *text = CTX_data_edit_text(C);
  1589. int type = RNA_enum_get(op->ptr, "type");
  1590. text_drawcache_tag_update(CTX_wm_space_text(C), 0);
  1591. if (type == DEL_PREV_WORD)
  1592. txt_backspace_word(text);
  1593. else if (type == DEL_PREV_CHAR)
  1594. txt_backspace_char(text);
  1595. else if (type == DEL_NEXT_WORD)
  1596. txt_delete_word(text);
  1597. else if (type == DEL_NEXT_CHAR)
  1598. txt_delete_char(text);
  1599. text_update_line_edited(text->curl);
  1600. text_update_cursor_moved(C);
  1601. WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
  1602. /* run the script while editing, evil but useful */
  1603. if (CTX_wm_space_text(C)->live_edit)
  1604. text_run_script(C, NULL);
  1605. return OPERATOR_FINISHED;
  1606. }
  1607. void TEXT_OT_delete(wmOperatorType *ot)
  1608. {
  1609. /* identifiers */
  1610. ot->name = "Delete";
  1611. ot->idname = "TEXT_OT_delete";
  1612. ot->description = "Delete text by cursor position";
  1613. /* api callbacks */
  1614. ot->exec = text_delete_exec;
  1615. ot->poll = text_edit_poll;
  1616. /* properties */
  1617. RNA_def_enum(ot->srna, "type", delete_type_items, DEL_NEXT_CHAR, "Type", "Which part of the text to delete");
  1618. }
  1619. /******************* toggle overwrite operator **********************/
  1620. static int text_toggle_overwrite_exec(bContext *C, wmOperator *UNUSED(op))
  1621. {
  1622. SpaceText *st = CTX_wm_space_text(C);
  1623. st->overwrite = !st->overwrite;
  1624. WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, st->text);
  1625. return OPERATOR_FINISHED;
  1626. }
  1627. void TEXT_OT_overwrite_toggle(wmOperatorType *ot)
  1628. {
  1629. /* identifiers */
  1630. ot->name = "Toggle Overwrite";
  1631. ot->idname = "TEXT_OT_overwrite_toggle";
  1632. ot->description = "Toggle overwrite while typing";
  1633. /* api callbacks */
  1634. ot->exec = text_toggle_overwrite_exec;
  1635. ot->poll = text_space_edit_poll;
  1636. }
  1637. /******************* scroll operator **********************/
  1638. /* Moves the view vertically by the specified number of lines */
  1639. static void txt_screen_skip(SpaceText *st, ARegion *ar, int lines)
  1640. {
  1641. int last;
  1642. st->top += lines;
  1643. last = text_get_total_lines(st, ar);
  1644. last = last - (st->viewlines / 2);
  1645. if (st->top > last) st->top = last;
  1646. if (st->top < 0) st->top = 0;
  1647. }
  1648. /* quick enum for tsc->zone (scroller handles) */
  1649. enum {
  1650. SCROLLHANDLE_BAR,
  1651. SCROLLHANDLE_MIN_OUTSIDE,
  1652. SCROLLHANDLE_MAX_OUTSIDE
  1653. };
  1654. typedef struct TextScroll {
  1655. short old[2];
  1656. short delta[2];
  1657. int first;
  1658. int scrollbar;
  1659. int zone;
  1660. } TextScroll;
  1661. static int text_scroll_poll(bContext *C)
  1662. {
  1663. /* it should be possible to still scroll linked texts to read them, even if they can't be edited... */
  1664. return CTX_data_edit_text(C) != NULL;
  1665. }
  1666. static int text_scroll_exec(bContext *C, wmOperator *op)
  1667. {
  1668. SpaceText *st = CTX_wm_space_text(C);
  1669. ARegion *ar = CTX_wm_region(C);
  1670. int lines = RNA_int_get(op->ptr, "lines");
  1671. if (lines == 0)
  1672. return OPERATOR_CANCELLED;
  1673. txt_screen_skip(st, ar, lines * U.wheellinescroll);
  1674. ED_area_tag_redraw(CTX_wm_area(C));
  1675. return OPERATOR_FINISHED;
  1676. }
  1677. static void text_scroll_apply(bContext *C, wmOperator *op, wmEvent *event)
  1678. {
  1679. SpaceText *st = CTX_wm_space_text(C);
  1680. ARegion *ar = CTX_wm_region(C);
  1681. TextScroll *tsc = op->customdata;
  1682. int mval[2] = {event->x, event->y};
  1683. short txtdelta[2] = {0, 0};
  1684. text_update_character_width(st);
  1685. if (tsc->first) {
  1686. tsc->old[0] = mval[0];
  1687. tsc->old[1] = mval[1];
  1688. tsc->first = 0;
  1689. }
  1690. tsc->delta[0] += mval[0] - tsc->old[0];
  1691. tsc->delta[1] += mval[1] - tsc->old[1];
  1692. if (!tsc->scrollbar) {
  1693. txtdelta[0] = -tsc->delta[0] / st->cwidth;
  1694. txtdelta[1] = tsc->delta[1] / st->lheight;
  1695. tsc->delta[0] %= st->cwidth;
  1696. tsc->delta[1] %= st->lheight;
  1697. }
  1698. else {
  1699. txtdelta[1] = -tsc->delta[1] * st->pix_per_line;
  1700. tsc->delta[1] += txtdelta[1] / st->pix_per_line;
  1701. }
  1702. if (txtdelta[0] || txtdelta[1]) {
  1703. txt_screen_skip(st, ar, txtdelta[1]);
  1704. if (st->wordwrap) {
  1705. st->left = 0;
  1706. }
  1707. else {
  1708. st->left += txtdelta[0];
  1709. if (st->left < 0) st->left = 0;
  1710. }
  1711. ED_area_tag_redraw(CTX_wm_area(C));
  1712. }
  1713. tsc->old[0] = mval[0];
  1714. tsc->old[1] = mval[1];
  1715. }
  1716. static void scroll_exit(bContext *C, wmOperator *op)
  1717. {
  1718. SpaceText *st = CTX_wm_space_text(C);
  1719. st->flags &= ~ST_SCROLL_SELECT;
  1720. MEM_freeN(op->customdata);
  1721. }
  1722. static int text_scroll_modal(bContext *C, wmOperator *op, wmEvent *event)
  1723. {
  1724. TextScroll *tsc = op->customdata;
  1725. SpaceText *st = CTX_wm_space_text(C);
  1726. ARegion *ar = CTX_wm_region(C);
  1727. switch (event->type) {
  1728. case MOUSEMOVE:
  1729. if (tsc->zone == SCROLLHANDLE_BAR)
  1730. text_scroll_apply(C, op, event);
  1731. break;
  1732. case LEFTMOUSE:
  1733. case RIGHTMOUSE:
  1734. case MIDDLEMOUSE:
  1735. if (ELEM(tsc->zone, SCROLLHANDLE_MIN_OUTSIDE, SCROLLHANDLE_MAX_OUTSIDE)) {
  1736. int last;
  1737. st->top += st->viewlines * (tsc->zone == SCROLLHANDLE_MIN_OUTSIDE ? 1 : -1);
  1738. last = text_get_total_lines(st, ar);
  1739. last = last - (st->viewlines / 2);
  1740. CLAMP(st->top, 0, last);
  1741. ED_area_tag_redraw(CTX_wm_area(C));
  1742. }
  1743. scroll_exit(C, op);
  1744. return OPERATOR_FINISHED;
  1745. }
  1746. return OPERATOR_RUNNING_MODAL;
  1747. }
  1748. static int text_scroll_cancel(bContext *C, wmOperator *op)
  1749. {
  1750. scroll_exit(C, op);
  1751. return OPERATOR_CANCELLED;
  1752. }
  1753. static int text_scroll_invoke(bContext *C, wmOperator *op, wmEvent *event)
  1754. {
  1755. SpaceText *st = CTX_wm_space_text(C);
  1756. TextScroll *tsc;
  1757. if (RNA_struct_property_is_set(op->ptr, "lines"))
  1758. return text_scroll_exec(C, op);
  1759. tsc = MEM_callocN(sizeof(TextScroll), "TextScroll");
  1760. tsc->first = 1;
  1761. tsc->zone = SCROLLHANDLE_BAR;
  1762. op->customdata = tsc;
  1763. st->flags |= ST_SCROLL_SELECT;
  1764. if (event->type == MOUSEPAN) {
  1765. text_update_character_width(st);
  1766. tsc->old[0] = event->x;
  1767. tsc->old[1] = event->y;
  1768. /* Sensitivity of scroll set to 4pix per line/char */
  1769. tsc->delta[0] = (event->x - event->prevx) * st->cwidth / 4;
  1770. tsc->delta[1] = (event->y - event->prevy) * st->lheight / 4;
  1771. tsc->first = 0;
  1772. tsc->scrollbar = 0;
  1773. text_scroll_apply(C, op, event);
  1774. scroll_exit(C, op);
  1775. return OPERATOR_FINISHED;
  1776. }
  1777. WM_event_add_modal_handler(C, op);
  1778. return OPERATOR_RUNNING_MODAL;
  1779. }
  1780. void TEXT_OT_scroll(wmOperatorType *ot)
  1781. {
  1782. /* identifiers */
  1783. ot->name = "Scroll";
  1784. /* don't really see the difference between this and
  1785. * scroll_bar. Both do basically the same thing (aside
  1786. * from keymaps).*/
  1787. ot->idname = "TEXT_OT_scroll";
  1788. ot->description = "Scroll text screen";
  1789. /* api callbacks */
  1790. ot->exec = text_scroll_exec;
  1791. ot->invoke = text_scroll_invoke;
  1792. ot->modal = text_scroll_modal;
  1793. ot->cancel = text_scroll_cancel;
  1794. ot->poll = text_scroll_poll;
  1795. /* flags */
  1796. ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER;
  1797. /* properties */
  1798. RNA_def_int(ot->srna, "lines", 1, INT_MIN, INT_MAX, "Lines", "Number of lines to scroll", -100, 100);
  1799. }
  1800. /******************** scroll bar operator *******************/
  1801. static int text_region_scroll_poll(bContext *C)
  1802. {
  1803. /* same as text_region_edit_poll except it works on libdata too */
  1804. SpaceText *st = CTX_wm_space_text(C);
  1805. Text *text = CTX_data_edit_text(C);
  1806. ARegion *ar = CTX_wm_region(C);
  1807. if (!st || !text)
  1808. return 0;
  1809. if (!ar || ar->regiontype != RGN_TYPE_WINDOW)
  1810. return 0;
  1811. return 1;
  1812. }
  1813. static int text_scroll_bar_invoke(bContext *C, wmOperator *op, wmEvent *event)
  1814. {
  1815. SpaceText *st = CTX_wm_space_text(C);
  1816. ARegion *ar = CTX_wm_region(C);
  1817. TextScroll *tsc;
  1818. const int *mval = event->mval;
  1819. int zone = -1;
  1820. if (RNA_struct_property_is_set(op->ptr, "lines"))
  1821. return text_scroll_exec(C, op);
  1822. /* verify we are in the right zone */
  1823. if (mval[0] > st->txtbar.xmin && mval[0] < st->txtbar.xmax) {
  1824. if (mval[1] >= st->txtbar.ymin && mval[1] <= st->txtbar.ymax) {
  1825. /* mouse inside scroll handle */
  1826. zone = SCROLLHANDLE_BAR;
  1827. }
  1828. else if (mval[1] > TXT_SCROLL_SPACE && mval[1] < ar->winy - TXT_SCROLL_SPACE) {
  1829. if (mval[1] < st->txtbar.ymin) zone = SCROLLHANDLE_MIN_OUTSIDE;
  1830. else zone = SCROLLHANDLE_MAX_OUTSIDE;
  1831. }
  1832. }
  1833. if (zone == -1) {
  1834. /* we are outside slider - nothing to do */
  1835. return OPERATOR_PASS_THROUGH;
  1836. }
  1837. tsc = MEM_callocN(sizeof(TextScroll), "TextScroll");
  1838. tsc->first = 1;
  1839. tsc->scrollbar = 1;
  1840. tsc->zone = zone;
  1841. op->customdata = tsc;
  1842. st->flags |= ST_SCROLL_SELECT;
  1843. /* jump scroll, works in v2d but needs to be added here too :S */
  1844. if (event->type == MIDDLEMOUSE) {
  1845. tsc->old[0] = ar->winrct.xmin + (st->txtbar.xmax + st->txtbar.xmin) / 2;
  1846. tsc->old[1] = ar->winrct.ymin + (st->txtbar.ymax + st->txtbar.ymin) / 2;
  1847. tsc->delta[0] = 0;
  1848. tsc->delta[1] = 0;
  1849. tsc->first = 0;
  1850. tsc->zone = SCROLLHANDLE_BAR;
  1851. text_scroll_apply(C, op, event);
  1852. }
  1853. WM_event_add_modal_handler(C, op);
  1854. return OPERATOR_RUNNING_MODAL;
  1855. }
  1856. void TEXT_OT_scroll_bar(wmOperatorType *ot)
  1857. {
  1858. /* identifiers */
  1859. ot->name = "Scrollbar";
  1860. /* don't really see the difference between this and
  1861. * scroll. Both do basically the same thing (aside
  1862. * from keymaps).*/
  1863. ot->idname = "TEXT_OT_scroll_bar";
  1864. ot->description = "Scroll text screen";
  1865. /* api callbacks */
  1866. ot->invoke = text_scroll_bar_invoke;
  1867. ot->modal = text_scroll_modal;
  1868. ot->cancel = text_scroll_cancel;
  1869. ot->poll = text_region_scroll_poll;
  1870. /* flags */
  1871. ot->flag = OPTYPE_BLOCKING;
  1872. /* properties */
  1873. RNA_def_int(ot->srna, "lines", 1, INT_MIN, INT_MAX, "Lines", "Number of lines to scroll", -100, 100);
  1874. }
  1875. /******************* set selection operator **********************/
  1876. typedef struct SetSelection {
  1877. int selecting;
  1878. int selc, sell;
  1879. short old[2];
  1880. } SetSelection;
  1881. static int flatten_len(SpaceText *st, const char *str)
  1882. {
  1883. int i, total = 0;
  1884. for (i = 0; str[i]; i += BLI_str_utf8_size(str + i)) {
  1885. if (str[i] == '\t') {
  1886. total += st->tabnumber - total % st->tabnumber;
  1887. }
  1888. else total++;
  1889. }
  1890. return total;
  1891. }
  1892. static int flatten_index_to_offset(SpaceText *st, const char *str, int index)
  1893. {
  1894. int i, j;
  1895. for (i = 0, j = 0; i < index; j += BLI_str_utf8_size(str + j))
  1896. if (str[j] == '\t')
  1897. i += st->tabnumber - i % st->tabnumber;
  1898. else
  1899. i++;
  1900. return j;
  1901. }
  1902. static TextLine *get_first_visible_line(SpaceText *st, ARegion *ar, int *y)
  1903. {
  1904. TextLine *linep = st->text->lines.first;
  1905. int i;
  1906. for (i = st->top; i > 0 && linep; ) {
  1907. int lines = text_get_visible_lines(st, ar, linep->line);
  1908. if (i - lines < 0) {
  1909. *y += i;
  1910. break;
  1911. }
  1912. else {
  1913. linep = linep->next;
  1914. i -= lines;
  1915. }
  1916. }
  1917. return linep;
  1918. }
  1919. static void text_cursor_set_to_pos_wrapped(SpaceText *st, ARegion *ar, int x, int y, int sel)
  1920. {
  1921. Text *text = st->text;
  1922. int max = wrap_width(st, ar); /* view */
  1923. int charp = -1; /* mem */
  1924. int loop = 1, found = 0; /* flags */
  1925. char ch;
  1926. /* Point to first visible line */
  1927. TextLine *linep = get_first_visible_line(st, ar, &y);
  1928. while (loop && linep) {
  1929. int i = 0, start = 0, end = max; /* view */
  1930. int j = 0, curs = 0, endj = 0; /* mem */
  1931. int chop = 1; /* flags */
  1932. for (; loop; j += BLI_str_utf8_size(linep->line + j)) {
  1933. int chars;
  1934. /* Mimic replacement of tabs */
  1935. ch = linep->line[j];
  1936. if (ch == '\t') {
  1937. chars = st->tabnumber - i % st->tabnumber;
  1938. ch = ' ';
  1939. }
  1940. else chars = 1;
  1941. while (chars--) {
  1942. /* Gone too far, go back to last wrap point */
  1943. if (y < 0) {
  1944. charp = endj;
  1945. loop = 0;
  1946. break;
  1947. /* Exactly at the cursor */
  1948. }
  1949. else if (y == 0 && i - start == x) {
  1950. /* current position could be wrapped to next line */
  1951. /* this should be checked when end of current line would be reached */
  1952. charp = curs = j;
  1953. found = 1;
  1954. /* Prepare curs for next wrap */
  1955. }
  1956. else if (i - end == x) {
  1957. curs = j;
  1958. }
  1959. if (i - start >= max) {
  1960. if (found) {
  1961. /* exact cursor position was found, check if it's */
  1962. /* still on needed line (hasn't been wrapped) */
  1963. if (charp > endj && !chop && ch != '\0') charp = endj;
  1964. loop = 0;
  1965. break;
  1966. }
  1967. if (chop) endj = j;
  1968. start = end;
  1969. end += max;
  1970. if (j < linep->len)
  1971. y--;
  1972. chop = 1;
  1973. if (y == 0 && i - start >= x) {
  1974. charp = curs;
  1975. loop = 0;
  1976. break;
  1977. }
  1978. }
  1979. else if (ch == ' ' || ch == '-' || ch == '\0') {
  1980. if (found) {
  1981. loop = 0;
  1982. break;
  1983. }
  1984. if (y == 0 && i - start >= x) {
  1985. charp = curs;
  1986. loop = 0;
  1987. break;
  1988. }
  1989. end = i + 1;
  1990. endj = j;
  1991. chop = 0;
  1992. }
  1993. i++;
  1994. }
  1995. if (ch == '\0') break;
  1996. }
  1997. if (!loop || found) break;
  1998. if (!linep->next) {
  1999. charp = linep->len;
  2000. break;
  2001. }
  2002. /* On correct line but didn't meet cursor, must be at end */
  2003. if (y == 0) {
  2004. charp = linep->len;
  2005. break;
  2006. }
  2007. linep = linep->next;
  2008. y--;
  2009. }
  2010. if (linep && charp != -1) {
  2011. if (sel) { text->sell = linep; text->selc = charp; }
  2012. else { text->curl = linep; text->curc = charp; }
  2013. }
  2014. }
  2015. static void text_cursor_set_to_pos(SpaceText *st, ARegion *ar, int x, int y, int sel)
  2016. {
  2017. Text *text = st->text;
  2018. text_update_character_width(st);
  2019. y = (ar->winy - 2 - y) / st->lheight;
  2020. if (st->showlinenrs) x -= TXT_OFFSET + TEXTXLOC;
  2021. else x -= TXT_OFFSET;
  2022. if (x < 0) x = 0;
  2023. x = text_pixel_x_to_index(st, x) + st->left;
  2024. if (st->wordwrap) {
  2025. text_cursor_set_to_pos_wrapped(st, ar, x, y, sel);
  2026. }
  2027. else {
  2028. TextLine **linep;
  2029. int *charp;
  2030. int w;
  2031. if (sel) { linep = &text->sell; charp = &text->selc; }
  2032. else { linep = &text->curl; charp = &text->curc; }
  2033. y -= txt_get_span(text->lines.first, *linep) - st->top;
  2034. if (y > 0) {
  2035. while (y-- != 0) if ((*linep)->next) *linep = (*linep)->next;
  2036. }
  2037. else if (y < 0) {
  2038. while (y++ != 0) if ((*linep)->prev) *linep = (*linep)->prev;
  2039. }
  2040. w = flatten_len(st, (*linep)->line);
  2041. if (x < w) *charp = flatten_index_to_offset(st, (*linep)->line, x);
  2042. else *charp = (*linep)->len;
  2043. }
  2044. if (!sel) txt_pop_sel(text);
  2045. }
  2046. static void text_cursor_set_apply(bContext *C, wmOperator *op, wmEvent *event)
  2047. {
  2048. SpaceText *st = CTX_wm_space_text(C);
  2049. ARegion *ar = CTX_wm_region(C);
  2050. SetSelection *ssel = op->customdata;
  2051. if (event->mval[1] < 0 || event->mval[1] > ar->winy) {
  2052. int d = (ssel->old[1] - event->mval[1]) * st->pix_per_line;
  2053. if (d) txt_screen_skip(st, ar, d);
  2054. text_cursor_set_to_pos(st, ar, event->mval[0], event->mval[1] < 0 ? 0 : ar->winy, 1);
  2055. text_update_cursor_moved(C);
  2056. WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, st->text);
  2057. }
  2058. else if (!st->wordwrap && (event->mval[0] < 0 || event->mval[0] > ar->winx)) {
  2059. if (event->mval[0] > ar->winx) st->left++;
  2060. else if (event->mval[0] < 0 && st->left > 0) st->left--;
  2061. text_cursor_set_to_pos(st, ar, event->mval[0], event->mval[1], 1);
  2062. text_update_cursor_moved(C);
  2063. WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, st->text);
  2064. // XXX PIL_sleep_ms(10);
  2065. }
  2066. else {
  2067. text_cursor_set_to_pos(st, ar, event->mval[0], event->mval[1], 1);
  2068. text_update_cursor_moved(C);
  2069. WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, st->text);
  2070. ssel->old[0] = event->mval[0];
  2071. ssel->old[1] = event->mval[1];
  2072. }
  2073. }
  2074. static void text_cursor_set_exit(bContext *C, wmOperator *op)
  2075. {
  2076. SpaceText *st = CTX_wm_space_text(C);
  2077. Text *text = st->text;
  2078. SetSelection *ssel = op->customdata;
  2079. int linep2, charp2;
  2080. char *buffer;
  2081. if (txt_has_sel(text)) {
  2082. buffer = txt_sel_to_buf(text);
  2083. WM_clipboard_text_set(buffer, 1);
  2084. MEM_freeN(buffer);
  2085. }
  2086. linep2 = txt_get_span(st->text->lines.first, st->text->sell);
  2087. charp2 = st->text->selc;
  2088. if (ssel->sell != linep2 || ssel->selc != charp2)
  2089. txt_undo_add_toop(st->text, UNDO_STO, ssel->sell, ssel->selc, linep2, charp2);
  2090. text_update_cursor_moved(C);
  2091. WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, st->text);
  2092. MEM_freeN(ssel);
  2093. }
  2094. static int text_set_selection_invoke(bContext *C, wmOperator *op, wmEvent *event)
  2095. {
  2096. SpaceText *st = CTX_wm_space_text(C);
  2097. SetSelection *ssel;
  2098. if (event->mval[0] >= st->txtbar.xmin)
  2099. return OPERATOR_PASS_THROUGH;
  2100. op->customdata = MEM_callocN(sizeof(SetSelection), "SetCursor");
  2101. ssel = op->customdata;
  2102. ssel->selecting = RNA_boolean_get(op->ptr, "select");
  2103. ssel->old[0] = event->mval[0];
  2104. ssel->old[1] = event->mval[1];
  2105. ssel->sell = txt_get_span(st->text->lines.first, st->text->sell);
  2106. ssel->selc = st->text->selc;
  2107. WM_event_add_modal_handler(C, op);
  2108. text_cursor_set_apply(C, op, event);
  2109. return OPERATOR_RUNNING_MODAL;
  2110. }
  2111. static int text_set_selection_modal(bContext *C, wmOperator *op, wmEvent *event)
  2112. {
  2113. switch (event->type) {
  2114. case LEFTMOUSE:
  2115. case MIDDLEMOUSE:
  2116. case RIGHTMOUSE:
  2117. text_cursor_set_exit(C, op);
  2118. return OPERATOR_FINISHED;
  2119. case MOUSEMOVE:
  2120. text_cursor_set_apply(C, op, event);
  2121. break;
  2122. }
  2123. return OPERATOR_RUNNING_MODAL;
  2124. }
  2125. static int text_set_selection_cancel(bContext *C, wmOperator *op)
  2126. {
  2127. text_cursor_set_exit(C, op);
  2128. return OPERATOR_FINISHED;
  2129. }
  2130. void TEXT_OT_selection_set(wmOperatorType *ot)
  2131. {
  2132. /* identifiers */
  2133. ot->name = "Set Selection";
  2134. ot->idname = "TEXT_OT_selection_set";
  2135. ot->description = "Set cursor selection";
  2136. /* api callbacks */
  2137. ot->invoke = text_set_selection_invoke;
  2138. ot->modal = text_set_selection_modal;
  2139. ot->cancel = text_set_selection_cancel;
  2140. ot->poll = text_region_edit_poll;
  2141. /* properties */
  2142. RNA_def_boolean(ot->srna, "select", 0, "Select", "Set selection end rather than cursor");
  2143. }
  2144. /******************* set cursor operator **********************/
  2145. static int text_cursor_set_exec(bContext *C, wmOperator *op)
  2146. {
  2147. SpaceText *st = CTX_wm_space_text(C);
  2148. Text *text = st->text;
  2149. ARegion *ar = CTX_wm_region(C);
  2150. int x = RNA_int_get(op->ptr, "x");
  2151. int y = RNA_int_get(op->ptr, "y");
  2152. int oldl, oldc;
  2153. oldl = txt_get_span(text->lines.first, text->curl);
  2154. oldc = text->curc;
  2155. text_cursor_set_to_pos(st, ar, x, y, 0);
  2156. txt_undo_add_toop(text, UNDO_CTO, oldl, oldc, txt_get_span(text->lines.first, text->curl), text->curc);
  2157. text_update_cursor_moved(C);
  2158. WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, st->text);
  2159. return OPERATOR_PASS_THROUGH;
  2160. }
  2161. static int text_cursor_set_invoke(bContext *C, wmOperator *op, wmEvent *event)
  2162. {
  2163. SpaceText *st = CTX_wm_space_text(C);
  2164. if (event->mval[0] >= st->txtbar.xmin)
  2165. return OPERATOR_PASS_THROUGH;
  2166. RNA_int_set(op->ptr, "x", event->mval[0]);
  2167. RNA_int_set(op->ptr, "y", event->mval[1]);
  2168. return text_cursor_set_exec(C, op);
  2169. }
  2170. void TEXT_OT_cursor_set(wmOperatorType *ot)
  2171. {
  2172. /* identifiers */
  2173. ot->name = "Set Cursor";
  2174. ot->idname = "TEXT_OT_cursor_set";
  2175. ot->description = "Set cursor position";
  2176. /* api callbacks */
  2177. ot->invoke = text_cursor_set_invoke;
  2178. ot->exec = text_cursor_set_exec;
  2179. ot->poll = text_region_edit_poll;
  2180. /* properties */
  2181. RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
  2182. RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
  2183. }
  2184. /******************* line number operator **********************/
  2185. static int text_line_number_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
  2186. {
  2187. SpaceText *st = CTX_wm_space_text(C);
  2188. Text *text = CTX_data_edit_text(C);
  2189. ARegion *ar = CTX_wm_region(C);
  2190. const int *mval = event->mval;
  2191. double time;
  2192. static int jump_to = 0;
  2193. static double last_jump = 0;
  2194. text_update_character_width(st);
  2195. if (!st->showlinenrs)
  2196. return OPERATOR_PASS_THROUGH;
  2197. if (!(mval[0] > 2 && mval[0] < (TXT_OFFSET + TEXTXLOC) && mval[1] > 2 && mval[1] < ar->winy - 2))
  2198. return OPERATOR_PASS_THROUGH;
  2199. if (!(event->ascii >= '0' && event->ascii <= '9'))
  2200. return OPERATOR_PASS_THROUGH;
  2201. time = PIL_check_seconds_timer();
  2202. if (last_jump < time - 1)
  2203. jump_to = 0;
  2204. jump_to *= 10;
  2205. jump_to += (int)(event->ascii - '0');
  2206. txt_move_toline(text, jump_to - 1, 0);
  2207. last_jump = time;
  2208. text_update_cursor_moved(C);
  2209. WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, text);
  2210. return OPERATOR_FINISHED;
  2211. }
  2212. void TEXT_OT_line_number(wmOperatorType *ot)
  2213. {
  2214. /* identifiers */
  2215. ot->name = "Line Number";
  2216. ot->idname = "TEXT_OT_line_number";
  2217. ot->description = "The current line number";
  2218. /* api callbacks */
  2219. ot->invoke = text_line_number_invoke;
  2220. ot->poll = text_region_edit_poll;
  2221. }
  2222. /******************* insert operator **********************/
  2223. static int text_insert_exec(bContext *C, wmOperator *op)
  2224. {
  2225. SpaceText *st = CTX_wm_space_text(C);
  2226. Text *text = CTX_data_edit_text(C);
  2227. char *str;
  2228. int done = 0;
  2229. size_t i = 0;
  2230. unsigned int code;
  2231. text_drawcache_tag_update(st, 0);
  2232. str = RNA_string_get_alloc(op->ptr, "text", NULL, 0);
  2233. if (st && st->overwrite) {
  2234. while (str[i]) {
  2235. code = BLI_str_utf8_as_unicode_step(str, &i);
  2236. done |= txt_replace_char(text, code);
  2237. }
  2238. }
  2239. else {
  2240. while (str[i]) {
  2241. code = BLI_str_utf8_as_unicode_step(str, &i);
  2242. done |= txt_add_char(text, code);
  2243. }
  2244. }
  2245. MEM_freeN(str);
  2246. if (!done)
  2247. return OPERATOR_CANCELLED;
  2248. text_update_line_edited(text->curl);
  2249. text_update_cursor_moved(C);
  2250. WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
  2251. return OPERATOR_FINISHED;
  2252. }
  2253. static int text_insert_invoke(bContext *C, wmOperator *op, wmEvent *event)
  2254. {
  2255. int ret;
  2256. // if (!RNA_struct_property_is_set(op->ptr, "text")) { /* always set from keymap XXX */
  2257. if (!RNA_string_length(op->ptr, "text")) {
  2258. /* if alt/ctrl/super are pressed pass through */
  2259. if (event->ctrl || event->oskey) {
  2260. return OPERATOR_PASS_THROUGH;
  2261. }
  2262. else {
  2263. char str[BLI_UTF8_MAX + 1];
  2264. size_t len;
  2265. if (event->utf8_buf[0]) {
  2266. len = BLI_str_utf8_size(event->utf8_buf);
  2267. memcpy(str, event->utf8_buf, len);
  2268. }
  2269. else {
  2270. /* in theory, ghost can set value to extended ascii here */
  2271. len = BLI_str_utf8_from_unicode(event->ascii, str);
  2272. }
  2273. str[len] = '\0';
  2274. RNA_string_set(op->ptr, "text", str);
  2275. }
  2276. }
  2277. ret = text_insert_exec(C, op);
  2278. /* run the script while editing, evil but useful */
  2279. if (ret == OPERATOR_FINISHED && CTX_wm_space_text(C)->live_edit)
  2280. text_run_script(C, NULL);
  2281. return ret;
  2282. }
  2283. void TEXT_OT_insert(wmOperatorType *ot)
  2284. {
  2285. PropertyRNA *prop;
  2286. /* identifiers */
  2287. ot->name = "Insert";
  2288. ot->idname = "TEXT_OT_insert";
  2289. ot->description = "Insert text at cursor position";
  2290. /* api callbacks */
  2291. ot->exec = text_insert_exec;
  2292. ot->invoke = text_insert_invoke;
  2293. ot->poll = text_edit_poll;
  2294. /* properties */
  2295. prop = RNA_def_string(ot->srna, "text", "", 0, "Text", "Text to insert at the cursor position");
  2296. RNA_def_property_flag(prop, PROP_SKIP_SAVE);
  2297. }
  2298. /******************* find operator *********************/
  2299. /* mode */
  2300. #define TEXT_FIND 0
  2301. #define TEXT_REPLACE 1
  2302. #define TEXT_MARK_ALL 2
  2303. static int text_find_and_replace(bContext *C, wmOperator *op, short mode)
  2304. {
  2305. Main *bmain = CTX_data_main(C);
  2306. SpaceText *st = CTX_wm_space_text(C);
  2307. Text *start = NULL, *text = st->text;
  2308. int flags, first = 1;
  2309. int found = 0;
  2310. char *tmp;
  2311. if (!st->findstr[0] || (mode == TEXT_REPLACE && !st->replacestr[0]))
  2312. return OPERATOR_CANCELLED;
  2313. flags = st->flags;
  2314. if (flags & ST_FIND_ALL)
  2315. flags ^= ST_FIND_WRAP;
  2316. do {
  2317. int proceed = 0;
  2318. if (first) {
  2319. if (text->markers.first)
  2320. WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
  2321. txt_clear_markers(text, TMARK_GRP_FINDALL, 0);
  2322. }
  2323. first = 0;
  2324. /* Replace current */
  2325. if (mode != TEXT_FIND && txt_has_sel(text)) {
  2326. tmp = txt_sel_to_buf(text);
  2327. if (flags & ST_MATCH_CASE) proceed = strcmp(st->findstr, tmp) == 0;
  2328. else proceed = BLI_strcasecmp(st->findstr, tmp) == 0;
  2329. if (proceed) {
  2330. if (mode == TEXT_REPLACE) {
  2331. txt_insert_buf(text, st->replacestr);
  2332. if (text->curl && text->curl->format) {
  2333. MEM_freeN(text->curl->format);
  2334. text->curl->format = NULL;
  2335. }
  2336. text_update_cursor_moved(C);
  2337. WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
  2338. text_drawcache_tag_update(CTX_wm_space_text(C), 1);
  2339. }
  2340. else if (mode == TEXT_MARK_ALL) {
  2341. unsigned char color[4];
  2342. UI_GetThemeColor4ubv(TH_SHADE2, color);
  2343. if (txt_find_marker(text, text->curl, text->selc, TMARK_GRP_FINDALL, 0)) {
  2344. if (tmp) MEM_freeN(tmp), tmp = NULL;
  2345. break;
  2346. }
  2347. txt_add_marker(text, text->curl, text->curc, text->selc, color, TMARK_GRP_FINDALL, TMARK_EDITALL);
  2348. text_update_cursor_moved(C);
  2349. WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
  2350. }
  2351. }
  2352. MEM_freeN(tmp);
  2353. tmp = NULL;
  2354. }
  2355. /* Find next */
  2356. if (txt_find_string(text, st->findstr, flags & ST_FIND_WRAP, flags & ST_MATCH_CASE)) {
  2357. text_update_cursor_moved(C);
  2358. WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, text);
  2359. }
  2360. else if (flags & ST_FIND_ALL) {
  2361. if (text == start) break;
  2362. if (!start) start = text;
  2363. if (text->id.next)
  2364. text = st->text = text->id.next;
  2365. else
  2366. text = st->text = bmain->text.first;
  2367. txt_move_toline(text, 0, 0);
  2368. text_update_cursor_moved(C);
  2369. WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, text);
  2370. first = 1;
  2371. }
  2372. else {
  2373. if (!found && !proceed) BKE_reportf(op->reports, RPT_ERROR, "Text not found: %s", st->findstr);
  2374. break;
  2375. }
  2376. found = 1;
  2377. } while (mode == TEXT_MARK_ALL);
  2378. return OPERATOR_FINISHED;
  2379. }
  2380. static int text_find_exec(bContext *C, wmOperator *op)
  2381. {
  2382. return text_find_and_replace(C, op, TEXT_FIND);
  2383. }
  2384. void TEXT_OT_find(wmOperatorType *ot)
  2385. {
  2386. /* identifiers */
  2387. ot->name = "Find";
  2388. ot->idname = "TEXT_OT_find";
  2389. ot->description = "Find specified text";
  2390. /* api callbacks */
  2391. ot->exec = text_find_exec;
  2392. ot->poll = text_space_edit_poll;
  2393. }
  2394. /******************* replace operator *********************/
  2395. static int text_replace_exec(bContext *C, wmOperator *op)
  2396. {
  2397. return text_find_and_replace(C, op, TEXT_REPLACE);
  2398. }
  2399. void TEXT_OT_replace(wmOperatorType *ot)
  2400. {
  2401. /* identifiers */
  2402. ot->name = "Replace";
  2403. ot->idname = "TEXT_OT_replace";
  2404. ot->description = "Replace text with the specified text";
  2405. /* api callbacks */
  2406. ot->exec = text_replace_exec;
  2407. ot->poll = text_space_edit_poll;
  2408. }
  2409. /******************* mark all operator *********************/
  2410. static int text_mark_all_exec(bContext *C, wmOperator *op)
  2411. {
  2412. return text_find_and_replace(C, op, TEXT_MARK_ALL);
  2413. }
  2414. void TEXT_OT_mark_all(wmOperatorType *ot)
  2415. {
  2416. /* identifiers */
  2417. ot->name = "Mark All";
  2418. ot->idname = "TEXT_OT_mark_all";
  2419. ot->description = "Mark all specified text";
  2420. /* api callbacks */
  2421. ot->exec = text_mark_all_exec;
  2422. ot->poll = text_space_edit_poll;
  2423. }
  2424. /******************* find set selected *********************/
  2425. static int text_find_set_selected_exec(bContext *C, wmOperator *op)
  2426. {
  2427. SpaceText *st = CTX_wm_space_text(C);
  2428. Text *text = CTX_data_edit_text(C);
  2429. char *tmp;
  2430. tmp = txt_sel_to_buf(text);
  2431. BLI_strncpy(st->findstr, tmp, ST_MAX_FIND_STR);
  2432. MEM_freeN(tmp);
  2433. if (!st->findstr[0])
  2434. return OPERATOR_FINISHED;
  2435. return text_find_and_replace(C, op, TEXT_FIND);
  2436. }
  2437. void TEXT_OT_find_set_selected(wmOperatorType *ot)
  2438. {
  2439. /* identifiers */
  2440. ot->name = "Find Set Selected";
  2441. ot->idname = "TEXT_OT_find_set_selected";
  2442. ot->description = "Find specified text and set as selected";
  2443. /* api callbacks */
  2444. ot->exec = text_find_set_selected_exec;
  2445. ot->poll = text_space_edit_poll;
  2446. }
  2447. /******************* replace set selected *********************/
  2448. static int text_replace_set_selected_exec(bContext *C, wmOperator *UNUSED(op))
  2449. {
  2450. SpaceText *st = CTX_wm_space_text(C);
  2451. Text *text = CTX_data_edit_text(C);
  2452. char *tmp;
  2453. tmp = txt_sel_to_buf(text);
  2454. BLI_strncpy(st->replacestr, tmp, ST_MAX_FIND_STR);
  2455. MEM_freeN(tmp);
  2456. return OPERATOR_FINISHED;
  2457. }
  2458. void TEXT_OT_replace_set_selected(wmOperatorType *ot)
  2459. {
  2460. /* identifiers */
  2461. ot->name = "Replace Set Selected";
  2462. ot->idname = "TEXT_OT_replace_set_selected";
  2463. ot->description = "Replace text with specified text and set as selected";
  2464. /* api callbacks */
  2465. ot->exec = text_replace_set_selected_exec;
  2466. ot->poll = text_space_edit_poll;
  2467. }
  2468. /****************** resolve conflict operator ******************/
  2469. enum { RESOLVE_IGNORE, RESOLVE_RELOAD, RESOLVE_SAVE, RESOLVE_MAKE_INTERNAL };
  2470. static EnumPropertyItem resolution_items[] = {
  2471. {RESOLVE_IGNORE, "IGNORE", 0, "Ignore", ""},
  2472. {RESOLVE_RELOAD, "RELOAD", 0, "Reload", ""},
  2473. {RESOLVE_SAVE, "SAVE", 0, "Save", ""},
  2474. {RESOLVE_MAKE_INTERNAL, "MAKE_INTERNAL", 0, "Make Internal", ""},
  2475. {0, NULL, 0, NULL, NULL}
  2476. };
  2477. /* returns 0 if file on disk is the same or Text is in memory only
  2478. * returns 1 if file has been modified on disk since last local edit
  2479. * returns 2 if file on disk has been deleted
  2480. * -1 is returned if an error occurs */
  2481. int text_file_modified(Text *text)
  2482. {
  2483. struct stat st;
  2484. int result;
  2485. char file[FILE_MAX];
  2486. if (!text || !text->name)
  2487. return 0;
  2488. BLI_strncpy(file, text->name, FILE_MAX);
  2489. BLI_path_abs(file, G.main->name);
  2490. if (!BLI_exists(file))
  2491. return 2;
  2492. result = stat(file, &st);
  2493. if (result == -1)
  2494. return -1;
  2495. if ((st.st_mode & S_IFMT) != S_IFREG)
  2496. return -1;
  2497. if (st.st_mtime > text->mtime)
  2498. return 1;
  2499. return 0;
  2500. }
  2501. static void text_ignore_modified(Text *text)
  2502. {
  2503. struct stat st;
  2504. int result;
  2505. char file[FILE_MAX];
  2506. if (!text || !text->name) return;
  2507. BLI_strncpy(file, text->name, FILE_MAX);
  2508. BLI_path_abs(file, G.main->name);
  2509. if (!BLI_exists(file)) return;
  2510. result = stat(file, &st);
  2511. if (result == -1 || (st.st_mode & S_IFMT) != S_IFREG)
  2512. return;
  2513. text->mtime = st.st_mtime;
  2514. }
  2515. static int text_resolve_conflict_exec(bContext *C, wmOperator *op)
  2516. {
  2517. Text *text = CTX_data_edit_text(C);
  2518. int resolution = RNA_enum_get(op->ptr, "resolution");
  2519. switch (resolution) {
  2520. case RESOLVE_RELOAD:
  2521. return text_reload_exec(C, op);
  2522. case RESOLVE_SAVE:
  2523. return text_save_exec(C, op);
  2524. case RESOLVE_MAKE_INTERNAL:
  2525. return text_make_internal_exec(C, op);
  2526. case RESOLVE_IGNORE:
  2527. text_ignore_modified(text);
  2528. return OPERATOR_FINISHED;
  2529. }
  2530. return OPERATOR_CANCELLED;
  2531. }
  2532. static int text_resolve_conflict_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
  2533. {
  2534. Text *text = CTX_data_edit_text(C);
  2535. uiPopupMenu *pup;
  2536. uiLayout *layout;
  2537. switch (text_file_modified(text)) {
  2538. case 1:
  2539. if (text->flags & TXT_ISDIRTY) {
  2540. /* modified locally and externally, ahhh. offer more possibilites. */
  2541. pup = uiPupMenuBegin(C, "File Modified Outside and Inside Blender", ICON_NONE);
  2542. layout = uiPupMenuLayout(pup);
  2543. uiItemEnumO_ptr(layout, op->type, "Reload from disk (ignore local changes)", 0, "resolution", RESOLVE_RELOAD);
  2544. uiItemEnumO_ptr(layout, op->type, "Save to disk (ignore outside changes)", 0, "resolution", RESOLVE_SAVE);
  2545. uiItemEnumO_ptr(layout, op->type, "Make text internal (separate copy)", 0, "resolution", RESOLVE_MAKE_INTERNAL);
  2546. uiPupMenuEnd(C, pup);
  2547. }
  2548. else {
  2549. pup = uiPupMenuBegin(C, "File Modified Outside Blender", ICON_NONE);
  2550. layout = uiPupMenuLayout(pup);
  2551. uiItemEnumO_ptr(layout, op->type, "Reload from disk", 0, "resolution", RESOLVE_RELOAD);
  2552. uiItemEnumO_ptr(layout, op->type, "Make text internal (separate copy)", 0, "resolution", RESOLVE_MAKE_INTERNAL);
  2553. uiItemEnumO_ptr(layout, op->type, "Ignore", 0, "resolution", RESOLVE_IGNORE);
  2554. uiPupMenuEnd(C, pup);
  2555. }
  2556. break;
  2557. case 2:
  2558. pup = uiPupMenuBegin(C, "File Deleted Outside Blender", ICON_NONE);
  2559. layout = uiPupMenuLayout(pup);
  2560. uiItemEnumO_ptr(layout, op->type, "Make text internal", 0, "resolution", RESOLVE_MAKE_INTERNAL);
  2561. uiItemEnumO_ptr(layout, op->type, "Recreate file", 0, "resolution", RESOLVE_SAVE);
  2562. uiPupMenuEnd(C, pup);
  2563. break;
  2564. }
  2565. return OPERATOR_CANCELLED;
  2566. }
  2567. void TEXT_OT_resolve_conflict(wmOperatorType *ot)
  2568. {
  2569. /* identifiers */
  2570. ot->name = "Resolve Conflict";
  2571. ot->idname = "TEXT_OT_resolve_conflict";
  2572. ot->description = "When external text is out of sync, resolve the conflict";
  2573. /* api callbacks */
  2574. ot->exec = text_resolve_conflict_exec;
  2575. ot->invoke = text_resolve_conflict_invoke;
  2576. ot->poll = text_save_poll;
  2577. /* properties */
  2578. RNA_def_enum(ot->srna, "resolution", resolution_items, RESOLVE_IGNORE, "Resolution", "How to solve conflict due to differences in internal and external text");
  2579. }
  2580. /********************** to 3d object operator *****************/
  2581. static int text_to_3d_object_exec(bContext *C, wmOperator *op)
  2582. {
  2583. Text *text = CTX_data_edit_text(C);
  2584. int split_lines = RNA_boolean_get(op->ptr, "split_lines");
  2585. ED_text_to_object(C, text, split_lines);
  2586. return OPERATOR_FINISHED;
  2587. }
  2588. void TEXT_OT_to_3d_object(wmOperatorType *ot)
  2589. {
  2590. /* identifiers */
  2591. ot->name = "To 3D Object";
  2592. ot->idname = "TEXT_OT_to_3d_object";
  2593. ot->description = "Create 3d text object from active text data block";
  2594. /* api callbacks */
  2595. ot->exec = text_to_3d_object_exec;
  2596. ot->poll = text_edit_poll;
  2597. /* flags */
  2598. ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
  2599. /* properties */
  2600. RNA_def_boolean(ot->srna, "split_lines", 0, "Split Lines", "Create one object per line in the text");
  2601. }
  2602. /************************ undo ******************************/
  2603. void ED_text_undo_step(bContext *C, int step)
  2604. {
  2605. Text *text = CTX_data_edit_text(C);
  2606. if (!text)
  2607. return;
  2608. if (step == 1)
  2609. txt_do_undo(text);
  2610. else if (step == -1)
  2611. txt_do_redo(text);
  2612. text_update_edited(text);
  2613. text_update_cursor_moved(C);
  2614. text_drawcache_tag_update(CTX_wm_space_text(C), 1);
  2615. WM_event_add_notifier(C, NC_TEXT | NA_EDITED, text);
  2616. }