Blender V5.0
sequencer_text_edit.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2024 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cstddef>
10
11#include "DNA_sequence_types.h"
12
13#include "BLI_math_matrix.hh"
14#include "BLI_math_vector.hh"
15#include "BLI_string.h"
16#include "BLI_string_utf8.h"
17
18#include "BKE_context.hh"
19#include "BKE_scene.hh"
20
21#include "SEQ_effects.hh"
22#include "SEQ_relations.hh"
23#include "SEQ_select.hh"
24#include "SEQ_time.hh"
25#include "SEQ_transform.hh"
26
27#include "WM_api.hh"
28
29#include "RNA_define.hh"
30
31#include "UI_view2d.hh"
32
33#include "ED_screen.hh"
34
35/* Own include. */
36#include "sequencer_intern.hh"
37
38namespace blender::ed::vse {
39
41{
43 return false;
44 }
45 const Scene *scene = CTX_data_sequencer_scene(C);
46 if (!scene) {
47 return false;
48 }
49
50 const Strip *strip = seq::select_active_get(scene);
51 if (strip == nullptr || strip->type != STRIP_TYPE_TEXT || !seq::effects_can_render_text(strip)) {
52 return false;
53 }
54
55 const TextVars *data = static_cast<TextVars *>(strip->effectdata);
56 if (data == nullptr || data->runtime == nullptr) {
57 return false;
58 }
59
60 return true;
61}
62
64{
65 const Scene *scene = CTX_data_sequencer_scene(C);
66 if (!scene) {
67 return false;
68 }
69 const Strip *strip = seq::select_active_get(scene);
70 if (strip == nullptr || !sequencer_text_editing_poll(C)) {
71 return false;
72 }
73
75 return false;
76 }
77
78 if (!seq::time_strip_intersects_frame(scene, strip, BKE_scene_frame_get(scene))) {
79 return false;
80 }
81
82 return (strip->flag & SEQ_FLAG_TEXT_EDITING_ACTIVE) != 0;
83}
84
86{
87 cursor_offset = std::clamp(cursor_offset, 0, text->character_count);
88
89 int2 cursor_position{0, 0};
90 for (const seq::LineInfo &line : text->lines) {
91 if (cursor_offset < line.characters.size()) {
92 cursor_position.x = cursor_offset;
93 break;
94 }
95 cursor_offset -= line.characters.size();
96 cursor_position.y += 1;
97 }
98
99 cursor_position.y = std::clamp(cursor_position.y, 0, int(text->lines.size() - 1));
100 cursor_position.x = std::clamp(
101 cursor_position.x, 0, int(text->lines[cursor_position.y].characters.size() - 1));
102
103 return cursor_position;
104}
105
107 const int2 cursor_pos)
108{
109 return text->lines[cursor_pos.y].characters[cursor_pos.x];
110}
111
113 const int cursor_offset)
114{
115 const int2 cursor_pos = strip_text_cursor_offset_to_position(text, cursor_offset);
116 return character_at_cursor_pos_get(text, cursor_pos);
117}
118
119static int cursor_position_to_offset(const TextVarsRuntime *text, int2 cursor_position)
120{
121 return character_at_cursor_pos_get(text, cursor_position).index;
122}
123
125{
126 data->selection_start_offset = 0;
127 data->selection_end_offset = 0;
128}
129
131{
132 /* Ensure, that selection start < selection end. */
133 int sel_start_offset = data->selection_start_offset;
134 int sel_end_offset = data->selection_end_offset;
135 if (sel_start_offset > sel_end_offset) {
136 std::swap(sel_start_offset, sel_end_offset);
137 }
138
139 return IndexRange(sel_start_offset, sel_end_offset - sel_start_offset);
140}
141
143{
145}
146
148{
149 if (!text_has_selection(data)) {
150 return;
151 }
152
153 TextVarsRuntime *text = data->runtime;
155
156 seq::CharInfo char_start = character_at_cursor_offset_get(text, sel_range.first());
157 seq::CharInfo char_end = character_at_cursor_offset_get(text, sel_range.last());
158
159 const int offset_start = char_start.offset;
160 const int offset_end = char_end.offset + char_end.byte_length;
161 BLI_assert(offset_start >= 0 && offset_end <= data->text_len_bytes);
162 BLI_assert(offset_end >= 0 && offset_end <= data->text_len_bytes);
163 BLI_assert(offset_start <= offset_end);
164 const int remaining = data->text_len_bytes - offset_end;
165
166 std::memmove(data->text_ptr + offset_start, data->text_ptr + offset_end, remaining + 1);
167 data->text_len_bytes = offset_start + remaining;
168
169 const int2 sel_start = strip_text_cursor_offset_to_position(text, sel_range.first());
170 data->cursor_offset = cursor_position_to_offset(text, sel_start);
172}
173
180
181enum {
192};
193
195 {LINE_BEGIN, "LINE_BEGIN", 0, "Line Begin", ""},
196 {LINE_END, "LINE_END", 0, "Line End", ""},
197 {TEXT_BEGIN, "TEXT_BEGIN", 0, "Text Begin", ""},
198 {TEXT_END, "TEXT_END", 0, "Text End", ""},
199 {PREV_CHAR, "PREVIOUS_CHARACTER", 0, "Previous Character", ""},
200 {NEXT_CHAR, "NEXT_CHARACTER", 0, "Next Character", ""},
201 {PREV_WORD, "PREVIOUS_WORD", 0, "Previous Word", ""},
202 {NEXT_WORD, "NEXT_WORD", 0, "Next Word", ""},
203 {PREV_LINE, "PREVIOUS_LINE", 0, "Previous Line", ""},
204 {NEXT_LINE, "NEXT_LINE", 0, "Next Line", ""},
205 {0, nullptr, 0, nullptr, nullptr},
206};
207
208static int2 cursor_move_by_character(int2 cursor_position, const TextVarsRuntime *text, int offset)
209{
210 const seq::LineInfo &cur_line = text->lines[cursor_position.y];
211 /* Move to next line. */
212 if (cursor_position.x + offset > cur_line.characters.size() - 1 &&
213 cursor_position.y < text->lines.size() - 1)
214 {
215 cursor_position.x = 0;
216 cursor_position.y++;
217 }
218 /* Move to previous line. */
219 else if (cursor_position.x + offset < 0 && cursor_position.y > 0) {
220 cursor_position.y--;
221 cursor_position.x = text->lines[cursor_position.y].characters.size() - 1;
222 }
223 else {
224 cursor_position.x += offset;
225 const int position_max = text->lines[cursor_position.y].characters.size() - 1;
226 cursor_position.x = std::clamp(cursor_position.x, 0, position_max);
227 }
228 return cursor_position;
229}
230
231static int2 cursor_move_by_line(int2 cursor_position, const TextVarsRuntime *text, int offset)
232{
233 const seq::LineInfo &cur_line = text->lines[cursor_position.y];
234 const int cur_pos_x = cur_line.characters[cursor_position.x].position.x;
235
236 const int line_max = text->lines.size() - 1;
237 const int new_line_index = std::clamp(cursor_position.y + offset, 0, line_max);
238 const seq::LineInfo &new_line = text->lines[new_line_index];
239
240 if (cursor_position.y == new_line_index) {
241 return cursor_position;
242 }
243
244 /* Find character in another line closest to current position. */
245 int best_distance = std::numeric_limits<int>::max();
246 int best_character_index = 0;
247
248 for (int i : new_line.characters.index_range()) {
249 seq::CharInfo character = new_line.characters[i];
250 const int distance = std::abs(character.position.x - cur_pos_x);
251 if (distance < best_distance) {
252 best_distance = distance;
253 best_character_index = i;
254 }
255 }
256
257 cursor_position.x = best_character_index;
258 cursor_position.y = new_line_index;
259 return cursor_position;
260}
261
262static int2 cursor_move_line_end(int2 cursor_position, const TextVarsRuntime *text)
263{
264 const seq::LineInfo &cur_line = text->lines[cursor_position.y];
265 cursor_position.x = cur_line.characters.size() - 1;
266 return cursor_position;
267}
268
269static bool is_whitespace_transition(char chr1, char chr2)
270{
271 return ELEM(chr1, ' ', '\t', '\n') && !ELEM(chr2, ' ', '\t', '\n');
272}
273
274static int2 cursor_move_prev_word(int2 cursor_position,
275 const TextVarsRuntime *text,
276 const char *text_ptr)
277{
278 cursor_position = cursor_move_by_character(cursor_position, text, -1);
279
280 while (cursor_position.x > 0 || cursor_position.y > 0) {
281 const seq::CharInfo character = character_at_cursor_pos_get(text, cursor_position);
282 const int2 prev_cursor_pos = cursor_move_by_character(cursor_position, text, -1);
283 const seq::CharInfo prev_character = character_at_cursor_pos_get(text, prev_cursor_pos);
284
285 if (is_whitespace_transition(text_ptr[prev_character.offset], text_ptr[character.offset])) {
286 break;
287 }
288 cursor_position = prev_cursor_pos;
289 }
290 return cursor_position;
291}
292
293static int2 cursor_move_next_word(int2 cursor_position,
294 const TextVarsRuntime *text,
295 const char *text_ptr)
296{
297 const int maxline = text->lines.size() - 1;
298 const int maxchar = text->lines.last().characters.size() - 1;
299
300 while ((cursor_position.x < maxchar) || (cursor_position.y < maxline)) {
301 const seq::CharInfo character = character_at_cursor_pos_get(text, cursor_position);
302 cursor_position = cursor_move_by_character(cursor_position, text, 1);
303 const seq::CharInfo next_character = character_at_cursor_pos_get(text, cursor_position);
304
305 if (is_whitespace_transition(text_ptr[next_character.offset], text_ptr[character.offset])) {
306 break;
307 }
308 }
309 return cursor_position;
310}
311
313{
315 TextVars *data = static_cast<TextVars *>(strip->effectdata);
316 const TextVarsRuntime *text = data->runtime;
317
318 if (RNA_boolean_get(op->ptr, "select_text") && !text_has_selection(data)) {
319 data->selection_start_offset = data->cursor_offset;
320 }
321
322 int2 cursor_position = strip_text_cursor_offset_to_position(text, data->cursor_offset);
323
324 switch (RNA_enum_get(op->ptr, "type")) {
325 case PREV_CHAR:
326 cursor_position = cursor_move_by_character(cursor_position, text, -1);
327 break;
328 case NEXT_CHAR:
329 cursor_position = cursor_move_by_character(cursor_position, text, 1);
330 break;
331 case PREV_LINE:
332 cursor_position = cursor_move_by_line(cursor_position, text, -1);
333 break;
334 case NEXT_LINE:
335 cursor_position = cursor_move_by_line(cursor_position, text, 1);
336 break;
337 case LINE_BEGIN:
338 cursor_position.x = 0;
339 break;
340 case LINE_END:
341 cursor_position = cursor_move_line_end(cursor_position, text);
342 break;
343 case TEXT_BEGIN:
344 cursor_position = {0, 0};
345 break;
346 case TEXT_END:
347 cursor_position.y = text->lines.size() - 1;
348 cursor_position = cursor_move_line_end(cursor_position, text);
349 break;
350 case PREV_WORD:
351 cursor_position = cursor_move_prev_word(cursor_position, text, data->text_ptr);
352 break;
353 case NEXT_WORD:
354 cursor_position = cursor_move_next_word(cursor_position, text, data->text_ptr);
355 break;
356 }
357
358 data->cursor_offset = cursor_position_to_offset(text, cursor_position);
359 if (RNA_boolean_get(op->ptr, "select_text")) {
360 data->selection_end_offset = data->cursor_offset;
361 }
362
363 if (!RNA_boolean_get(op->ptr, "select_text") ||
364 data->cursor_offset == data->selection_start_offset)
365 {
367 }
368
370 return OPERATOR_FINISHED;
371}
372
374{
375 /* identifiers */
376 ot->name = "Move Cursor";
377 ot->description = "Move cursor in text";
378 ot->idname = "SEQUENCER_OT_text_cursor_move";
379
380 /* API callbacks. */
383
384 /* flags */
386
387 /* properties */
388 RNA_def_enum(ot->srna,
389 "type",
392 "Type",
393 "Where to move cursor to, to make a selection");
394
396 ot->srna, "select_text", false, "Select Text", "Select text while moving cursor");
398}
399
400static bool text_insert(TextVars *data, const char *buf, const size_t buf_len)
401{
402 BLI_assert(strlen(buf) == buf_len);
403 const TextVarsRuntime *text = data->runtime;
404
406
407 size_t needed_size = data->text_len_bytes + buf_len + 1;
408 char *new_text = MEM_malloc_arrayN<char>(needed_size, "text");
409
410 const seq::CharInfo cur_char = character_at_cursor_offset_get(text, data->cursor_offset);
411 BLI_assert(cur_char.offset >= 0 && cur_char.offset <= data->text_len_bytes);
412 std::memcpy(new_text, data->text_ptr, cur_char.offset);
413 std::memcpy(new_text + cur_char.offset, buf, buf_len);
414 std::memcpy(new_text + cur_char.offset + buf_len,
415 data->text_ptr + cur_char.offset,
416 data->text_len_bytes - cur_char.offset + 1);
417 data->text_len_bytes += buf_len;
418 MEM_freeN(data->text_ptr);
419 data->text_ptr = new_text;
420
421 data->cursor_offset += 1;
422 return true;
423}
424
426{
428 TextVars *data = static_cast<TextVars *>(strip->effectdata);
429
430 char str[512];
431 RNA_string_get(op->ptr, "string", str);
432
433 const size_t in_buf_len = STRNLEN(str);
434 if (in_buf_len == 0) {
436 }
437
438 if (!text_insert(data, str, in_buf_len)) {
439 return OPERATOR_CANCELLED;
440 }
441
443 return OPERATOR_FINISHED;
444}
445
447 wmOperator *op,
448 const wmEvent *event)
449{
450 char str[6];
452 RNA_string_set(op->ptr, "string", str);
453 return sequencer_text_insert_exec(C, op);
454}
455
457{
458 /* identifiers */
459 ot->name = "Insert Character";
460 ot->description = "Insert text at cursor position";
461 ot->idname = "SEQUENCER_OT_text_insert";
462
463 /* API callbacks. */
467
468 /* flags */
469 ot->flag = OPTYPE_UNDO;
470
471 /* properties */
473 ot->srna, "string", nullptr, 512, "String", "String to be inserted at cursor position");
474}
475
478 {DEL_NEXT_SEL, "NEXT_OR_SELECTION", 0, "Next or Selection", ""},
479 {DEL_PREV_SEL, "PREVIOUS_OR_SELECTION", 0, "Previous or Selection", ""},
480 {0, nullptr, 0, nullptr, nullptr},
481};
482
483static void delete_character(const seq::CharInfo character, TextVars *data)
484{
485 const int offset_start = character.offset;
486 const int offset_end = character.offset + character.byte_length;
487 BLI_assert(offset_start >= 0 && offset_start <= data->text_len_bytes);
488 BLI_assert(offset_end >= 0 && offset_end <= data->text_len_bytes);
489 const int remaining = data->text_len_bytes - offset_end + 1;
490 std::memmove(data->text_ptr + offset_start, data->text_ptr + offset_end, remaining);
491 data->text_len_bytes -= character.byte_length;
492 BLI_assert(data->text_len_bytes >= 0);
493}
494
496{
498 TextVars *data = static_cast<TextVars *>(strip->effectdata);
499 const TextVarsRuntime *text = data->runtime;
500 const int type = RNA_enum_get(op->ptr, "type");
501
505 return OPERATOR_FINISHED;
506 }
507
508 if (type == DEL_NEXT_SEL) {
509 if (data->cursor_offset >= text->character_count) {
510 return OPERATOR_CANCELLED;
511 }
512
514 }
515 if (type == DEL_PREV_SEL) {
516 if (data->cursor_offset == 0) {
517 return OPERATOR_CANCELLED;
518 }
519
521 data->cursor_offset -= 1;
522 }
523
525 return OPERATOR_FINISHED;
526}
527
529{
530 /* identifiers */
531 ot->name = "Delete Character";
532 ot->description = "Delete text at cursor position";
533 ot->idname = "SEQUENCER_OT_text_delete";
534
535 /* API callbacks. */
538
539 /* flags */
540 ot->flag = OPTYPE_UNDO;
541
542 /* properties */
543 RNA_def_enum(ot->srna,
544 "type",
547 "Type",
548 "Which part of the text to delete");
549}
550
552{
554 TextVars *data = static_cast<TextVars *>(strip->effectdata);
555
556 if (!text_insert(data, "\n", 1)) {
557 return OPERATOR_CANCELLED;
558 }
559
561 return OPERATOR_FINISHED;
562}
563
565{
566 /* identifiers */
567 ot->name = "Insert Line Break";
568 ot->description = "Insert line break at cursor position";
569 ot->idname = "SEQUENCER_OT_text_line_break";
570
571 /* API callbacks. */
574
575 /* flags */
576 ot->flag = OPTYPE_UNDO;
577}
578
580{
582 TextVars *data = static_cast<TextVars *>(strip->effectdata);
583 data->selection_start_offset = 0;
584 data->selection_end_offset = data->runtime->character_count;
586 return OPERATOR_FINISHED;
587}
588
590{
591 /* identifiers */
592 ot->name = "Select All";
593 ot->description = "Select all characters";
594 ot->idname = "SEQUENCER_OT_text_select_all";
595
596 /* API callbacks. */
599
600 /* flags */
601 ot->flag = OPTYPE_UNDO;
602}
603
605{
607 TextVars *data = static_cast<TextVars *>(strip->effectdata);
608
609 if (!text_has_selection(data)) {
610 /* Exit edit mode, so text can be translated by mouse. */
612 }
613 else {
615 }
616
618 return OPERATOR_FINISHED;
619}
620
622{
623 /* identifiers */
624 ot->name = "Deselect All";
625 ot->description = "Deselect all characters";
626 ot->idname = "SEQUENCER_OT_text_deselect_all";
627
628 /* API callbacks. */
631
632 /* flags */
633 ot->flag = OPTYPE_UNDO;
634}
635
649
651{
652 /* identifiers */
653 ot->name = "Edit Text";
654 ot->description = "Toggle text editing";
655 ot->idname = "SEQUENCER_OT_text_edit_mode_toggle";
656
657 /* API callbacks. */
660
661 /* flags */
662 ot->flag = OPTYPE_UNDO;
663}
664
665static int find_closest_cursor_offset(const TextVars *data, float2 mouse_loc)
666{
667 const TextVarsRuntime *text = data->runtime;
668 int best_cursor_offset = 0;
669 float best_distance = std::numeric_limits<float>::max();
670
671 for (const seq::LineInfo &line : text->lines) {
672 for (const seq::CharInfo &character : line.characters) {
673 const float distance = math::distance(mouse_loc, character.position);
674 if (distance < best_distance) {
675 best_distance = distance;
676 best_cursor_offset = character.index;
677 }
678 }
679 }
680
681 return best_cursor_offset;
682}
683
684static void cursor_set_by_mouse_position(const bContext *C, const wmEvent *event)
685{
686 const Scene *scene = CTX_data_sequencer_scene(C);
687 const Strip *strip = seq::select_active_get(scene);
688 TextVars *data = static_cast<TextVars *>(strip->effectdata);
689 const View2D *v2d = UI_view2d_fromcontext(C);
690
691 int2 mval_region;
692 WM_event_drag_start_mval(event, CTX_wm_region(C), mval_region);
693 float2 mouse_loc;
694 UI_view2d_region_to_view(v2d, mval_region.x, mval_region.y, &mouse_loc.x, &mouse_loc.y);
695
696 /* Convert cursor coordinates to domain of CharInfo::position. */
697 const blender::float2 view_offs{-scene->r.xsch / 2.0f, -scene->r.ysch / 2.0f};
698 const float view_aspect = scene->r.xasp / scene->r.yasp;
700 strip);
701 // MSVC 2019 can't decide here for some reason, pick the template for it.
702 transform_mat = blender::math::invert<float, 3>(transform_mat);
703
704 mouse_loc.x /= view_aspect;
705 mouse_loc = blender::math::transform_point(transform_mat, mouse_loc);
706 mouse_loc -= view_offs;
707 data->cursor_offset = find_closest_cursor_offset(data, float2(mouse_loc));
708}
709
711 wmOperator * /*op*/,
712 const wmEvent *event)
713{
714 const Scene *scene = CTX_data_sequencer_scene(C);
715 const Strip *strip = seq::select_active_get(scene);
716 TextVars *data = static_cast<TextVars *>(strip->effectdata);
717 bool make_selection = false;
718
719 switch (event->type) {
720 case LEFTMOUSE:
721 if (event->val == KM_RELEASE) {
723 if (make_selection) {
724 data->selection_end_offset = data->cursor_offset;
725 }
726 return OPERATOR_FINISHED;
727 }
728 break;
729 case MIDDLEMOUSE:
730 case RIGHTMOUSE:
731 return OPERATOR_FINISHED;
732 case MOUSEMOVE:
733 make_selection = true;
734 if (!text_has_selection(data)) {
735 data->selection_start_offset = data->cursor_offset;
736 }
738 data->selection_end_offset = data->cursor_offset;
739 break;
740 default: {
741 break;
742 }
743 }
744
747}
748
750 wmOperator *op,
751 const wmEvent *event)
752{
753 const Scene *scene = CTX_data_sequencer_scene(C);
754 Strip *strip = seq::select_active_get(scene);
755 TextVars *data = static_cast<TextVars *>(strip->effectdata);
756 const View2D *v2d = UI_view2d_fromcontext(C);
757
758 int2 mval_region;
759 WM_event_drag_start_mval(event, CTX_wm_region(C), mval_region);
760 float2 mouse_loc;
761 UI_view2d_region_to_view(v2d, mval_region.x, mval_region.y, &mouse_loc.x, &mouse_loc.y);
762
763 if (!strip_point_image_isect(scene, strip, mouse_loc)) {
766 }
767
770
774}
775
777{
778 /* identifiers */
779 ot->name = "Set Cursor";
780 ot->description = "Set cursor position in text";
781 ot->idname = "SEQUENCER_OT_text_cursor_set";
782
783 /* API callbacks. */
787
788 /* flags */
790
791 /* properties */
792
794 ot->srna, "select_text", false, "Select Text", "Select text while moving cursor");
796}
797
798static void text_edit_copy(const TextVars *data)
799{
800 const TextVarsRuntime *text = data->runtime;
801 const IndexRange selection_range = strip_text_selection_range_get(data);
802 const seq::CharInfo start = character_at_cursor_offset_get(text, selection_range.first());
803 const seq::CharInfo end = character_at_cursor_offset_get(text, selection_range.last());
804
805 const int offset_start = start.offset;
806 const int offset_end = end.offset + end.byte_length;
807 BLI_assert(offset_start >= 0 && offset_start <= data->text_len_bytes);
808 BLI_assert(offset_end >= 0 && offset_end <= data->text_len_bytes);
809 BLI_assert(offset_start <= offset_end);
810
811 const size_t len = offset_end - offset_start;
812 char *buf = MEM_malloc_arrayN<char>(len + 1, "text clipboard");
813 memcpy(buf, data->text_ptr + offset_start, len);
814 buf[len] = 0;
815 WM_clipboard_text_set(buf, false);
816 MEM_freeN(buf);
817}
818
820{
822 const TextVars *data = static_cast<TextVars *>(strip->effectdata);
823
824 if (!text_has_selection(data)) {
825 return OPERATOR_CANCELLED;
826 }
827
829
830 return OPERATOR_FINISHED;
831}
832
834{
835 /* identifiers */
836 ot->name = "Copy Text";
837 ot->description = "Copy text to clipboard";
838 ot->idname = "SEQUENCER_OT_text_edit_copy";
839
840 /* API callbacks. */
843
844 /* flags */
845 ot->flag = OPTYPE_UNDO;
846}
847
849{
851 TextVars *data = static_cast<TextVars *>(strip->effectdata);
852 const TextVarsRuntime *text = data->runtime;
853
854 int buf_len;
855 char *buf = WM_clipboard_text_get(false, true, &buf_len);
856
857 if (buf_len == 0) {
858 return OPERATOR_CANCELLED;
859 }
860
862 size_t needed_size = data->text_len_bytes + buf_len + 1;
863 char *new_text = MEM_malloc_arrayN<char>(needed_size, "text");
864
865 const seq::CharInfo cur_char = character_at_cursor_offset_get(text, data->cursor_offset);
866 BLI_assert(cur_char.offset >= 0 && cur_char.offset <= data->text_len_bytes);
867 std::memcpy(new_text, data->text_ptr, cur_char.offset);
868 std::memcpy(new_text + cur_char.offset, buf, buf_len);
869 std::memcpy(new_text + cur_char.offset + buf_len,
870 data->text_ptr + cur_char.offset,
871 data->text_len_bytes - cur_char.offset + 1);
872 data->text_len_bytes += buf_len;
873 MEM_freeN(data->text_ptr);
874 data->text_ptr = new_text;
875
876 data->cursor_offset += BLI_strlen_utf8(buf);
877
878 MEM_freeN(buf);
880 return OPERATOR_FINISHED;
881}
882
884{
885 /* identifiers */
886 ot->name = "Paste Text";
887 ot->description = "Paste text from clipboard";
888 ot->idname = "SEQUENCER_OT_text_edit_paste";
889
890 /* API callbacks. */
893
894 /* flags */
895 ot->flag = OPTYPE_UNDO;
896}
897
913
915{
916 /* identifiers */
917 ot->name = "Cut Text";
918 ot->description = "Cut text to clipboard";
919 ot->idname = "SEQUENCER_OT_text_edit_cut";
920
921 /* API callbacks. */
924
925 /* flags */
926 ot->flag = OPTYPE_UNDO;
927}
928
929} // namespace blender::ed::vse
ARegion * CTX_wm_region(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
Scene * CTX_data_sequencer_scene(const bContext *C)
float BKE_scene_frame_get(const Scene *scene)
Definition scene.cc:2384
#define BLI_assert(a)
Definition BLI_assert.h:46
#define STRNLEN(str)
Definition BLI_string.h:613
char * BLI_strncpy_utf8(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
int BLI_str_utf8_size_safe(const char *p) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
size_t BLI_strlen_utf8(const char *strc) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
#define ELEM(...)
struct TextVarsRuntime TextVarsRuntime
@ STRIP_TYPE_TEXT
@ SEQ_FLAG_TEXT_EDITING_ACTIVE
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
bScreen * ED_screen_animation_no_scrub(const wmWindowManager *wm)
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
#define C
Definition RandGen.cpp:29
View2D * UI_view2d_fromcontext(const bContext *C)
Definition view2d.cc:1855
void UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
Definition view2d.cc:1668
#define ND_SEQUENCER
Definition WM_types.hh:437
@ KM_RELEASE
Definition WM_types.hh:312
#define NC_SCENE
Definition WM_types.hh:378
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
BMesh const char void * data
constexpr int64_t first() const
constexpr int64_t last(const int64_t n=0) const
constexpr bool is_empty() const
#define str(s)
float distance(VecOp< float, D >, VecOp< float, D >) RET
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
blender::IndexRange strip_text_selection_range_get(const TextVars *data)
static void delete_character(const seq::CharInfo character, TextVars *data)
void SEQUENCER_OT_text_edit_copy(wmOperatorType *ot)
void SEQUENCER_OT_text_line_break(wmOperatorType *ot)
static bool text_insert(TextVars *data, const char *buf, const size_t buf_len)
static void cursor_set_by_mouse_position(const bContext *C, const wmEvent *event)
static wmOperatorStatus sequencer_text_insert_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static int2 cursor_move_next_word(int2 cursor_position, const TextVarsRuntime *text, const char *text_ptr)
void SEQUENCER_OT_text_delete(wmOperatorType *ot)
void SEQUENCER_OT_text_edit_cut(wmOperatorType *ot)
static const EnumPropertyItem move_type_items[]
bool sequencer_text_editing_active_poll(bContext *C)
static wmOperatorStatus sequencer_text_edit_mode_toggle_exec(bContext *C, wmOperator *)
static const EnumPropertyItem delete_type_items[]
static wmOperatorStatus sequencer_text_edit_paste_exec(bContext *C, wmOperator *)
void SEQUENCER_OT_text_insert(wmOperatorType *ot)
void SEQUENCER_OT_text_deselect_all(wmOperatorType *ot)
bool sequencer_editing_initialized_and_active(bContext *C)
static wmOperatorStatus sequencer_text_edit_copy_exec(bContext *C, wmOperator *)
static int2 cursor_move_by_line(int2 cursor_position, const TextVarsRuntime *text, int offset)
static void text_editing_update(const bContext *C)
static const seq::CharInfo & character_at_cursor_offset_get(const TextVarsRuntime *text, const int cursor_offset)
void SEQUENCER_OT_text_select_all(wmOperatorType *ot)
static wmOperatorStatus sequencer_text_deselect_all_exec(bContext *C, wmOperator *)
static int2 cursor_move_line_end(int2 cursor_position, const TextVarsRuntime *text)
static bool sequencer_text_editing_poll(bContext *C)
static wmOperatorStatus sequencer_text_cursor_move_exec(bContext *C, wmOperator *op)
static int2 cursor_move_prev_word(int2 cursor_position, const TextVarsRuntime *text, const char *text_ptr)
static wmOperatorStatus sequencer_text_cursor_set_invoke(bContext *C, wmOperator *op, const wmEvent *event)
blender::int2 strip_text_cursor_offset_to_position(const TextVarsRuntime *text, int cursor_offset)
static wmOperatorStatus sequencer_text_delete_exec(bContext *C, wmOperator *op)
static void text_selection_cancel(TextVars *data)
static bool is_whitespace_transition(char chr1, char chr2)
static wmOperatorStatus sequencer_text_insert_exec(bContext *C, wmOperator *op)
void SEQUENCER_OT_text_edit_paste(wmOperatorType *ot)
static void delete_selected_text(TextVars *data)
static int cursor_position_to_offset(const TextVarsRuntime *text, int2 cursor_position)
bool strip_point_image_isect(const Scene *scene, const Strip *strip, float point_view[2])
static const seq::CharInfo & character_at_cursor_pos_get(const TextVarsRuntime *text, const int2 cursor_pos)
static bool text_has_selection(const TextVars *data)
void SEQUENCER_OT_text_cursor_move(wmOperatorType *ot)
static int find_closest_cursor_offset(const TextVars *data, float2 mouse_loc)
static wmOperatorStatus sequencer_text_edit_cut_exec(bContext *C, wmOperator *)
static wmOperatorStatus sequencer_text_select_all_exec(bContext *C, wmOperator *)
static void text_edit_copy(const TextVars *data)
static wmOperatorStatus sequencer_text_line_break_exec(bContext *C, wmOperator *)
static int2 cursor_move_by_character(int2 cursor_position, const TextVarsRuntime *text, int offset)
void SEQUENCER_OT_text_cursor_set(wmOperatorType *ot)
void SEQUENCER_OT_text_edit_mode_toggle(wmOperatorType *ot)
static wmOperatorStatus sequencer_text_cursor_set_modal(bContext *C, wmOperator *, const wmEvent *event)
T distance(const T &a, const T &b)
CartesianBasis invert(const CartesianBasis &basis)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
float3x3 image_transform_matrix_get(const Scene *scene, const Strip *strip)
void relations_invalidate_cache_raw(Scene *scene, Strip *strip)
Strip * select_active_get(const Scene *scene)
bool time_strip_intersects_frame(const Scene *scene, const Strip *strip, const int timeline_frame)
bool effects_can_render_text(const Strip *strip)
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
MatBase< float, 3, 3 > float3x3
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
std::string RNA_string_get(PointerRNA *ptr, const char *name)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_string(StructOrFunctionRNA *cont_, const char *identifier, const char *default_value, const int maxlen, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
struct RenderData r
void * effectdata
Vector< CharInfo > characters
wmEventType type
Definition WM_types.hh:757
short val
Definition WM_types.hh:759
char utf8_buf[6]
Definition WM_types.hh:771
struct PointerRNA * ptr
i
Definition text_draw.cc:230
uint len
void WM_event_drag_start_mval(const wmEvent *event, const ARegion *region, int r_mval[2])
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
@ RIGHTMOUSE
@ MOUSEMOVE
@ LEFTMOUSE
@ MIDDLEMOUSE
wmOperatorType * ot
Definition wm_files.cc:4237
void WM_clipboard_text_set(const char *buf, bool selection)
char * WM_clipboard_text_get(bool selection, bool ensure_utf8, int *r_len)