1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2008 Blender Foundation.
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup edanimation
22 */
23
24 #include <math.h>
25
26 #include "MEM_guardedalloc.h"
27
28 #include "DNA_object_types.h"
29 #include "DNA_scene_types.h"
30
31 #include "BLI_blenlib.h"
32 #include "BLI_math.h"
33 #include "BLI_utildefines.h"
34
35 #include "BLT_translation.h"
36
37 #include "BKE_context.h"
38 #include "BKE_fcurve.h"
39 #include "BKE_idprop.h"
40 #include "BKE_layer.h"
41 #include "BKE_main.h"
42 #include "BKE_report.h"
43 #include "BKE_scene.h"
44 #include "BKE_screen.h"
45 #include "BKE_unit.h"
46
47 #include "RNA_access.h"
48 #include "RNA_define.h"
49 #include "RNA_enum_types.h"
50
51 #include "WM_api.h"
52 #include "WM_types.h"
53
54 #include "GPU_immediate.h"
55 #include "GPU_matrix.h"
56 #include "GPU_state.h"
57
58 #include "UI_interface.h"
59 #include "UI_interface_icons.h"
60 #include "UI_resources.h"
61 #include "UI_view2d.h"
62
63 #include "ED_anim_api.h"
64 #include "ED_markers.h"
65 #include "ED_numinput.h"
66 #include "ED_object.h"
67 #include "ED_screen.h"
68 #include "ED_select_utils.h"
69 #include "ED_transform.h"
70 #include "ED_types.h"
71 #include "ED_util.h"
72
73 #include "DEG_depsgraph.h"
74
75 /* ************* Marker API **************** */
76
77 /* helper function for getting the list of markers to work on */
context_get_markers(Scene * scene,ScrArea * area)78 static ListBase *context_get_markers(Scene *scene, ScrArea *area)
79 {
80 /* local marker sets... */
81 if (area) {
82 if (area->spacetype == SPACE_ACTION) {
83 SpaceAction *saction = (SpaceAction *)area->spacedata.first;
84
85 /* local markers can only be shown when there's only a single active action to grab them from
86 * - flag only takes effect when there's an action, otherwise it can get too confusing?
87 */
88 if (ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_SHAPEKEY) && (saction->action)) {
89 if (saction->flag & SACTION_POSEMARKERS_SHOW) {
90 return &saction->action->markers;
91 }
92 }
93 }
94 }
95
96 /* default to using the scene's markers */
97 return &scene->markers;
98 }
99
100 /* ............. */
101
102 /* public API for getting markers from context */
ED_context_get_markers(const bContext * C)103 ListBase *ED_context_get_markers(const bContext *C)
104 {
105 return context_get_markers(CTX_data_scene(C), CTX_wm_area(C));
106 }
107
108 /* public API for getting markers from "animation" context */
ED_animcontext_get_markers(const bAnimContext * ac)109 ListBase *ED_animcontext_get_markers(const bAnimContext *ac)
110 {
111 if (ac) {
112 return context_get_markers(ac->scene, ac->area);
113 }
114 return NULL;
115 }
116
117 /* --------------------------------- */
118
119 /**
120 * Apply some transformation to markers after the fact
121 *
122 * \param markers: List of markers to affect - this may or may not be the scene markers list,
123 * so don't assume anything.
124 * \param scene: Current scene (for getting current frame)
125 * \param mode: (TfmMode) transform mode that this transform is for
126 * \param value: From the transform code, this is ``t->vec[0]``
127 * (which is delta transform for grab/extend, and scale factor for scale)
128 * \param side: (B/L/R) for 'extend' functionality, which side of current frame to use
129 */
ED_markers_post_apply_transform(ListBase * markers,Scene * scene,int mode,float value,char side)130 int ED_markers_post_apply_transform(
131 ListBase *markers, Scene *scene, int mode, float value, char side)
132 {
133 TimeMarker *marker;
134 float cfra = (float)CFRA;
135 int changed_tot = 0;
136
137 /* sanity check - no markers, or locked markers */
138 if ((scene->toolsettings->lock_markers) || (markers == NULL)) {
139 return changed_tot;
140 }
141
142 /* affect selected markers - it's unlikely that we will want to affect all in this way? */
143 for (marker = markers->first; marker; marker = marker->next) {
144 if (marker->flag & SELECT) {
145 switch (mode) {
146 case TFM_TIME_TRANSLATE:
147 case TFM_TIME_EXTEND: {
148 /* apply delta if marker is on the right side of the current frame */
149 if ((side == 'B') || (side == 'L' && marker->frame < cfra) ||
150 (side == 'R' && marker->frame >= cfra)) {
151 marker->frame += round_fl_to_int(value);
152 changed_tot++;
153 }
154 break;
155 }
156 case TFM_TIME_SCALE: {
157 /* rescale the distance between the marker and the current frame */
158 marker->frame = cfra + round_fl_to_int((float)(marker->frame - cfra) * value);
159 changed_tot++;
160 break;
161 }
162 }
163 }
164 }
165
166 return changed_tot;
167 }
168
169 /* --------------------------------- */
170
171 /* Get the marker that is closest to this point */
172 /* XXX for select, the min_dist should be small */
ED_markers_find_nearest_marker(ListBase * markers,float x)173 TimeMarker *ED_markers_find_nearest_marker(ListBase *markers, float x)
174 {
175 TimeMarker *marker, *nearest = NULL;
176 float dist, min_dist = 1000000;
177
178 if (markers) {
179 for (marker = markers->first; marker; marker = marker->next) {
180 dist = fabsf((float)marker->frame - x);
181
182 if (dist < min_dist) {
183 min_dist = dist;
184 nearest = marker;
185 }
186 }
187 }
188
189 return nearest;
190 }
191
192 /* Return the time of the marker that occurs on a frame closest to the given time */
ED_markers_find_nearest_marker_time(ListBase * markers,float x)193 int ED_markers_find_nearest_marker_time(ListBase *markers, float x)
194 {
195 TimeMarker *nearest = ED_markers_find_nearest_marker(markers, x);
196 return (nearest) ? (nearest->frame) : round_fl_to_int(x);
197 }
198
ED_markers_get_minmax(ListBase * markers,short sel,float * r_first,float * r_last)199 void ED_markers_get_minmax(ListBase *markers, short sel, float *r_first, float *r_last)
200 {
201 TimeMarker *marker;
202 float min, max;
203
204 /* sanity check */
205 // printf("markers = %p - %p, %p\n", markers, markers->first, markers->last);
206 if (ELEM(NULL, markers, markers->first, markers->last)) {
207 *r_first = 0.0f;
208 *r_last = 0.0f;
209 return;
210 }
211
212 min = FLT_MAX;
213 max = -FLT_MAX;
214 for (marker = markers->first; marker; marker = marker->next) {
215 if (!sel || (marker->flag & SELECT)) {
216 if (marker->frame < min) {
217 min = (float)marker->frame;
218 }
219 if (marker->frame > max) {
220 max = (float)marker->frame;
221 }
222 }
223 }
224
225 /* set the min/max values */
226 *r_first = min;
227 *r_last = max;
228 }
229
230 /**
231 * Function used in operator polls, checks whether the markers region is currently drawn in the
232 * editor in which the operator is called.
233 */
ED_operator_markers_region_active(bContext * C)234 static bool ED_operator_markers_region_active(bContext *C)
235 {
236 ScrArea *area = CTX_wm_area(C);
237 if (area == NULL) {
238 return false;
239 }
240
241 switch (area->spacetype) {
242 case SPACE_ACTION: {
243 SpaceAction *saction = area->spacedata.first;
244 if (saction->flag & SACTION_SHOW_MARKERS) {
245 return true;
246 }
247 break;
248 }
249 case SPACE_GRAPH: {
250 SpaceGraph *sipo = area->spacedata.first;
251 if (sipo->mode != SIPO_MODE_DRIVERS && sipo->flag & SIPO_SHOW_MARKERS) {
252 return true;
253 }
254 break;
255 }
256 case SPACE_NLA: {
257 SpaceNla *snla = area->spacedata.first;
258 if (snla->flag & SNLA_SHOW_MARKERS) {
259 return true;
260 }
261 break;
262 }
263 case SPACE_SEQ: {
264 SpaceSeq *seq = area->spacedata.first;
265 if (seq->flag & SEQ_SHOW_MARKERS) {
266 return true;
267 }
268 break;
269 }
270 }
271 return false;
272 }
273
region_position_is_over_marker(View2D * v2d,ListBase * markers,float region_x)274 static bool region_position_is_over_marker(View2D *v2d, ListBase *markers, float region_x)
275 {
276 if (markers == NULL || BLI_listbase_is_empty(markers)) {
277 return false;
278 }
279
280 float frame_at_position = UI_view2d_region_to_view_x(v2d, region_x);
281 TimeMarker *nearest_marker = ED_markers_find_nearest_marker(markers, frame_at_position);
282 float pixel_distance = UI_view2d_scale_get_x(v2d) *
283 fabsf(nearest_marker->frame - frame_at_position);
284
285 return pixel_distance <= UI_DPI_ICON_SIZE;
286 }
287
288 /* --------------------------------- */
289
290 /* Adds a marker to list of cfra elems */
add_marker_to_cfra_elem(ListBase * lb,TimeMarker * marker,short only_sel)291 static void add_marker_to_cfra_elem(ListBase *lb, TimeMarker *marker, short only_sel)
292 {
293 CfraElem *ce, *cen;
294
295 /* should this one only be considered if it is selected? */
296 if ((only_sel) && ((marker->flag & SELECT) == 0)) {
297 return;
298 }
299
300 /* insertion sort - try to find a previous cfra elem */
301 for (ce = lb->first; ce; ce = ce->next) {
302 if (ce->cfra == marker->frame) {
303 /* do because of double keys */
304 if (marker->flag & SELECT) {
305 ce->sel = marker->flag;
306 }
307 return;
308 }
309 if (ce->cfra > marker->frame) {
310 break;
311 }
312 }
313
314 cen = MEM_callocN(sizeof(CfraElem), "add_to_cfra_elem");
315 if (ce) {
316 BLI_insertlinkbefore(lb, ce, cen);
317 }
318 else {
319 BLI_addtail(lb, cen);
320 }
321
322 cen->cfra = marker->frame;
323 cen->sel = marker->flag;
324 }
325
326 /* This function makes a list of all the markers. The only_sel
327 * argument is used to specify whether only the selected markers
328 * are added.
329 */
ED_markers_make_cfra_list(ListBase * markers,ListBase * lb,short only_sel)330 void ED_markers_make_cfra_list(ListBase *markers, ListBase *lb, short only_sel)
331 {
332 TimeMarker *marker;
333
334 if (lb) {
335 /* Clear the list first, since callers have no way of knowing
336 * whether this terminated early otherwise. This may lead
337 * to crashes if the user didn't clear the memory first.
338 */
339 lb->first = lb->last = NULL;
340 }
341 else {
342 return;
343 }
344
345 if (markers == NULL) {
346 return;
347 }
348
349 for (marker = markers->first; marker; marker = marker->next) {
350 add_marker_to_cfra_elem(lb, marker, only_sel);
351 }
352 }
353
ED_markers_deselect_all(ListBase * markers,int action)354 void ED_markers_deselect_all(ListBase *markers, int action)
355 {
356 if (action == SEL_TOGGLE) {
357 action = ED_markers_get_first_selected(markers) ? SEL_DESELECT : SEL_SELECT;
358 }
359
360 LISTBASE_FOREACH (TimeMarker *, marker, markers) {
361 if (action == SEL_SELECT) {
362 marker->flag |= SELECT;
363 }
364 else if (action == SEL_DESELECT) {
365 marker->flag &= ~SELECT;
366 }
367 else if (action == SEL_INVERT) {
368 marker->flag ^= SELECT;
369 }
370 else {
371 BLI_assert(0);
372 }
373 }
374 }
375
376 /* --------------------------------- */
377
378 /* Get the first selected marker */
ED_markers_get_first_selected(ListBase * markers)379 TimeMarker *ED_markers_get_first_selected(ListBase *markers)
380 {
381 TimeMarker *marker;
382
383 if (markers) {
384 for (marker = markers->first; marker; marker = marker->next) {
385 if (marker->flag & SELECT) {
386 return marker;
387 }
388 }
389 }
390
391 return NULL;
392 }
393
394 /* --------------------------------- */
395
396 /* Print debugging prints of list of markers
397 * BSI's: do NOT make static or put in if-defs as "unused code".
398 * That's too much trouble when we need to use for quick debugging!
399 */
debug_markers_print_list(ListBase * markers)400 void debug_markers_print_list(ListBase *markers)
401 {
402 TimeMarker *marker;
403
404 if (markers == NULL) {
405 printf("No markers list to print debug for\n");
406 return;
407 }
408
409 printf("List of markers follows: -----\n");
410
411 for (marker = markers->first; marker; marker = marker->next) {
412 printf(
413 "\t'%s' on %d at %p with %u\n", marker->name, marker->frame, (void *)marker, marker->flag);
414 }
415
416 printf("End of list ------------------\n");
417 }
418
419 /* ************* Marker Drawing ************ */
420
marker_color_get(const TimeMarker * marker,uchar * r_text_color,uchar * r_line_color)421 static void marker_color_get(const TimeMarker *marker, uchar *r_text_color, uchar *r_line_color)
422 {
423 if (marker->flag & SELECT) {
424 UI_GetThemeColor4ubv(TH_TEXT_HI, r_text_color);
425 UI_GetThemeColor4ubv(TH_TIME_MARKER_LINE_SELECTED, r_line_color);
426 }
427 else {
428 UI_GetThemeColor4ubv(TH_TEXT, r_text_color);
429 UI_GetThemeColor4ubv(TH_TIME_MARKER_LINE, r_line_color);
430 }
431 }
432
draw_marker_name(const uchar * text_color,const uiFontStyle * fstyle,TimeMarker * marker,float marker_x,float text_y)433 static void draw_marker_name(const uchar *text_color,
434 const uiFontStyle *fstyle,
435 TimeMarker *marker,
436 float marker_x,
437 float text_y)
438 {
439 const char *name = marker->name;
440 uchar final_text_color[4];
441
442 copy_v4_v4_uchar(final_text_color, text_color);
443
444 #ifdef DURIAN_CAMERA_SWITCH
445 if (marker->camera) {
446 Object *camera = marker->camera;
447 name = camera->id.name + 2;
448 if (camera->restrictflag & OB_RESTRICT_RENDER) {
449 final_text_color[3] = 100;
450 }
451 }
452 #endif
453
454 int name_x = marker_x + UI_DPI_ICON_SIZE * 0.6;
455 UI_fontstyle_draw_simple(fstyle, name_x, text_y, name, final_text_color);
456 }
457
draw_marker_line(const uchar * color,int xpos,int ymin,int ymax)458 static void draw_marker_line(const uchar *color, int xpos, int ymin, int ymax)
459 {
460 GPUVertFormat *format = immVertexFormat();
461 uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
462
463 immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
464
465 float viewport_size[4];
466 GPU_viewport_size_get_f(viewport_size);
467 immUniform2f("viewport_size", viewport_size[2] / UI_DPI_FAC, viewport_size[3] / UI_DPI_FAC);
468
469 immUniformColor4ubv(color);
470 immUniform1i("colors_len", 0); /* "simple" mode */
471 immUniform1f("dash_width", 6.0f);
472 immUniform1f("dash_factor", 0.5f);
473
474 immBegin(GPU_PRIM_LINES, 2);
475 immVertex2f(pos, xpos, ymin);
476 immVertex2f(pos, xpos, ymax);
477 immEnd();
478
479 immUnbindProgram();
480 }
481
marker_get_icon_id(TimeMarker * marker,int flag)482 static int marker_get_icon_id(TimeMarker *marker, int flag)
483 {
484 if (flag & DRAW_MARKERS_LOCAL) {
485 return (marker->flag & ACTIVE) ? ICON_PMARKER_ACT :
486 (marker->flag & SELECT) ? ICON_PMARKER_SEL : ICON_PMARKER;
487 }
488 #ifdef DURIAN_CAMERA_SWITCH
489 if (marker->camera) {
490 return (marker->flag & SELECT) ? ICON_OUTLINER_OB_CAMERA : ICON_CAMERA_DATA;
491 }
492 #endif
493 return (marker->flag & SELECT) ? ICON_MARKER_HLT : ICON_MARKER;
494 }
495
draw_marker(const uiFontStyle * fstyle,TimeMarker * marker,int cfra,int xpos,int flag,int region_height)496 static void draw_marker(
497 const uiFontStyle *fstyle, TimeMarker *marker, int cfra, int xpos, int flag, int region_height)
498 {
499 uchar line_color[4], text_color[4];
500
501 marker_color_get(marker, text_color, line_color);
502
503 GPU_blend(GPU_BLEND_ALPHA);
504
505 draw_marker_line(line_color, xpos, UI_DPI_FAC * 20, region_height);
506
507 int icon_id = marker_get_icon_id(marker, flag);
508 UI_icon_draw(xpos - 0.55f * UI_DPI_ICON_SIZE, UI_DPI_FAC * 18, icon_id);
509
510 GPU_blend(GPU_BLEND_NONE);
511
512 float name_y = UI_DPI_FAC * 18;
513 /* Give an offset to the marker name when selected,
514 * or when near the current frame (5 frames range, starting from the current one). */
515 if ((marker->flag & SELECT) || (cfra - 4 <= marker->frame && marker->frame <= cfra)) {
516 name_y += UI_DPI_FAC * 10;
517 }
518 draw_marker_name(text_color, fstyle, marker, xpos, name_y);
519 }
520
draw_markers_background(rctf * rect)521 static void draw_markers_background(rctf *rect)
522 {
523 uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
524 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
525
526 uchar shade[4];
527 UI_GetThemeColor4ubv(TH_TIME_SCRUB_BACKGROUND, shade);
528
529 immUniformColor4ubv(shade);
530
531 GPU_blend(GPU_BLEND_ALPHA);
532
533 immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
534
535 GPU_blend(GPU_BLEND_NONE);
536
537 immUnbindProgram();
538 }
539
marker_is_in_frame_range(TimeMarker * marker,const int frame_range[2])540 static bool marker_is_in_frame_range(TimeMarker *marker, const int frame_range[2])
541 {
542 if (marker->frame < frame_range[0]) {
543 return false;
544 }
545 if (marker->frame > frame_range[1]) {
546 return false;
547 }
548 return true;
549 }
550
get_marker_region_rect(View2D * v2d,rctf * rect)551 static void get_marker_region_rect(View2D *v2d, rctf *rect)
552 {
553 rect->xmin = v2d->cur.xmin;
554 rect->xmax = v2d->cur.xmax;
555 rect->ymin = 0;
556 rect->ymax = UI_MARKER_MARGIN_Y;
557 }
558
get_marker_clip_frame_range(View2D * v2d,float xscale,int r_range[2])559 static void get_marker_clip_frame_range(View2D *v2d, float xscale, int r_range[2])
560 {
561 float font_width_max = (10 * UI_DPI_FAC) / xscale;
562 r_range[0] = v2d->cur.xmin - sizeof(((TimeMarker *)NULL)->name) * font_width_max;
563 r_range[1] = v2d->cur.xmax + font_width_max;
564 }
565
566 /* Draw Scene-Markers in time window */
ED_markers_draw(const bContext * C,int flag)567 void ED_markers_draw(const bContext *C, int flag)
568 {
569 ListBase *markers = ED_context_get_markers(C);
570 if (markers == NULL || BLI_listbase_is_empty(markers)) {
571 return;
572 }
573
574 ARegion *region = CTX_wm_region(C);
575 View2D *v2d = UI_view2d_fromcontext(C);
576 int cfra = CTX_data_scene(C)->r.cfra;
577
578 rctf markers_region_rect;
579 get_marker_region_rect(v2d, &markers_region_rect);
580
581 draw_markers_background(&markers_region_rect);
582
583 /* no time correction for framelen! space is drawn with old values */
584 float xscale, dummy;
585 UI_view2d_scale_get(v2d, &xscale, &dummy);
586 GPU_matrix_push();
587 GPU_matrix_scale_2f(1.0f / xscale, 1.0f);
588
589 int clip_frame_range[2];
590 get_marker_clip_frame_range(v2d, xscale, clip_frame_range);
591
592 const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
593
594 /* Separate loops in order to draw selected markers on top */
595 LISTBASE_FOREACH (TimeMarker *, marker, markers) {
596 if ((marker->flag & SELECT) == 0) {
597 if (marker_is_in_frame_range(marker, clip_frame_range)) {
598 draw_marker(fstyle, marker, cfra, marker->frame * xscale, flag, region->winy);
599 }
600 }
601 }
602 LISTBASE_FOREACH (TimeMarker *, marker, markers) {
603 if (marker->flag & SELECT) {
604 if (marker_is_in_frame_range(marker, clip_frame_range)) {
605 draw_marker(fstyle, marker, cfra, marker->frame * xscale, flag, region->winy);
606 }
607 }
608 }
609
610 GPU_matrix_pop();
611 }
612
613 /* ************************ Marker Wrappers API ********************* */
614 /* These wrappers allow marker operators to function within the confines
615 * of standard animation editors, such that they can coexist with the
616 * primary operations of those editors.
617 */
618
619 /* ------------------------ */
620
621 /* special poll() which checks if there are selected markers first */
ed_markers_poll_selected_markers(bContext * C)622 static bool ed_markers_poll_selected_markers(bContext *C)
623 {
624 ListBase *markers = ED_context_get_markers(C);
625
626 if (!ED_operator_markers_region_active(C)) {
627 return 0;
628 }
629
630 /* check if some marker is selected */
631 return ED_markers_get_first_selected(markers) != NULL;
632 }
633
ed_markers_poll_selected_no_locked_markers(bContext * C)634 static bool ed_markers_poll_selected_no_locked_markers(bContext *C)
635 {
636 ListBase *markers = ED_context_get_markers(C);
637 ToolSettings *ts = CTX_data_tool_settings(C);
638
639 if (ts->lock_markers || !ED_operator_markers_region_active(C)) {
640 return 0;
641 }
642
643 /* check if some marker is selected */
644 return ED_markers_get_first_selected(markers) != NULL;
645 }
646
647 /* special poll() which checks if there are any markers at all first */
ed_markers_poll_markers_exist(bContext * C)648 static bool ed_markers_poll_markers_exist(bContext *C)
649 {
650 ListBase *markers = ED_context_get_markers(C);
651 ToolSettings *ts = CTX_data_tool_settings(C);
652
653 if (ts->lock_markers || !ED_operator_markers_region_active(C)) {
654 return 0;
655 }
656
657 /* list of markers must exist, as well as some markers in it! */
658 return (markers && markers->first);
659 }
660
661 /* ************************** add markers *************************** */
662
663 /* add TimeMarker at current frame */
ed_marker_add_exec(bContext * C,wmOperator * UNUSED (op))664 static int ed_marker_add_exec(bContext *C, wmOperator *UNUSED(op))
665 {
666 ListBase *markers = ED_context_get_markers(C);
667 TimeMarker *marker;
668 int frame = CTX_data_scene(C)->r.cfra;
669
670 if (markers == NULL) {
671 return OPERATOR_CANCELLED;
672 }
673
674 /* prefer not having 2 markers at the same place,
675 * though the user can move them to overlap once added */
676 for (marker = markers->first; marker; marker = marker->next) {
677 if (marker->frame == frame) {
678 return OPERATOR_CANCELLED;
679 }
680 }
681
682 /* deselect all */
683 for (marker = markers->first; marker; marker = marker->next) {
684 marker->flag &= ~SELECT;
685 }
686
687 marker = MEM_callocN(sizeof(TimeMarker), "TimeMarker");
688 marker->flag = SELECT;
689 marker->frame = frame;
690 BLI_snprintf(marker->name, sizeof(marker->name), "F_%02d", frame); /* XXX - temp code only */
691 BLI_addtail(markers, marker);
692
693 WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL);
694 WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL);
695
696 return OPERATOR_FINISHED;
697 }
698
MARKER_OT_add(wmOperatorType * ot)699 static void MARKER_OT_add(wmOperatorType *ot)
700 {
701 /* identifiers */
702 ot->name = "Add Time Marker";
703 ot->description = "Add a new time marker";
704 ot->idname = "MARKER_OT_add";
705
706 /* api callbacks */
707 ot->exec = ed_marker_add_exec;
708 ot->poll = ED_operator_markers_region_active;
709
710 /* flags */
711 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
712 }
713
714 /* ************************** transform markers *************************** */
715
716 /* operator state vars used:
717 * frs: delta movement
718 *
719 * functions:
720 *
721 * init() check selection, add customdata with old values and some lookups
722 *
723 * apply() do the actual movement
724 *
725 * exit() cleanup, send notifier
726 *
727 * cancel() to escape from modal
728 *
729 * callbacks:
730 *
731 * exec() calls init, apply, exit
732 *
733 * invoke() calls init, adds modal handler
734 *
735 * modal() accept modal events while doing it, ends with apply and exit, or cancel
736 */
737
738 typedef struct MarkerMove {
739 SpaceLink *slink;
740 ListBase *markers;
741 int event_type; /* store invoke-event, to verify */
742 int *oldframe, evtx, firstx;
743 NumInput num;
744 } MarkerMove;
745
ed_marker_move_use_time(MarkerMove * mm)746 static bool ed_marker_move_use_time(MarkerMove *mm)
747 {
748 if (((mm->slink->spacetype == SPACE_SEQ) && !(((SpaceSeq *)mm->slink)->flag & SEQ_DRAWFRAMES)) ||
749 ((mm->slink->spacetype == SPACE_ACTION) &&
750 (((SpaceAction *)mm->slink)->flag & SACTION_DRAWTIME)) ||
751 ((mm->slink->spacetype == SPACE_GRAPH) &&
752 !(((SpaceGraph *)mm->slink)->flag & SIPO_DRAWTIME)) ||
753 ((mm->slink->spacetype == SPACE_NLA) && !(((SpaceNla *)mm->slink)->flag & SNLA_DRAWTIME))) {
754 return true;
755 }
756
757 return false;
758 }
759
ed_marker_move_update_header(bContext * C,wmOperator * op)760 static void ed_marker_move_update_header(bContext *C, wmOperator *op)
761 {
762 Scene *scene = CTX_data_scene(C);
763 MarkerMove *mm = op->customdata;
764 TimeMarker *marker, *selmarker = NULL;
765 const int offs = RNA_int_get(op->ptr, "frames");
766 char str[UI_MAX_DRAW_STR];
767 char str_offs[NUM_STR_REP_LEN];
768 int totmark;
769 const bool use_time = ed_marker_move_use_time(mm);
770
771 for (totmark = 0, marker = mm->markers->first; marker; marker = marker->next) {
772 if (marker->flag & SELECT) {
773 selmarker = marker;
774 totmark++;
775 }
776 }
777
778 if (hasNumInput(&mm->num)) {
779 outputNumInput(&mm->num, str_offs, &scene->unit);
780 }
781 else if (use_time) {
782 BLI_snprintf(str_offs, sizeof(str_offs), "%.2f", FRA2TIME(offs));
783 }
784 else {
785 BLI_snprintf(str_offs, sizeof(str_offs), "%d", offs);
786 }
787
788 if (totmark == 1 && selmarker) {
789 /* we print current marker value */
790 if (use_time) {
791 BLI_snprintf(
792 str, sizeof(str), TIP_("Marker %.2f offset %s"), FRA2TIME(selmarker->frame), str_offs);
793 }
794 else {
795 BLI_snprintf(str, sizeof(str), TIP_("Marker %d offset %s"), selmarker->frame, str_offs);
796 }
797 }
798 else {
799 BLI_snprintf(str, sizeof(str), TIP_("Marker offset %s"), str_offs);
800 }
801
802 ED_area_status_text(CTX_wm_area(C), str);
803 }
804
805 /* copy selection to temp buffer */
806 /* return 0 if not OK */
ed_marker_move_init(bContext * C,wmOperator * op)807 static bool ed_marker_move_init(bContext *C, wmOperator *op)
808 {
809 Scene *scene = CTX_data_scene(C);
810 ListBase *markers = ED_context_get_markers(C);
811 MarkerMove *mm;
812 TimeMarker *marker;
813 int a, totmark;
814
815 if (markers == NULL) {
816 return false;
817 }
818
819 for (totmark = 0, marker = markers->first; marker; marker = marker->next) {
820 if (marker->flag & SELECT) {
821 totmark++;
822 }
823 }
824
825 if (totmark == 0) {
826 return false;
827 }
828
829 op->customdata = mm = MEM_callocN(sizeof(MarkerMove), "Markermove");
830 mm->slink = CTX_wm_space_data(C);
831 mm->markers = markers;
832 mm->oldframe = MEM_callocN(totmark * sizeof(int), "MarkerMove oldframe");
833
834 initNumInput(&mm->num);
835 mm->num.idx_max = 0; /* one axis */
836 mm->num.val_flag[0] |= NUM_NO_FRACTION;
837 mm->num.unit_sys = scene->unit.system;
838 /* No time unit supporting frames currently... */
839 mm->num.unit_type[0] = ed_marker_move_use_time(mm) ? B_UNIT_TIME : B_UNIT_NONE;
840
841 for (a = 0, marker = markers->first; marker; marker = marker->next) {
842 if (marker->flag & SELECT) {
843 mm->oldframe[a] = marker->frame;
844 a++;
845 }
846 }
847
848 return true;
849 }
850
851 /* free stuff */
ed_marker_move_exit(bContext * C,wmOperator * op)852 static void ed_marker_move_exit(bContext *C, wmOperator *op)
853 {
854 MarkerMove *mm = op->customdata;
855
856 /* free data */
857 MEM_freeN(mm->oldframe);
858 MEM_freeN(op->customdata);
859 op->customdata = NULL;
860
861 /* clear custom header prints */
862 ED_area_status_text(CTX_wm_area(C), NULL);
863 }
864
ed_marker_move_invoke(bContext * C,wmOperator * op,const wmEvent * event)865 static int ed_marker_move_invoke(bContext *C, wmOperator *op, const wmEvent *event)
866 {
867 const bool tweak = RNA_struct_find_property(op->ptr, "tweak") &&
868 RNA_boolean_get(op->ptr, "tweak");
869
870 if (tweak) {
871 ARegion *region = CTX_wm_region(C);
872 View2D *v2d = ®ion->v2d;
873 ListBase *markers = ED_context_get_markers(C);
874 if (!region_position_is_over_marker(v2d, markers, event->x - region->winrct.xmin)) {
875 return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
876 }
877 }
878
879 if (ed_marker_move_init(C, op)) {
880 MarkerMove *mm = op->customdata;
881
882 mm->evtx = event->x;
883 mm->firstx = event->x;
884 mm->event_type = event->type;
885
886 /* add temp handler */
887 WM_event_add_modal_handler(C, op);
888
889 /* reset frs delta */
890 RNA_int_set(op->ptr, "frames", 0);
891
892 ed_marker_move_update_header(C, op);
893
894 return OPERATOR_RUNNING_MODAL;
895 }
896
897 return OPERATOR_CANCELLED;
898 }
899
900 /* note, init has to be called successfully */
ed_marker_move_apply(bContext * C,wmOperator * op)901 static void ed_marker_move_apply(bContext *C, wmOperator *op)
902 {
903 #ifdef DURIAN_CAMERA_SWITCH
904 bScreen *screen = CTX_wm_screen(C);
905 Scene *scene = CTX_data_scene(C);
906 Object *camera = scene->camera;
907 #endif
908 MarkerMove *mm = op->customdata;
909 TimeMarker *marker;
910 int a, offs;
911
912 offs = RNA_int_get(op->ptr, "frames");
913 for (a = 0, marker = mm->markers->first; marker; marker = marker->next) {
914 if (marker->flag & SELECT) {
915 marker->frame = mm->oldframe[a] + offs;
916 a++;
917 }
918 }
919
920 WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL);
921 WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL);
922
923 #ifdef DURIAN_CAMERA_SWITCH
924 /* so we get view3d redraws */
925 BKE_scene_camera_switch_update(scene);
926
927 if (camera != scene->camera) {
928 BKE_screen_view3d_scene_sync(screen, scene);
929 WM_event_add_notifier(C, NC_SCENE | NA_EDITED, scene);
930 }
931 #endif
932 }
933
934 /* only for modal */
ed_marker_move_cancel(bContext * C,wmOperator * op)935 static void ed_marker_move_cancel(bContext *C, wmOperator *op)
936 {
937 RNA_int_set(op->ptr, "frames", 0);
938 ed_marker_move_apply(C, op);
939 ed_marker_move_exit(C, op);
940 }
941
ed_marker_move_modal(bContext * C,wmOperator * op,const wmEvent * event)942 static int ed_marker_move_modal(bContext *C, wmOperator *op, const wmEvent *event)
943 {
944 Scene *scene = CTX_data_scene(C);
945 MarkerMove *mm = op->customdata;
946 View2D *v2d = UI_view2d_fromcontext(C);
947 const bool has_numinput = hasNumInput(&mm->num);
948 const bool use_time = ed_marker_move_use_time(mm);
949
950 /* Modal numinput active, try to handle numeric inputs first... */
951 if (event->val == KM_PRESS && has_numinput && handleNumInput(C, &mm->num, event)) {
952 float value = (float)RNA_int_get(op->ptr, "frames");
953
954 applyNumInput(&mm->num, &value);
955 if (use_time) {
956 value = TIME2FRA(value);
957 }
958
959 RNA_int_set(op->ptr, "frames", (int)value);
960 ed_marker_move_apply(C, op);
961 ed_marker_move_update_header(C, op);
962 }
963 else {
964 bool handled = false;
965 switch (event->type) {
966 case EVT_ESCKEY:
967 ed_marker_move_cancel(C, op);
968 return OPERATOR_CANCELLED;
969 case RIGHTMOUSE:
970 /* press = user manually demands transform to be canceled */
971 if (event->val == KM_PRESS) {
972 ed_marker_move_cancel(C, op);
973 return OPERATOR_CANCELLED;
974 }
975 /* else continue; <--- see if release event should be caught for tweak-end */
976 ATTR_FALLTHROUGH;
977
978 case EVT_RETKEY:
979 case EVT_PADENTER:
980 case LEFTMOUSE:
981 case MIDDLEMOUSE:
982 if (WM_event_is_modal_tweak_exit(event, mm->event_type)) {
983 ed_marker_move_exit(C, op);
984 WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL);
985 WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL);
986 return OPERATOR_FINISHED;
987 }
988 break;
989 case MOUSEMOVE:
990 if (!has_numinput) {
991 float dx;
992
993 dx = BLI_rctf_size_x(&v2d->cur) / BLI_rcti_size_x(&v2d->mask);
994
995 if (event->x != mm->evtx) { /* XXX maybe init for first time */
996 float fac;
997
998 mm->evtx = event->x;
999 fac = ((float)(event->x - mm->firstx) * dx);
1000
1001 apply_keyb_grid(event->shift, event->ctrl, &fac, 0.0, FPS, 0.1 * FPS, 0);
1002
1003 RNA_int_set(op->ptr, "frames", (int)fac);
1004 ed_marker_move_apply(C, op);
1005 ed_marker_move_update_header(C, op);
1006 }
1007 }
1008 break;
1009 }
1010
1011 if (!handled && event->val == KM_PRESS && handleNumInput(C, &mm->num, event)) {
1012 float value = (float)RNA_int_get(op->ptr, "frames");
1013
1014 applyNumInput(&mm->num, &value);
1015 if (use_time) {
1016 value = TIME2FRA(value);
1017 }
1018
1019 RNA_int_set(op->ptr, "frames", (int)value);
1020 ed_marker_move_apply(C, op);
1021 ed_marker_move_update_header(C, op);
1022 }
1023 }
1024
1025 return OPERATOR_RUNNING_MODAL;
1026 }
1027
ed_marker_move_exec(bContext * C,wmOperator * op)1028 static int ed_marker_move_exec(bContext *C, wmOperator *op)
1029 {
1030 if (ed_marker_move_init(C, op)) {
1031 ed_marker_move_apply(C, op);
1032 ed_marker_move_exit(C, op);
1033 return OPERATOR_FINISHED;
1034 }
1035 return OPERATOR_PASS_THROUGH;
1036 }
1037
MARKER_OT_move(wmOperatorType * ot)1038 static void MARKER_OT_move(wmOperatorType *ot)
1039 {
1040 /* identifiers */
1041 ot->name = "Move Time Marker";
1042 ot->description = "Move selected time marker(s)";
1043 ot->idname = "MARKER_OT_move";
1044
1045 /* api callbacks */
1046 ot->exec = ed_marker_move_exec;
1047 ot->invoke = ed_marker_move_invoke;
1048 ot->modal = ed_marker_move_modal;
1049 ot->poll = ed_markers_poll_selected_no_locked_markers;
1050 ot->cancel = ed_marker_move_cancel;
1051
1052 /* flags */
1053 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_XY;
1054
1055 /* rna storage */
1056 RNA_def_int(ot->srna, "frames", 0, INT_MIN, INT_MAX, "Frames", "", INT_MIN, INT_MAX);
1057 PropertyRNA *prop = RNA_def_boolean(
1058 ot->srna, "tweak", 0, "Tweak", "Operator has been activated using a tweak event");
1059 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1060 }
1061
1062 /* ************************** duplicate markers *************************** */
1063
1064 /* operator state vars used:
1065 * frs: delta movement
1066 *
1067 * functions:
1068 *
1069 * apply() do the actual duplicate
1070 *
1071 * callbacks:
1072 *
1073 * exec() calls apply, move_exec
1074 *
1075 * invoke() calls apply, move_invoke
1076 *
1077 * modal() uses move_modal
1078 */
1079
1080 /* duplicate selected TimeMarkers */
ed_marker_duplicate_apply(bContext * C)1081 static void ed_marker_duplicate_apply(bContext *C)
1082 {
1083 ListBase *markers = ED_context_get_markers(C);
1084 TimeMarker *marker, *newmarker;
1085
1086 if (markers == NULL) {
1087 return;
1088 }
1089
1090 /* go through the list of markers, duplicate selected markers and add duplicated copies
1091 * to the beginning of the list (unselect original markers)
1092 */
1093 for (marker = markers->first; marker; marker = marker->next) {
1094 if (marker->flag & SELECT) {
1095 /* unselect selected marker */
1096 marker->flag &= ~SELECT;
1097
1098 /* create and set up new marker */
1099 newmarker = MEM_callocN(sizeof(TimeMarker), "TimeMarker");
1100 newmarker->flag = SELECT;
1101 newmarker->frame = marker->frame;
1102 BLI_strncpy(newmarker->name, marker->name, sizeof(marker->name));
1103
1104 #ifdef DURIAN_CAMERA_SWITCH
1105 newmarker->camera = marker->camera;
1106 #endif
1107
1108 if (marker->prop != NULL) {
1109 newmarker->prop = IDP_CopyProperty(marker->prop);
1110 }
1111
1112 /* new marker is added to the beginning of list */
1113 /* FIXME: bad ordering! */
1114 BLI_addhead(markers, newmarker);
1115 }
1116 }
1117 }
1118
ed_marker_duplicate_exec(bContext * C,wmOperator * op)1119 static int ed_marker_duplicate_exec(bContext *C, wmOperator *op)
1120 {
1121 ed_marker_duplicate_apply(C);
1122 ed_marker_move_exec(C, op); /* assumes frs delta set */
1123
1124 return OPERATOR_FINISHED;
1125 }
1126
ed_marker_duplicate_invoke(bContext * C,wmOperator * op,const wmEvent * event)1127 static int ed_marker_duplicate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1128 {
1129 ed_marker_duplicate_apply(C);
1130 return ed_marker_move_invoke(C, op, event);
1131 }
1132
MARKER_OT_duplicate(wmOperatorType * ot)1133 static void MARKER_OT_duplicate(wmOperatorType *ot)
1134 {
1135 /* identifiers */
1136 ot->name = "Duplicate Time Marker";
1137 ot->description = "Duplicate selected time marker(s)";
1138 ot->idname = "MARKER_OT_duplicate";
1139
1140 /* api callbacks */
1141 ot->exec = ed_marker_duplicate_exec;
1142 ot->invoke = ed_marker_duplicate_invoke;
1143 ot->modal = ed_marker_move_modal;
1144 ot->poll = ed_markers_poll_selected_no_locked_markers;
1145 ot->cancel = ed_marker_move_cancel;
1146
1147 /* flags */
1148 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1149
1150 /* rna storage */
1151 RNA_def_int(ot->srna, "frames", 0, INT_MIN, INT_MAX, "Frames", "", INT_MIN, INT_MAX);
1152 }
1153
1154 /* ************************** selection ************************************/
1155
deselect_markers(ListBase * markers)1156 static void deselect_markers(ListBase *markers)
1157 {
1158 LISTBASE_FOREACH (TimeMarker *, marker, markers) {
1159 marker->flag &= ~SELECT;
1160 }
1161 }
1162
1163 /* select/deselect TimeMarker at current frame */
select_timeline_marker_frame(ListBase * markers,int frame,bool extend,bool wait_to_deselect_others)1164 static int select_timeline_marker_frame(ListBase *markers,
1165 int frame,
1166 bool extend,
1167 bool wait_to_deselect_others)
1168 {
1169 TimeMarker *marker, *marker_selected = NULL;
1170 int ret_val = OPERATOR_FINISHED;
1171
1172 if (extend) {
1173 wait_to_deselect_others = false;
1174 }
1175
1176 /* support for selection cycling */
1177 for (marker = markers->first; marker; marker = marker->next) {
1178 if (marker->frame == frame) {
1179 if (marker->flag & SELECT) {
1180 marker_selected = marker->next;
1181 break;
1182 }
1183 }
1184 }
1185
1186 if (wait_to_deselect_others && marker_selected) {
1187 ret_val = OPERATOR_RUNNING_MODAL;
1188 }
1189 /* if extend is not set, then deselect markers */
1190 else {
1191 if (extend == false) {
1192 deselect_markers(markers);
1193 }
1194
1195 LISTBASE_CIRCULAR_FORWARD_BEGIN (markers, marker, marker_selected) {
1196 /* this way a not-extend select will always give 1 selected marker */
1197 if (marker->frame == frame) {
1198 marker->flag ^= SELECT;
1199 break;
1200 }
1201 }
1202 LISTBASE_CIRCULAR_FORWARD_END(markers, marker, marker_selected);
1203 }
1204
1205 return ret_val;
1206 }
1207
select_marker_camera_switch(bContext * C,bool camera,bool extend,ListBase * markers,int cfra)1208 static void select_marker_camera_switch(
1209 bContext *C, bool camera, bool extend, ListBase *markers, int cfra)
1210 {
1211 #ifdef DURIAN_CAMERA_SWITCH
1212 if (camera) {
1213 Scene *scene = CTX_data_scene(C);
1214 ViewLayer *view_layer = CTX_data_view_layer(C);
1215 Base *base;
1216 TimeMarker *marker;
1217 int sel = 0;
1218
1219 if (!extend) {
1220 BKE_view_layer_base_deselect_all(view_layer);
1221 }
1222
1223 for (marker = markers->first; marker; marker = marker->next) {
1224 if (marker->frame == cfra) {
1225 sel = (marker->flag & SELECT);
1226 break;
1227 }
1228 }
1229
1230 for (marker = markers->first; marker; marker = marker->next) {
1231 if (marker->camera) {
1232 if (marker->frame == cfra) {
1233 base = BKE_view_layer_base_find(view_layer, marker->camera);
1234 if (base) {
1235 ED_object_base_select(base, sel);
1236 if (sel) {
1237 ED_object_base_activate(C, base);
1238 }
1239 }
1240 }
1241 }
1242 }
1243
1244 DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
1245 WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1246 }
1247 #else
1248 (void)camera;
1249 #endif
1250 }
1251
ed_marker_select(bContext * C,const int mval[2],bool extend,bool camera,bool wait_to_deselect_others)1252 static int ed_marker_select(
1253 bContext *C, const int mval[2], bool extend, bool camera, bool wait_to_deselect_others)
1254 {
1255 ListBase *markers = ED_context_get_markers(C);
1256 View2D *v2d = UI_view2d_fromcontext(C);
1257 int ret_val = OPERATOR_FINISHED;
1258
1259 if (region_position_is_over_marker(v2d, markers, mval[0])) {
1260 float frame_at_mouse_position = UI_view2d_region_to_view_x(v2d, mval[0]);
1261 int cfra = ED_markers_find_nearest_marker_time(markers, frame_at_mouse_position);
1262 ret_val = select_timeline_marker_frame(markers, cfra, extend, wait_to_deselect_others);
1263
1264 select_marker_camera_switch(C, camera, extend, markers, cfra);
1265 }
1266 else {
1267 deselect_markers(markers);
1268 }
1269
1270 WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL);
1271 WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL);
1272
1273 /* allowing tweaks, but needs OPERATOR_FINISHED, otherwise renaming fails, see T25987. */
1274 return ret_val | OPERATOR_PASS_THROUGH;
1275 }
1276
ed_marker_select_exec(bContext * C,wmOperator * op)1277 static int ed_marker_select_exec(bContext *C, wmOperator *op)
1278 {
1279 const bool extend = RNA_boolean_get(op->ptr, "extend");
1280 const bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others");
1281 bool camera = false;
1282 #ifdef DURIAN_CAMERA_SWITCH
1283 camera = RNA_boolean_get(op->ptr, "camera");
1284 #endif
1285 int mval[2];
1286 mval[0] = RNA_int_get(op->ptr, "mouse_x");
1287 mval[1] = RNA_int_get(op->ptr, "mouse_y");
1288
1289 return ed_marker_select(C, mval, extend, camera, wait_to_deselect_others);
1290 }
1291
MARKER_OT_select(wmOperatorType * ot)1292 static void MARKER_OT_select(wmOperatorType *ot)
1293 {
1294 PropertyRNA *prop;
1295
1296 /* identifiers */
1297 ot->name = "Select Time Marker";
1298 ot->description = "Select time marker(s)";
1299 ot->idname = "MARKER_OT_select";
1300
1301 /* api callbacks */
1302 ot->poll = ed_markers_poll_markers_exist;
1303 ot->exec = ed_marker_select_exec;
1304 ot->invoke = WM_generic_select_invoke;
1305 ot->modal = WM_generic_select_modal;
1306
1307 /* flags */
1308 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1309
1310 WM_operator_properties_generic_select(ot);
1311 prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection");
1312 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1313 #ifdef DURIAN_CAMERA_SWITCH
1314 prop = RNA_def_boolean(ot->srna, "camera", 0, "Camera", "Select the camera");
1315 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1316 #endif
1317 }
1318
1319 /* *************************** box select markers **************** */
1320
1321 /* operator state vars used: (added by default WM callbacks)
1322 * xmin, ymin
1323 * xmax, ymax
1324 *
1325 * customdata: the wmGesture pointer, with subwindow
1326 *
1327 * callbacks:
1328 *
1329 * exec() has to be filled in by user
1330 *
1331 * invoke() default WM function
1332 * adds modal handler
1333 *
1334 * modal() default WM function
1335 * accept modal events while doing it, calls exec(), handles ESC and border drawing
1336 *
1337 * poll() has to be filled in by user for context
1338 */
1339
ed_marker_box_select_invoke(bContext * C,wmOperator * op,const wmEvent * event)1340 static int ed_marker_box_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1341 {
1342 ARegion *region = CTX_wm_region(C);
1343 View2D *v2d = ®ion->v2d;
1344
1345 ListBase *markers = ED_context_get_markers(C);
1346 bool over_marker = region_position_is_over_marker(v2d, markers, event->x - region->winrct.xmin);
1347
1348 bool tweak = RNA_boolean_get(op->ptr, "tweak");
1349 if (tweak && over_marker) {
1350 return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
1351 }
1352
1353 return WM_gesture_box_invoke(C, op, event);
1354 }
1355
ed_marker_box_select_exec(bContext * C,wmOperator * op)1356 static int ed_marker_box_select_exec(bContext *C, wmOperator *op)
1357 {
1358 View2D *v2d = UI_view2d_fromcontext(C);
1359 ListBase *markers = ED_context_get_markers(C);
1360 rctf rect;
1361
1362 WM_operator_properties_border_to_rctf(op, &rect);
1363 UI_view2d_region_to_view_rctf(v2d, &rect, &rect);
1364
1365 if (markers == NULL) {
1366 return 0;
1367 }
1368
1369 const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
1370 const bool select = (sel_op != SEL_OP_SUB);
1371 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1372 ED_markers_deselect_all(markers, SEL_DESELECT);
1373 }
1374
1375 LISTBASE_FOREACH (TimeMarker *, marker, markers) {
1376 if (BLI_rctf_isect_x(&rect, marker->frame)) {
1377 SET_FLAG_FROM_TEST(marker->flag, select, SELECT);
1378 }
1379 }
1380
1381 WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL);
1382 WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL);
1383
1384 return 1;
1385 }
1386
MARKER_OT_select_box(wmOperatorType * ot)1387 static void MARKER_OT_select_box(wmOperatorType *ot)
1388 {
1389 /* identifiers */
1390 ot->name = "Marker Box Select";
1391 ot->description = "Select all time markers using box selection";
1392 ot->idname = "MARKER_OT_select_box";
1393
1394 /* api callbacks */
1395 ot->exec = ed_marker_box_select_exec;
1396 ot->invoke = ed_marker_box_select_invoke;
1397 ot->modal = WM_gesture_box_modal;
1398 ot->cancel = WM_gesture_box_cancel;
1399
1400 ot->poll = ed_markers_poll_markers_exist;
1401
1402 /* flags */
1403 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1404
1405 /* properties */
1406 WM_operator_properties_gesture_box(ot);
1407 WM_operator_properties_select_operation_simple(ot);
1408
1409 PropertyRNA *prop = RNA_def_boolean(
1410 ot->srna, "tweak", 0, "Tweak", "Operator has been activated using a tweak event");
1411 RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1412 }
1413
1414 /* *********************** (de)select all ***************** */
1415
ed_marker_select_all_exec(bContext * C,wmOperator * op)1416 static int ed_marker_select_all_exec(bContext *C, wmOperator *op)
1417 {
1418 ListBase *markers = ED_context_get_markers(C);
1419 if (markers == NULL) {
1420 return OPERATOR_CANCELLED;
1421 }
1422
1423 int action = RNA_enum_get(op->ptr, "action");
1424 ED_markers_deselect_all(markers, action);
1425
1426 WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL);
1427 WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL);
1428
1429 return OPERATOR_FINISHED;
1430 }
1431
MARKER_OT_select_all(wmOperatorType * ot)1432 static void MARKER_OT_select_all(wmOperatorType *ot)
1433 {
1434 /* identifiers */
1435 ot->name = "(De)select all Markers";
1436 ot->description = "Change selection of all time markers";
1437 ot->idname = "MARKER_OT_select_all";
1438
1439 /* api callbacks */
1440 ot->exec = ed_marker_select_all_exec;
1441 ot->poll = ed_markers_poll_markers_exist;
1442
1443 /* flags */
1444 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1445
1446 /* rna */
1447 WM_operator_properties_select_all(ot);
1448 }
1449
1450 /* ***************** remove marker *********************** */
1451
1452 /* remove selected TimeMarkers */
ed_marker_delete_exec(bContext * C,wmOperator * UNUSED (op))1453 static int ed_marker_delete_exec(bContext *C, wmOperator *UNUSED(op))
1454 {
1455 ListBase *markers = ED_context_get_markers(C);
1456 TimeMarker *marker, *nmarker;
1457 bool changed = false;
1458
1459 if (markers == NULL) {
1460 return OPERATOR_CANCELLED;
1461 }
1462
1463 for (marker = markers->first; marker; marker = nmarker) {
1464 nmarker = marker->next;
1465 if (marker->flag & SELECT) {
1466 if (marker->prop != NULL) {
1467 IDP_FreePropertyContent(marker->prop);
1468 MEM_freeN(marker->prop);
1469 }
1470 BLI_freelinkN(markers, marker);
1471 changed = true;
1472 }
1473 }
1474
1475 if (changed) {
1476 WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL);
1477 WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL);
1478 }
1479
1480 return OPERATOR_FINISHED;
1481 }
1482
MARKER_OT_delete(wmOperatorType * ot)1483 static void MARKER_OT_delete(wmOperatorType *ot)
1484 {
1485 /* identifiers */
1486 ot->name = "Delete Markers";
1487 ot->description = "Delete selected time marker(s)";
1488 ot->idname = "MARKER_OT_delete";
1489
1490 /* api callbacks */
1491 ot->invoke = WM_operator_confirm;
1492 ot->exec = ed_marker_delete_exec;
1493 ot->poll = ed_markers_poll_selected_no_locked_markers;
1494
1495 /* flags */
1496 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1497 }
1498
1499 /* **************** rename marker ***************** */
1500
1501 /* rename first selected TimeMarker */
ed_marker_rename_exec(bContext * C,wmOperator * op)1502 static int ed_marker_rename_exec(bContext *C, wmOperator *op)
1503 {
1504 TimeMarker *marker = ED_markers_get_first_selected(ED_context_get_markers(C));
1505
1506 if (marker) {
1507 RNA_string_get(op->ptr, "name", marker->name);
1508
1509 WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL);
1510 WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL);
1511
1512 return OPERATOR_FINISHED;
1513 }
1514
1515 return OPERATOR_CANCELLED;
1516 }
1517
ed_marker_rename_invoke(bContext * C,wmOperator * op,const wmEvent * event)1518 static int ed_marker_rename_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1519 {
1520 /* must initialize the marker name first if there is a marker selected */
1521 TimeMarker *marker = ED_markers_get_first_selected(ED_context_get_markers(C));
1522 if (marker) {
1523 RNA_string_set(op->ptr, "name", marker->name);
1524 }
1525
1526 return WM_operator_props_popup_confirm(C, op, event);
1527 }
1528
MARKER_OT_rename(wmOperatorType * ot)1529 static void MARKER_OT_rename(wmOperatorType *ot)
1530 {
1531 /* identifiers */
1532 ot->name = "Rename Marker";
1533 ot->description = "Rename first selected time marker";
1534 ot->idname = "MARKER_OT_rename";
1535
1536 /* api callbacks */
1537 ot->invoke = ed_marker_rename_invoke;
1538 ot->exec = ed_marker_rename_exec;
1539 ot->poll = ed_markers_poll_selected_no_locked_markers;
1540
1541 /* flags */
1542 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1543
1544 /* properties */
1545 ot->prop = RNA_def_string(ot->srna,
1546 "name",
1547 "RenamedMarker",
1548 sizeof(((TimeMarker *)NULL)->name),
1549 "Name",
1550 "New name for marker");
1551 #if 0
1552 RNA_def_boolean(ot->srna,
1553 "ensure_unique",
1554 0,
1555 "Ensure Unique",
1556 "Ensure that new name is unique within collection of markers");
1557 #endif
1558 }
1559
1560 /* **************** make links to scene ***************** */
1561
ed_marker_make_links_scene_exec(bContext * C,wmOperator * op)1562 static int ed_marker_make_links_scene_exec(bContext *C, wmOperator *op)
1563 {
1564 Main *bmain = CTX_data_main(C);
1565 ListBase *markers = ED_context_get_markers(C);
1566 Scene *scene_to = BLI_findlink(&bmain->scenes, RNA_enum_get(op->ptr, "scene"));
1567 TimeMarker *marker, *marker_new;
1568
1569 if (scene_to == NULL) {
1570 BKE_report(op->reports, RPT_ERROR, "Scene not found");
1571 return OPERATOR_CANCELLED;
1572 }
1573
1574 if (scene_to == CTX_data_scene(C)) {
1575 BKE_report(op->reports, RPT_ERROR, "Cannot re-link markers into the same scene");
1576 return OPERATOR_CANCELLED;
1577 }
1578
1579 if (scene_to->toolsettings->lock_markers) {
1580 BKE_report(op->reports, RPT_ERROR, "Target scene has locked markers");
1581 return OPERATOR_CANCELLED;
1582 }
1583
1584 /* copy markers */
1585 for (marker = markers->first; marker; marker = marker->next) {
1586 if (marker->flag & SELECT) {
1587 marker_new = MEM_dupallocN(marker);
1588 marker_new->prev = marker_new->next = NULL;
1589
1590 BLI_addtail(&scene_to->markers, marker_new);
1591 }
1592 }
1593
1594 return OPERATOR_FINISHED;
1595 }
1596
MARKER_OT_make_links_scene(wmOperatorType * ot)1597 static void MARKER_OT_make_links_scene(wmOperatorType *ot)
1598 {
1599 PropertyRNA *prop;
1600
1601 /* identifiers */
1602 ot->name = "Make Links to Scene";
1603 ot->description = "Copy selected markers to another scene";
1604 ot->idname = "MARKER_OT_make_links_scene";
1605
1606 /* api callbacks */
1607 ot->exec = ed_marker_make_links_scene_exec;
1608 ot->invoke = WM_menu_invoke;
1609 ot->poll = ed_markers_poll_selected_markers;
1610
1611 /* flags */
1612 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1613
1614 /* properties */
1615 prop = RNA_def_enum(ot->srna, "scene", DummyRNA_NULL_items, 0, "Scene", "");
1616 RNA_def_enum_funcs(prop, RNA_scene_itemf);
1617 RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
1618 ot->prop = prop;
1619 }
1620
1621 #ifdef DURIAN_CAMERA_SWITCH
1622 /* ******************************* camera bind marker ***************** */
1623
ed_marker_camera_bind_exec(bContext * C,wmOperator * op)1624 static int ed_marker_camera_bind_exec(bContext *C, wmOperator *op)
1625 {
1626 bScreen *screen = CTX_wm_screen(C);
1627 Scene *scene = CTX_data_scene(C);
1628 Object *ob = CTX_data_active_object(C);
1629 ListBase *markers = ED_context_get_markers(C);
1630 TimeMarker *marker;
1631
1632 /* Don't do anything if we don't have a camera selected */
1633 if (ob == NULL) {
1634 BKE_report(op->reports, RPT_ERROR, "Select a camera to bind to a marker on this frame");
1635 return OPERATOR_CANCELLED;
1636 }
1637
1638 /* add new marker, unless we already have one on this frame, in which case, replace it */
1639 if (markers == NULL) {
1640 return OPERATOR_CANCELLED;
1641 }
1642
1643 marker = ED_markers_find_nearest_marker(markers, CFRA);
1644 if ((marker == NULL) || (marker->frame != CFRA)) {
1645 marker = MEM_callocN(sizeof(TimeMarker), "Camera TimeMarker");
1646 marker->flag = SELECT;
1647 marker->frame = CFRA;
1648 BLI_addtail(markers, marker);
1649
1650 /* deselect all others, so that the user can then move it without problems */
1651 LISTBASE_FOREACH (TimeMarker *, m, markers) {
1652 if (m != marker) {
1653 m->flag &= ~SELECT;
1654 }
1655 }
1656 }
1657
1658 /* bind to the nominated camera (as set in operator props) */
1659 marker->camera = ob;
1660
1661 /* camera may have changes */
1662 BKE_scene_camera_switch_update(scene);
1663 BKE_screen_view3d_scene_sync(screen, scene);
1664
1665 WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL);
1666 WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL);
1667 WM_event_add_notifier(C, NC_SCENE | NA_EDITED, scene); /* so we get view3d redraws */
1668
1669 return OPERATOR_FINISHED;
1670 }
1671
MARKER_OT_camera_bind(wmOperatorType * ot)1672 static void MARKER_OT_camera_bind(wmOperatorType *ot)
1673 {
1674 /* identifiers */
1675 ot->name = "Bind Camera to Markers";
1676 ot->description = "Bind the selected camera to a marker on the current frame";
1677 ot->idname = "MARKER_OT_camera_bind";
1678
1679 /* api callbacks */
1680 ot->exec = ed_marker_camera_bind_exec;
1681 ot->poll = ED_operator_markers_region_active;
1682
1683 /* flags */
1684 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1685 }
1686 #endif
1687
1688 /* ************************** registration **********************************/
1689
1690 /* called in screen_ops.c:ED_operatortypes_screen() */
ED_operatortypes_marker(void)1691 void ED_operatortypes_marker(void)
1692 {
1693 WM_operatortype_append(MARKER_OT_add);
1694 WM_operatortype_append(MARKER_OT_move);
1695 WM_operatortype_append(MARKER_OT_duplicate);
1696 WM_operatortype_append(MARKER_OT_select);
1697 WM_operatortype_append(MARKER_OT_select_box);
1698 WM_operatortype_append(MARKER_OT_select_all);
1699 WM_operatortype_append(MARKER_OT_delete);
1700 WM_operatortype_append(MARKER_OT_rename);
1701 WM_operatortype_append(MARKER_OT_make_links_scene);
1702 #ifdef DURIAN_CAMERA_SWITCH
1703 WM_operatortype_append(MARKER_OT_camera_bind);
1704 #endif
1705 }
1706
1707 /* called in screen_ops.c:ED_keymap_screen() */
ED_keymap_marker(wmKeyConfig * keyconf)1708 void ED_keymap_marker(wmKeyConfig *keyconf)
1709 {
1710 WM_keymap_ensure(keyconf, "Markers", 0, 0);
1711 }
1712