Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/transform/transform_mode_edge_seq_slide.c
| Show All 17 Lines | |||||
| */ | */ | ||||
| /** \file | /** \file | ||||
| * \ingroup edtransform | * \ingroup edtransform | ||||
| */ | */ | ||||
| #include <stdlib.h> | #include <stdlib.h> | ||||
| #include "MEM_guardedalloc.h" | |||||
| #include "BLI_blenlib.h" | |||||
| #include "BLI_math.h" | #include "BLI_math.h" | ||||
| #include "BLI_string.h" | |||||
| #include "BKE_context.h" | #include "BKE_context.h" | ||||
| #include "BKE_unit.h" | #include "BKE_unit.h" | ||||
| #include "ED_screen.h" | #include "ED_screen.h" | ||||
| #include "WM_api.h" | #include "WM_api.h" | ||||
| #include "WM_types.h" | #include "WM_types.h" | ||||
| #include "UI_interface.h" | #include "UI_interface.h" | ||||
| #include "UI_view2d.h" | #include "UI_view2d.h" | ||||
| #include "SEQ_iterator.h" | |||||
| #include "SEQ_sequencer.h" | |||||
| #include "SEQ_time.h" | |||||
| #include "BLT_translation.h" | #include "BLT_translation.h" | ||||
| #include "transform.h" | #include "transform.h" | ||||
| #include "transform_convert.h" | #include "transform_convert.h" | ||||
| #include "transform_mode.h" | #include "transform_mode.h" | ||||
| #include "transform_snap.h" | |||||
| typedef struct TransSeqSnapData { | |||||
mano-wii: A little weird to edit a `tsnap` member out of place.
Maybe it's better to make a dedicated… | |||||
| int *source_snap_points; | |||||
| int source_snap_point_count; | |||||
| int *target_snap_points; | |||||
| int target_snap_point_count; | |||||
| } TransSeqSnapData; | |||||
| typedef struct TransSeqSlideData { | |||||
| TransSeqSnapData *snap_data; | |||||
| struct wmKeyMapItem *kmi; | |||||
| } TransSeqSlideData; | |||||
| /* -------------------------------------------------------------------- */ | |||||
| /** \name Snap sources | |||||
| * \{ */ | |||||
| static int seq_get_snap_source_points_count(SeqCollection *snap_sources) | |||||
| { | |||||
| return SEQ_collection_count(snap_sources) * 2; | |||||
| } | |||||
| static void seq_snap_source_points_alloc(TransSeqSnapData *snap_data, SeqCollection *snap_sources) | |||||
| { | |||||
| const size_t point_count = seq_get_snap_source_points_count(snap_sources); | |||||
| snap_data->source_snap_points = MEM_callocN(sizeof(int) * point_count, __func__); | |||||
| memset(snap_data->source_snap_points, 0, sizeof(int)); | |||||
| snap_data->source_snap_point_count = point_count; | |||||
| } | |||||
| static int cmp_fn(const void *a, const void *b) | |||||
| { | |||||
| return (*(int *)a - *(int *)b); | |||||
| } | |||||
| static void seq_snap_source_points_build(const TransInfo *t, | |||||
| TransSeqSnapData *snap_data, | |||||
| SeqCollection *snap_sources) | |||||
| { | |||||
| int i = 0; | |||||
| Sequence *seq; | |||||
| SEQ_ITERATOR_FOREACH (seq, snap_sources) { | |||||
| int left = 0, right = 0; | |||||
| if (seq->flag & SEQ_LEFTSEL) { | |||||
| left = right = seq->startdisp; | |||||
| } | |||||
| else if (seq->flag & SEQ_RIGHTSEL) { | |||||
| left = right = seq->enddisp; | |||||
| } | |||||
| else { | |||||
| left = seq->startdisp; | |||||
| right = seq->enddisp; | |||||
| } | |||||
| snap_data->source_snap_points[i] = left; | |||||
| snap_data->source_snap_points[i + 1] = right; | |||||
| i += 2; | |||||
| BLI_assert(i <= snap_data->source_snap_point_count); | |||||
| } | |||||
| qsort(snap_data->source_snap_points, snap_data->source_snap_point_count, sizeof(int), cmp_fn); | |||||
| } | |||||
| /** \} */ | |||||
| /* -------------------------------------------------------------------- */ | |||||
| /** \name Snap targets | |||||
| * \{ */ | |||||
| static SeqCollection *query_snap_targets(const TransInfo *t) | |||||
| { | |||||
| const ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene, false)); | |||||
| const eSeqSnapFlag snap_flag = SEQ_tool_settings_snap_flag_get(t->scene); | |||||
| SeqCollection *collection = SEQ_collection_create(); | |||||
| LISTBASE_FOREACH (Sequence *, seq, seqbase) { | |||||
| if ((seq->flag & SELECT)) { | |||||
| continue; /* Selected are being transformed. */ | |||||
| } | |||||
| if ((seq->flag & SEQ_MUTE) && (snap_flag & SEQ_SNAP_IGNORE_MUTED)) { | |||||
| continue; | |||||
| } | |||||
| if (seq->type == SEQ_TYPE_SOUND_RAM && (snap_flag & SEQ_SNAP_IGNORE_SOUND)) { | |||||
| continue; | |||||
| } | |||||
| SEQ_collection_append_strip(seq, collection); | |||||
| } | |||||
| return collection; | |||||
| } | |||||
| static int seq_get_snap_target_points_count(const TransInfo *t, | |||||
| TransSeqSnapData *snap_data, | |||||
| SeqCollection *snap_targets) | |||||
| { | |||||
| const eSeqSnapFlag snap_flag = SEQ_tool_settings_snap_flag_get(t->scene); | |||||
| int count = 2; /* Strip start and end are always used. */ | |||||
| if (snap_flag & SEQ_SNAP_TO_STRIP_HOLD) { | |||||
| count += 2; | |||||
| } | |||||
| count *= SEQ_collection_count(snap_targets); | |||||
| if (snap_flag & SEQ_SNAP_TO_PLAYHEAD) { | |||||
| count++; | |||||
| } | |||||
| return count; | |||||
| } | |||||
| static void seq_snap_target_points_alloc(const TransInfo *t, | |||||
| TransSeqSnapData *snap_data, | |||||
| SeqCollection *snap_targets) | |||||
| { | |||||
| const size_t point_count = seq_get_snap_target_points_count(t, snap_data, snap_targets); | |||||
| snap_data->target_snap_points = MEM_callocN(sizeof(int) * point_count, __func__); | |||||
| memset(snap_data->target_snap_points, 0, sizeof(int)); | |||||
| snap_data->target_snap_point_count = point_count; | |||||
| } | |||||
| static void seq_snap_target_points_build(const TransInfo *t, | |||||
| TransSeqSnapData *snap_data, | |||||
| SeqCollection *snap_targets) | |||||
| { | |||||
| const Scene *scene = t->scene; | |||||
| const eSeqSnapFlag snap_flag = SEQ_tool_settings_snap_flag_get(t->scene); | |||||
| int i = 0; | |||||
| if (snap_flag & SEQ_SNAP_TO_PLAYHEAD) { | |||||
| snap_data->target_snap_points[i] = CFRA; | |||||
| i++; | |||||
| } | |||||
| Sequence *seq; | |||||
| SEQ_ITERATOR_FOREACH (seq, snap_targets) { | |||||
| snap_data->target_snap_points[i] = seq->startdisp; | |||||
| snap_data->target_snap_points[i + 1] = seq->enddisp; | |||||
| i += 2; | |||||
| if (snap_flag & SEQ_SNAP_TO_STRIP_HOLD) { | |||||
| int content_start = min_ii(seq->enddisp, seq->start); | |||||
| int content_end = max_ii(seq->startdisp, seq->start + seq->len); | |||||
| if (seq->anim_startofs == 0) { | |||||
| content_start = seq->startdisp; | |||||
| } | |||||
| if (seq->anim_endofs == 0) { | |||||
| content_end = seq->enddisp; | |||||
| } | |||||
| snap_data->target_snap_points[i] = content_start; | |||||
| snap_data->target_snap_points[i + 1] = content_end; | |||||
| i += 2; | |||||
| } | |||||
| } | |||||
| BLI_assert(i <= snap_data->target_snap_point_count); | |||||
| qsort(snap_data->target_snap_points, snap_data->target_snap_point_count, sizeof(int), cmp_fn); | |||||
| } | |||||
| /** \} */ | |||||
| /* -------------------------------------------------------------------- */ | |||||
| /** \name Snap utilities | |||||
| * \{ */ | |||||
| static bool seq_snap_use_snapping(const TransInfo *t) | |||||
| { | |||||
| if ((t->flag & T_MODAL) == 0) { | |||||
| return false; | |||||
| } | |||||
| const eSeqSnapFlag snap_flag = SEQ_tool_settings_snap_flag_get(t->scene); | |||||
| return (bool)(snap_flag & SEQ_USE_SNAPPING) ^ (bool)(t->modifiers & MOD_SNAP_INVERT); | |||||
| } | |||||
| static int seq_snap_threshold_get_frame_distance(const TransInfo *t) | |||||
| { | |||||
| const int snap_distance = SEQ_tool_settings_snap_distance_get(t->scene); | |||||
| const struct View2D *v2d = &t->region->v2d; | |||||
| return round_fl_to_int(UI_view2d_region_to_view_x(v2d, snap_distance) - | |||||
| UI_view2d_region_to_view_x(v2d, 0)); | |||||
| } | |||||
| static void transform_snap_sequencer_apply(TransInfo *t) | |||||
| { | |||||
| if (!seq_snap_use_snapping(t)) { | |||||
| return; | |||||
| } | |||||
| const TransSeqSlideData *custom_data = t->custom.mode.data; | |||||
| TransSeqSnapData *snap_data = custom_data->snap_data; | |||||
| t->tsnap.status = 0; | |||||
| int best_dist = MAXFRAME, best_target_frame = 0, best_source_frame = 0; | |||||
| for (int i = 0; i < snap_data->source_snap_point_count; i++) { | |||||
| int snap_source_frame = snap_data->source_snap_points[i] + round_fl_to_int(t->values[0]); | |||||
| for (int j = 0; j < snap_data->target_snap_point_count; j++) { | |||||
| int snap_target_frame = snap_data->target_snap_points[j]; | |||||
| int dist = abs(snap_target_frame - snap_source_frame); | |||||
| if (dist > best_dist) { | |||||
| continue; | |||||
| } | |||||
| best_dist = dist; | |||||
| best_target_frame = snap_target_frame; | |||||
| best_source_frame = snap_source_frame; | |||||
| } | |||||
| } | |||||
| if (best_dist > seq_snap_threshold_get_frame_distance(t)) { | |||||
| return; | |||||
| } | |||||
| t->tsnap.status = POINT_INIT | TARGET_INIT; // ??? | |||||
| t->tsnap.snapPoint[0] = best_target_frame; | |||||
| t->values[0] += best_target_frame - best_source_frame; | |||||
| } | |||||
| static TransSeqSnapData *transform_snap_sequencer_data_alloc(const TransInfo *t) | |||||
| { | |||||
| if (!seq_snap_use_snapping(t)) { | |||||
| return NULL; | |||||
| } | |||||
| TransSeqSnapData *snap_data = MEM_callocN(sizeof(TransSeqSnapData), __func__); | |||||
| ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene, false)); | |||||
| /* Build arrays of snap points. */ | |||||
| SeqCollection *snap_sources = SEQ_query_selected_strips(seqbase); | |||||
| seq_snap_source_points_alloc(snap_data, snap_sources); | |||||
| seq_snap_source_points_build(t, snap_data, snap_sources); | |||||
| SEQ_collection_free(snap_sources); | |||||
| SeqCollection *snap_targets = query_snap_targets(t); | |||||
| seq_snap_target_points_alloc(t, snap_data, snap_targets); | |||||
| seq_snap_target_points_build(t, snap_data, snap_targets); | |||||
| SEQ_collection_free(snap_targets); | |||||
| return snap_data; | |||||
| } | |||||
| static void transform_snap_sequencer_data_free(TransSeqSnapData *data) | |||||
| { | |||||
| if (data == NULL) { | |||||
| return; | |||||
| } | |||||
| MEM_freeN(data->source_snap_points); | |||||
| MEM_freeN(data->target_snap_points); | |||||
| } | |||||
| /** \} */ | |||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /** \name Transform (Sequencer Slide) | /** \name Transform (Sequencer Slide) | ||||
| * \{ */ | * \{ */ | ||||
| static eRedrawFlag seq_slide_handleEvent(struct TransInfo *t, const wmEvent *event) | static eRedrawFlag seq_slide_handleEvent(struct TransInfo *t, const wmEvent *event) | ||||
| { | { | ||||
| BLI_assert(t->mode == TFM_SEQ_SLIDE); | BLI_assert(t->mode == TFM_SEQ_SLIDE); | ||||
| const wmKeyMapItem *kmi = t->custom.mode.data; | const TransSeqSlideData *custom_data = t->custom.mode.data; | ||||
| const wmKeyMapItem *kmi = custom_data->kmi; | |||||
| if (kmi && event->type == kmi->type && event->val == kmi->val) { | if (kmi && event->type == kmi->type && event->val == kmi->val) { | ||||
| /* Allows the "Expand to Fit" effect to be enabled as a toggle. */ | /* Allows the "Expand to Fit" effect to be enabled as a toggle. */ | ||||
| t->flag ^= T_ALT_TRANSFORM; | t->flag ^= T_ALT_TRANSFORM; | ||||
| return TREDRAW_HARD; | return TREDRAW_HARD; | ||||
| } | } | ||||
| return TREDRAW_NOTHING; | return TREDRAW_NOTHING; | ||||
| } | } | ||||
| static void headerSeqSlide(TransInfo *t, const float val[2], char str[UI_MAX_DRAW_STR]) | static void headerSeqSlide(TransInfo *t, const float val[2], char str[UI_MAX_DRAW_STR]) | ||||
| { | { | ||||
| char tvec[NUM_STR_REP_LEN * 3]; | char tvec[NUM_STR_REP_LEN * 3]; | ||||
| size_t ofs = 0; | size_t ofs = 0; | ||||
| if (hasNumInput(&t->num)) { | if (hasNumInput(&t->num)) { | ||||
| outputNumInput(&(t->num), tvec, &t->scene->unit); | outputNumInput(&(t->num), tvec, &t->scene->unit); | ||||
| } | } | ||||
| else { | else { | ||||
| BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.0f, %.0f", val[0], val[1]); | BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.0f, %.0f", val[0], val[1]); | ||||
| } | } | ||||
| ofs += BLI_snprintf_rlen( | ofs += BLI_snprintf_rlen( | ||||
| str + ofs, UI_MAX_DRAW_STR - ofs, TIP_("Sequence Slide: %s%s, ("), &tvec[0], t->con.text); | str + ofs, UI_MAX_DRAW_STR - ofs, TIP_("Sequence Slide: %s%s, ("), &tvec[0], t->con.text); | ||||
| const wmKeyMapItem *kmi = t->custom.mode.data; | const TransSeqSlideData *custom_data = t->custom.mode.data; | ||||
| const wmKeyMapItem *kmi = custom_data->kmi; | |||||
| if (kmi) { | if (kmi) { | ||||
| ofs += WM_keymap_item_to_string(kmi, false, str + ofs, UI_MAX_DRAW_STR - ofs); | ofs += WM_keymap_item_to_string(kmi, false, str + ofs, UI_MAX_DRAW_STR - ofs); | ||||
| } | } | ||||
| ofs += BLI_snprintf_rlen(str + ofs, | ofs += BLI_snprintf_rlen(str + ofs, | ||||
| UI_MAX_DRAW_STR - ofs, | UI_MAX_DRAW_STR - ofs, | ||||
| TIP_(" or Alt) Expand to fit %s"), | TIP_(" or Alt) Expand to fit %s"), | ||||
| WM_bool_as_string((t->flag & T_ALT_TRANSFORM) != 0)); | WM_bool_as_string((t->flag & T_ALT_TRANSFORM) != 0)); | ||||
| Show All 10 Lines | for (i = 0; i < tc->data_len; i++, td++) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| madd_v2_v2v2fl(td->loc, td->iloc, val, td->factor); | madd_v2_v2v2fl(td->loc, td->iloc, val, td->factor); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| static void applySeqSlide(TransInfo *t, const int mval[2]) | static void applySeqSlide(TransInfo *t, const int UNUSED(mval[2])) | ||||
| { | { | ||||
| char str[UI_MAX_DRAW_STR]; | char str[UI_MAX_DRAW_STR]; | ||||
| float values_final[3] = {0.0f}; | float values_final[3] = {0.0f}; | ||||
| snapSequenceBounds(t, mval); | transform_snap_sequencer_apply(t); | ||||
| transform_convert_sequencer_channel_clamp(t); | transform_convert_sequencer_channel_clamp(t); | ||||
| if (applyNumInput(&t->num, values_final)) { | if (applyNumInput(&t->num, values_final)) { | ||||
| if (t->con.mode & CON_APPLY) { | if (t->con.mode & CON_APPLY) { | ||||
| if (t->con.mode & CON_AXIS0) { | if (t->con.mode & CON_AXIS0) { | ||||
| mul_v2_v2fl(values_final, t->spacemtx[0], values_final[0]); | mul_v2_v2fl(values_final, t->spacemtx[0], values_final[0]); | ||||
| } | } | ||||
| else { | else { | ||||
| mul_v2_v2fl(values_final, t->spacemtx[1], values_final[0]); | mul_v2_v2fl(values_final, t->spacemtx[1], values_final[0]); | ||||
| Show All 14 Lines | static void applySeqSlide(TransInfo *t, const int UNUSED(mval[2])) | ||||
| headerSeqSlide(t, t->values_final, str); | headerSeqSlide(t, t->values_final, str); | ||||
| applySeqSlideValue(t, t->values_final); | applySeqSlideValue(t, t->values_final); | ||||
| recalcData(t); | recalcData(t); | ||||
| ED_area_status_text(t->area, str); | ED_area_status_text(t->area, str); | ||||
| } | } | ||||
| static TransSeqSlideData *seq_slide_customdata_alloc(const TransInfo *t) | |||||
| { | |||||
| TransSeqSlideData *custom_data = MEM_callocN(sizeof(TransSeqSlideData), __func__); | |||||
| /* Workaround to use the same key as the modal keymap. */ | |||||
| if (t->keymap) { | |||||
| custom_data->kmi = (void *)WM_modalkeymap_find_propvalue(t->keymap, TFM_MODAL_TRANSLATE); | |||||
| } | |||||
| custom_data->snap_data = transform_snap_sequencer_data_alloc(t); | |||||
| return custom_data; | |||||
| } | |||||
| static void seq_slide_customdata_free(const TransInfo *UNUSED(t), | |||||
| TransDataContainer *UNUSED(tc), | |||||
| TransCustomData *custom_data) | |||||
| { | |||||
| TransSeqSlideData *data = custom_data->data; | |||||
| transform_snap_sequencer_data_free(data->snap_data); | |||||
| MEM_freeN(data); | |||||
| custom_data->data = NULL; | |||||
| } | |||||
| void initSeqSlide(TransInfo *t) | void initSeqSlide(TransInfo *t) | ||||
| { | { | ||||
| t->transform = applySeqSlide; | t->transform = applySeqSlide; | ||||
| t->handleEvent = seq_slide_handleEvent; | t->handleEvent = seq_slide_handleEvent; | ||||
| initMouseInputMode(t, &t->mouse, INPUT_VECTOR); | initMouseInputMode(t, &t->mouse, INPUT_VECTOR); | ||||
| t->idx_max = 1; | t->idx_max = 1; | ||||
| t->num.flag = 0; | t->num.flag = 0; | ||||
| t->num.idx_max = t->idx_max; | t->num.idx_max = t->idx_max; | ||||
| t->snap[0] = floorf(t->scene->r.frs_sec / t->scene->r.frs_sec_base); | t->snap[0] = floorf(t->scene->r.frs_sec / t->scene->r.frs_sec_base); | ||||
| t->snap[1] = 10.0f; | t->snap[1] = 10.0f; | ||||
| copy_v3_fl(t->num.val_inc, t->snap[0]); | copy_v3_fl(t->num.val_inc, t->snap[0]); | ||||
| t->num.unit_sys = t->scene->unit.system; | t->num.unit_sys = t->scene->unit.system; | ||||
| /* Would be nice to have a time handling in units as well | /* Would be nice to have a time handling in units as well | ||||
| * (supporting frames in addition to "natural" time...). */ | * (supporting frames in addition to "natural" time...). */ | ||||
| t->num.unit_type[0] = B_UNIT_NONE; | t->num.unit_type[0] = B_UNIT_NONE; | ||||
| t->num.unit_type[1] = B_UNIT_NONE; | t->num.unit_type[1] = B_UNIT_NONE; | ||||
| if (t->keymap) { | t->custom.mode.data = seq_slide_customdata_alloc(t); | ||||
| /* Workaround to use the same key as the modal keymap. */ | t->custom.mode.free_cb = seq_slide_customdata_free; | ||||
| t->custom.mode.data = (void *)WM_modalkeymap_find_propvalue(t->keymap, TFM_MODAL_TRANSLATE); | |||||
| } | |||||
| } | } | ||||
| /** \} */ | /** \} */ | ||||
A little weird to edit a tsnap member out of place.
Maybe it's better to make a dedicated drawing function for this mode, as we have the: drawEdgeSlide, drawVertSlide and drawDial3d