diff --git a/scripts/startup/bl_operators/sequencer.py b/scripts/startup/bl_operators/sequencer.py index da74affae0d..19907053bbb 100644 --- a/scripts/startup/bl_operators/sequencer.py +++ b/scripts/startup/bl_operators/sequencer.py @@ -3,7 +3,10 @@ # SPDX-License-Identifier: GPL-2.0-or-later import bpy -from bpy.types import Operator +from bpy.types import ( + FileHandler, + Operator, +) from bpy.props import ( EnumProperty, @@ -373,10 +376,46 @@ def calculate_duration_frames(scene, duration_seconds): return round(duration_seconds * scene.render.fps / scene.render.fps_base) +class SequencerFileHandlerBase: + @classmethod + def poll_drop(cls, context): + return ( + (context.region is not None) and + (context.region.type == 'WINDOW') and + (context.area is not None) and + (context.area.ui_type == 'SEQUENCE_EDITOR') + ) + + +class SEQUENCER_FH_image_strip(FileHandler, SequencerFileHandlerBase): + bl_idname = "SEQUENCER_FH_image_strip" + bl_label = "Image strip" + bl_import_operator = "SEQUENCER_OT_image_strip_add" + bl_file_extensions = ";".join(bpy.path.extensions_image) + + +class SEQUENCER_FH_movie_strip(FileHandler, SequencerFileHandlerBase): + bl_idname = "SEQUENCER_FH_movie_strip" + bl_label = "Movie strip" + bl_import_operator = "SEQUENCER_OT_movie_strip_add" + bl_file_extensions = ";".join(bpy.path.extensions_movie) + + +class SEQUENCER_FH_sound_strip(FileHandler, SequencerFileHandlerBase): + bl_idname = "SEQUENCER_FH_sound_strip" + bl_label = "Sound strip" + bl_import_operator = "SEQUENCER_OT_sound_strip_add" + bl_file_extensions = ";".join(bpy.path.extensions_audio) + + classes = ( SequencerCrossfadeSounds, SequencerSplitMulticam, SequencerDeinterlaceSelectedMovies, SequencerFadesClear, SequencerFadesAdd, + + SEQUENCER_FH_image_strip, + SEQUENCER_FH_movie_strip, + SEQUENCER_FH_sound_strip, ) diff --git a/source/blender/editors/space_sequencer/sequencer_add.cc b/source/blender/editors/space_sequencer/sequencer_add.cc index 075917996eb..d663ae7cbf7 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.cc +++ b/source/blender/editors/space_sequencer/sequencer_add.cc @@ -50,6 +50,7 @@ #include "ED_sequencer.hh" #include "UI_interface.hh" +#include "UI_view2d.hh" #ifdef WITH_AUDASPACE # include @@ -203,11 +204,42 @@ static int sequencer_generic_invoke_xy_guess_channel(bContext *C, int type) return 1; } -static void sequencer_generic_invoke_xy__internal(bContext *C, wmOperator *op, int flag, int type) +/* Sets `channel` and `frame_start` properties when the operator is likely to have been invoked + * with drag-and-drop data. */ +static void sequencer_file_drop_channel_frame_set(bContext *C, + wmOperator *op, + const wmEvent *event) +{ + BLI_assert((RNA_struct_property_is_set(op->ptr, "files") && + !RNA_collection_is_empty(op->ptr, "files")) || + RNA_struct_property_is_set(op->ptr, "filepath")); + + if (RNA_struct_property_is_set(op->ptr, "channel") || + RNA_struct_property_is_set(op->ptr, "frame_start")) + { + return; + } + + ARegion *region = CTX_wm_region(C); + if (!region || region->regiontype != RGN_TYPE_WINDOW) { + return; + } + + float frame_start, channel; + UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &frame_start, &channel); + RNA_int_set(op->ptr, "channel", int(channel)); + RNA_int_set(op->ptr, "frame_start", int(frame_start)); +} + +static void sequencer_generic_invoke_xy__internal( + bContext *C, wmOperator *op, int flag, int type, const wmEvent *event = nullptr) { Scene *scene = CTX_data_scene(C); int timeline_frame = int(scene->r.cfra); + if ((flag & SEQPROP_NOPATHS) && event) { + sequencer_file_drop_channel_frame_set(C, op, event); + } /* Effect strips don't need a channel initialized from the mouse. */ if (!(flag & SEQPROP_NOCHAN) && RNA_struct_property_is_set(op->ptr, "channel") == 0) { @@ -964,7 +996,7 @@ static int sequencer_add_movie_strip_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -static int sequencer_add_movie_strip_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) +static int sequencer_add_movie_strip_invoke(bContext *C, wmOperator *op, const wmEvent *event) { PropertyRNA *prop; Scene *scene = CTX_data_scene(C); @@ -979,7 +1011,7 @@ static int sequencer_add_movie_strip_invoke(bContext *C, wmOperator *op, const w !RNA_collection_is_empty(op->ptr, "files")) || RNA_struct_property_is_set(op->ptr, "filepath")) { - sequencer_generic_invoke_xy__internal(C, op, SEQPROP_NOPATHS, SEQ_TYPE_MOVIE); + sequencer_generic_invoke_xy__internal(C, op, SEQPROP_NOPATHS, SEQ_TYPE_MOVIE, event); return sequencer_add_movie_strip_exec(C, op); } @@ -1132,14 +1164,14 @@ static int sequencer_add_sound_strip_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -static int sequencer_add_sound_strip_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) +static int sequencer_add_sound_strip_invoke(bContext *C, wmOperator *op, const wmEvent *event) { /* This is for drag and drop. */ if ((RNA_struct_property_is_set(op->ptr, "files") && !RNA_collection_is_empty(op->ptr, "files")) || RNA_struct_property_is_set(op->ptr, "filepath")) { - sequencer_generic_invoke_xy__internal(C, op, SEQPROP_NOPATHS, SEQ_TYPE_SOUND_RAM); + sequencer_generic_invoke_xy__internal(C, op, SEQPROP_NOPATHS, SEQ_TYPE_SOUND_RAM, event); return sequencer_add_sound_strip_exec(C, op); } @@ -1320,7 +1352,7 @@ static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -static int sequencer_add_image_strip_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) +static int sequencer_add_image_strip_invoke(bContext *C, wmOperator *op, const wmEvent *event) { PropertyRNA *prop; Scene *scene = CTX_data_scene(C); @@ -1332,7 +1364,7 @@ static int sequencer_add_image_strip_invoke(bContext *C, wmOperator *op, const w /* Name set already by drag and drop. */ if (RNA_struct_property_is_set(op->ptr, "files") && !RNA_collection_is_empty(op->ptr, "files")) { sequencer_generic_invoke_xy__internal( - C, op, SEQPROP_ENDFRAME | SEQPROP_NOPATHS, SEQ_TYPE_IMAGE); + C, op, SEQPROP_ENDFRAME | SEQPROP_NOPATHS, SEQ_TYPE_IMAGE, event); return sequencer_add_image_strip_exec(C, op); } diff --git a/source/blender/editors/space_sequencer/sequencer_drag_drop.cc b/source/blender/editors/space_sequencer/sequencer_drag_drop.cc index 7a5e9fda7f1..fcc73e17fb3 100644 --- a/source/blender/editors/space_sequencer/sequencer_drag_drop.cc +++ b/source/blender/editors/space_sequencer/sequencer_drag_drop.cc @@ -12,9 +12,11 @@ #include "DNA_sound_types.h" #include "BLI_blenlib.h" +#include "BLI_string_ref.hh" #include "BLI_string_utils.hh" #include "BKE_context.hh" +#include "BKE_file_handler.hh" #include "BKE_image.h" #include "BKE_main.hh" @@ -75,11 +77,27 @@ static void generic_poll_operations(const wmEvent *event, uint8_t type) g_drop_coords.use_snapping = event->modifier & KM_CTRL; } -static bool image_drop_poll(bContext * /*C*/, wmDrag *drag, const wmEvent *event) +/* While drag-and-drop in the sequencer, the internal drop-box implementation allows to have a drop + * preview of the file dragged. This checks when drag-and-drop is done with a single file, and when + * only a expected `file_handler` can be used, so internal drop-box can be used instead of the + * `file_handler`. */ +static bool test_single_file_handler_poll(const bContext *C, + wmDrag *drag, + blender::StringRef file_handler) +{ + const auto paths = WM_drag_get_paths(drag); + auto file_handlers = blender::bke::file_handlers_poll_file_drop(C, paths); + return paths.size() == 1 && file_handlers.size() == 1 && + file_handler == file_handlers[0]->idname; +} + +static bool image_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { if (drag->type == WM_DRAG_PATH) { const eFileSel_File_Types file_type = eFileSel_File_Types(WM_drag_get_path_file_type(drag)); - if (file_type == FILE_TYPE_IMAGE) { + if (file_type == FILE_TYPE_IMAGE && + test_single_file_handler_poll(C, drag, "SEQUENCER_FH_image_strip")) + { generic_poll_operations(event, TH_SEQ_IMAGE); return true; } @@ -107,9 +125,11 @@ static bool is_movie(wmDrag *drag) return false; } -static bool movie_drop_poll(bContext * /*C*/, wmDrag *drag, const wmEvent *event) +static bool movie_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { - if (is_movie(drag)) { + if (is_movie(drag) && (drag->type != WM_DRAG_PATH || + test_single_file_handler_poll(C, drag, "SEQUENCER_FH_movie_strip"))) + { generic_poll_operations(event, TH_SEQ_MOVIE); return true; } @@ -131,9 +151,11 @@ static bool is_sound(wmDrag *drag) return false; } -static bool sound_drop_poll(bContext * /*C*/, wmDrag *drag, const wmEvent *event) +static bool sound_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { - if (is_sound(drag)) { + if (is_sound(drag) && (drag->type != WM_DRAG_PATH || + test_single_file_handler_poll(C, drag, "SEQUENCER_FH_sound_strip"))) + { generic_poll_operations(event, TH_SEQ_AUDIO); return true; }