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 = &region->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 = &region->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