Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Parborg <darkdefende@gmail.com>2022-04-28 13:50:22 +0300
committerSebastian Parborg <darkdefende@gmail.com>2022-04-28 13:55:51 +0300
commit77794b1a7b99bd689d1d9872c61d7990fbad2ce4 (patch)
tree48d0a00d5497cb94aa44a157af6fee2bffe0758f /source/blender/editors/space_sequencer/sequencer_drag_drop.c
parent16fe767d0090f3ff2b2c45c63cb4a84805991f3a (diff)
VSE: Add precise drag and drop and strip previews
This patch adds the drag and drop strip previews in the VSE. It also adds two new functions to the drag and drop API. 1. "draw_in_view" for callbacks that wants to draw elements in local viewport coordinates 2. "on_drag_start" that can be used for prefetching data only once at the start of the drag. Reviewed By: Julian, Campbell Differential Revision: http://developer.blender.org/D14560
Diffstat (limited to 'source/blender/editors/space_sequencer/sequencer_drag_drop.c')
-rw-r--r--source/blender/editors/space_sequencer/sequencer_drag_drop.c650
1 files changed, 650 insertions, 0 deletions
diff --git a/source/blender/editors/space_sequencer/sequencer_drag_drop.c b/source/blender/editors/space_sequencer/sequencer_drag_drop.c
new file mode 100644
index 00000000000..639d3651714
--- /dev/null
+++ b/source/blender/editors/space_sequencer/sequencer_drag_drop.c
@@ -0,0 +1,650 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2022 Blender Foundation. All rights reserved. */
+
+/** \file
+ * \ingroup spseq
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_scene_types.h"
+#include "DNA_sound_types.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_string_utils.h"
+
+#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_image.h"
+#include "BKE_main.h"
+
+#include "SEQ_channels.h"
+#include "SEQ_iterator.h"
+#include "SEQ_sequencer.h"
+#include "SEQ_transform.h"
+
+#include "UI_resources.h"
+#include "UI_view2d.h"
+
+#include "GPU_immediate.h"
+#include "GPU_matrix.h"
+
+#include "ED_screen.h"
+#include "ED_transform.h"
+
+#include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+/* For querying audio files. */
+#ifdef WITH_AUDASPACE
+# include "BKE_sound.h"
+# include <AUD_Sound.h>
+# include <AUD_Special.h>
+#endif
+
+/* Own include. */
+#include "sequencer_intern.h"
+
+typedef struct SeqDropCoords {
+ float start_frame, channel;
+ int strip_len, channel_len;
+ bool in_use;
+ bool is_intersecting;
+ bool use_snapping;
+ float snap_point_x;
+ uint8_t type;
+} SeqDropCoords;
+
+/* The current drag and drop API doesn't allow us to easily pass along the
+ * required custom data to all callbacks that need it. Especially when
+ * preloading data on drag start.
+ * Therefore we will for now use a global variable for this.
+ */
+static SeqDropCoords g_drop_coords = {.in_use = false};
+
+static void generic_poll_operations(const wmEvent *event, uint8_t type)
+{
+ g_drop_coords.type = type;
+ /* We purposely ignore the snapping tool setting here as currently other drag&drop operators only
+ * snaps when holding down Ctrl. */
+ g_drop_coords.use_snapping = event->modifier & KM_CTRL;
+}
+
+static bool image_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *event)
+{
+ if (drag->type == WM_DRAG_PATH) {
+ if (ELEM(drag->icon, ICON_FILE_IMAGE, ICON_FILE_BLANK)) { /* Rule might not work? */
+ generic_poll_operations(event, TH_SEQ_IMAGE);
+ return true;
+ }
+ }
+
+ return WM_drag_is_ID_type(drag, ID_IM);
+}
+
+static bool movie_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *event)
+{
+ if (drag->type == WM_DRAG_PATH) {
+ if (ELEM(drag->icon, 0, ICON_FILE_MOVIE, ICON_FILE_BLANK)) { /* Rule might not work? */
+ generic_poll_operations(event, TH_SEQ_MOVIE);
+ return true;
+ }
+ }
+
+ return WM_drag_is_ID_type(drag, ID_MC);
+}
+
+static bool sound_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *event)
+{
+ if (drag->type == WM_DRAG_PATH) {
+ if (ELEM(drag->icon, ICON_FILE_SOUND, ICON_FILE_BLANK)) { /* Rule might not work? */
+ generic_poll_operations(event, TH_SEQ_AUDIO);
+ return true;
+ }
+ }
+
+ return WM_drag_is_ID_type(drag, ID_SO);
+}
+
+static void sequencer_drop_copy(bContext *C, wmDrag *drag, wmDropBox *drop)
+{
+ ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0);
+ /* ID dropped. */
+ if (id != NULL) {
+ const ID_Type id_type = GS(id->name);
+ if (id_type == ID_IM) {
+ Image *ima = (Image *)id;
+ PointerRNA itemptr;
+ char dir[FILE_MAX], file[FILE_MAX];
+ BLI_split_dirfile(ima->filepath, dir, file, sizeof(dir), sizeof(file));
+ RNA_string_set(drop->ptr, "directory", dir);
+ RNA_collection_clear(drop->ptr, "files");
+ RNA_collection_add(drop->ptr, "files", &itemptr);
+ RNA_string_set(&itemptr, "name", file);
+ }
+ else if (id_type == ID_MC) {
+ MovieClip *clip = (MovieClip *)id;
+ RNA_string_set(drop->ptr, "filepath", clip->filepath);
+ RNA_struct_property_unset(drop->ptr, "name");
+ }
+ else if (id_type == ID_SO) {
+ bSound *sound = (bSound *)id;
+ RNA_string_set(drop->ptr, "filepath", sound->filepath);
+ RNA_struct_property_unset(drop->ptr, "name");
+ }
+ }
+ /* Path dropped. */
+ else if (drag->path[0]) {
+ if (RNA_struct_find_property(drop->ptr, "filepath")) {
+ RNA_string_set(drop->ptr, "filepath", drag->path);
+ }
+ if (RNA_struct_find_property(drop->ptr, "directory")) {
+ PointerRNA itemptr;
+ char dir[FILE_MAX], file[FILE_MAX];
+
+ BLI_split_dirfile(drag->path, dir, file, sizeof(dir), sizeof(file));
+
+ RNA_string_set(drop->ptr, "directory", dir);
+
+ RNA_collection_clear(drop->ptr, "files");
+ RNA_collection_add(drop->ptr, "files", &itemptr);
+ RNA_string_set(&itemptr, "name", file);
+ }
+
+ if (g_drop_coords.in_use) {
+ RNA_int_set(drop->ptr, "frame_start", g_drop_coords.start_frame);
+ RNA_int_set(drop->ptr, "channel", g_drop_coords.channel);
+ RNA_boolean_set(drop->ptr, "overlap_shuffle_override", true);
+ }
+ else {
+ Scene *scene = CTX_data_scene(C);
+ Editing *ed = SEQ_editing_get(scene);
+ ListBase *seqbase = SEQ_active_seqbase_get(ed);
+ ListBase *channels = SEQ_channels_displayed_get(ed);
+ SpaceSeq *sseq = CTX_wm_space_seq(C);
+
+ SeqCollection *strips = SEQ_query_rendered_strips(
+ channels, seqbase, scene->r.cfra, sseq->chanshown);
+
+ /* Get the top most strip channel that is in view.*/
+ Sequence *seq;
+ int max_channel = -1;
+ SEQ_ITERATOR_FOREACH (seq, strips) {
+ max_channel = max_ii(seq->machine, max_channel);
+ }
+
+ if (max_channel != -1) {
+ RNA_int_set(drop->ptr, "channel", max_channel);
+ }
+ }
+ }
+}
+
+static void update_overlay_strip_poistion_data(bContext *C, const int mval[2])
+{
+ SeqDropCoords *coords = &g_drop_coords;
+ ARegion *region = CTX_wm_region(C);
+ Scene *scene = CTX_data_scene(C);
+ int hand;
+ View2D *v2d = &region->v2d;
+
+ /* Update the position were we would place the strip if we complete the drag and drop action.
+ */
+ UI_view2d_region_to_view(v2d, mval[0], mval[1], &coords->start_frame, &coords->channel);
+ coords->start_frame = roundf(coords->start_frame);
+ if (coords->channel < 1.0f) {
+ coords->channel = 1;
+ }
+
+ float start_frame = coords->start_frame;
+ float end_frame = coords->start_frame + coords->strip_len;
+
+ if (coords->use_snapping) {
+ /* Do snapping via the exsiting transform code. */
+ int snap_delta;
+ float snap_frame;
+ bool valid_snap;
+
+ valid_snap = ED_transform_snap_sequencer_to_closest_strip_calc(
+ scene, region, start_frame, end_frame, &snap_delta, &snap_frame);
+
+ if (valid_snap) {
+ /* We snapped onto something! */
+ start_frame += snap_delta;
+ coords->start_frame = start_frame;
+ end_frame = start_frame + coords->strip_len;
+ coords->snap_point_x = snap_frame;
+ }
+ else {
+ /* Nothing was snapped to, disable snap drawing. */
+ coords->use_snapping = false;
+ }
+ }
+
+ if (coords->strip_len < 1) {
+ /* Only check if there is a strip already under the mouse cursor. */
+ coords->is_intersecting = find_nearest_seq(scene, &region->v2d, &hand, mval);
+ }
+ else {
+ /* Check if there is a strip that would intersect with the new strip(s). */
+ coords->is_intersecting = false;
+ Sequence dummy_seq = {.machine = coords->channel,
+ .startdisp = coords->start_frame,
+ .enddisp = coords->start_frame + coords->strip_len};
+ Editing *ed = SEQ_editing_get(scene);
+
+ for (int i = 0; i < coords->channel_len && !coords->is_intersecting; i++) {
+ coords->is_intersecting = SEQ_transform_test_overlap(ed->seqbasep, &dummy_seq);
+ dummy_seq.machine++;
+ }
+ }
+}
+
+static void draw_seq_in_view(bContext *C, wmWindow *UNUSED(win), wmDrag *drag, const int xy[2])
+{
+ SeqDropCoords *coords = &g_drop_coords;
+ if (!coords->in_use) {
+ return;
+ }
+
+ ARegion *region = CTX_wm_region(C);
+ int mval[2];
+ /* Convert mouse coordinates to region local coordinates. */
+ mval[0] = xy[0] - region->winrct.xmin;
+ mval[1] = xy[1] - region->winrct.ymin;
+
+ update_overlay_strip_poistion_data(C, mval);
+
+ GPU_matrix_push();
+ UI_view2d_view_ortho(&region->v2d);
+
+ /* Sometimes the active theme is not the sequencer theme, e.g. when an operator invokes the
+ * file browser. This makes sure we get the right color values for the theme. */
+ struct bThemeState theme_state;
+ UI_Theme_Store(&theme_state);
+ UI_SetTheme(SPACE_SEQ, RGN_TYPE_WINDOW);
+
+ if (coords->use_snapping) {
+ ED_draw_sequencer_snap_point(C, coords->snap_point_x);
+ }
+
+ /* Init GPU drawing. */
+ GPU_line_width(2.0f);
+ GPU_blend(GPU_BLEND_ALPHA);
+ GPU_line_smooth(true);
+ uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
+ immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
+
+ /* Draw strips. The code here is taken from sequencer_draw. */
+ float x1 = coords->start_frame;
+ float x2 = coords->start_frame + coords->strip_len;
+ float strip_color[3];
+ uchar text_color[4] = {255, 255, 255, 255};
+ float pixelx = BLI_rctf_size_x(&region->v2d.cur) / BLI_rcti_size_x(&region->v2d.mask);
+ float pixely = BLI_rctf_size_y(&region->v2d.cur) / BLI_rcti_size_y(&region->v2d.mask);
+
+ for (int i = 0; i < coords->channel_len; i++) {
+ float y1 = floorf(coords->channel) + i + SEQ_STRIP_OFSBOTTOM;
+ float y2 = floorf(coords->channel) + i + SEQ_STRIP_OFSTOP;
+
+ if (coords->type == TH_SEQ_MOVIE && i == 0 && coords->channel_len > 1) {
+ /* Assume only video strips occupies two channels.
+ * One for video and the other for audio.
+ * The audio channel is added first.
+ */
+ UI_GetThemeColor3fv(TH_SEQ_AUDIO, strip_color);
+ }
+ else {
+ UI_GetThemeColor3fv(coords->type, strip_color);
+ }
+
+ immUniformColor3fvAlpha(strip_color, 0.8f);
+ immRectf(pos, x1, y1, x2, y2);
+
+ if (coords->is_intersecting) {
+ strip_color[0] = 1.0f;
+ strip_color[1] = strip_color[2] = 0.3f;
+ }
+ else {
+ if (coords->channel_len - 1 == i) {
+ text_color[0] = text_color[1] = text_color[2] = 255;
+ UI_GetThemeColor3fv(TH_SEQ_ACTIVE, strip_color);
+ }
+ else {
+ text_color[0] = text_color[1] = text_color[2] = 10;
+ UI_GetThemeColor3fv(TH_SEQ_SELECTED, strip_color);
+ }
+ }
+
+ /* Draw a 2 pixel border around the strip. */
+ immUniformColor3fvAlpha(strip_color, 0.8f);
+ /* Left */
+ immRectf(pos, x1 - pixelx, y1, x1 + pixelx, y2);
+ /* Bottom */
+ immRectf(pos, x1 - pixelx, y1, x2 + pixelx, y1 + 2 * pixely);
+ /* Right */
+ immRectf(pos, x2 - pixelx, y1, x2 + pixelx, y2);
+ /* Top */
+ immRectf(pos, x1 - pixelx, y2 - 2 * pixely, x2 + pixelx, y2);
+
+ float handle_size = 8.0f; /* SEQ_HANDLE_SIZE */
+
+ /* Calculate height needed for drawing text on strip. */
+ float text_margin_y = y2 - min_ff(0.40f, 20 * U.dpi_fac * pixely);
+ float text_margin_x = 2.0f * (pixelx * handle_size) * U.pixelsize;
+
+ rctf rect;
+ rect.xmin = x1 + text_margin_x;
+ rect.ymin = text_margin_y;
+ rect.xmax = x2 - text_margin_x;
+ rect.ymax = y2;
+
+ if (rect.xmax <= rect.xmin) {
+ /* Exit early and skip text drawing if the strip doesn't have any space to put the text
+ * into.
+ */
+ break;
+ }
+
+ SpaceSeq *sseq = CTX_wm_space_seq(C);
+ const char *text_sep = " | ";
+ const char *text_array[5];
+ char text_display[FILE_MAX];
+ char filename[FILE_MAX];
+ char rel_path[FILE_MAX];
+ char strip_duration_text[16];
+ int len_text_arr = 0;
+
+ if (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_NAME) {
+ BLI_split_file_part(drag->path, filename, FILE_MAX);
+ text_array[len_text_arr++] = filename;
+ }
+
+ if (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_SOURCE) {
+ Main *bmain = CTX_data_main(C);
+ BLI_strncpy(rel_path, drag->path, FILE_MAX);
+ BLI_path_rel(rel_path, BKE_main_blendfile_path(bmain));
+ text_array[len_text_arr++] = text_sep;
+ text_array[len_text_arr++] = rel_path;
+ }
+
+ if (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_DURATION) {
+ SNPRINTF(strip_duration_text, "%d", (int)(x2 - x1));
+ text_array[len_text_arr++] = text_sep;
+ text_array[len_text_arr++] = strip_duration_text;
+ }
+
+ BLI_assert(len_text_arr <= ARRAY_SIZE(text_array));
+
+ BLI_string_join_array(text_display, FILE_MAX, text_array, len_text_arr);
+
+ UI_view2d_text_cache_add_rectf(
+ &region->v2d, &rect, text_display, strlen(text_display), text_color);
+ }
+
+ /* Clean after drawing up. */
+ UI_Theme_Restore(&theme_state);
+ GPU_matrix_pop();
+ immUnbindProgram();
+ GPU_blend(GPU_BLEND_NONE);
+ GPU_line_smooth(false);
+
+ UI_view2d_text_cache_draw(region);
+}
+
+static bool generic_drop_draw_handling(struct wmDropBox *drop)
+{
+ SeqDropCoords *coords = drop->draw_data;
+ if (coords && coords->in_use) {
+ return true;
+ }
+
+ coords = drop->draw_data = &g_drop_coords;
+ coords->in_use = true;
+
+ return false;
+}
+
+typedef struct DropJobData {
+ char path[FILE_MAX];
+ bool only_audio;
+ float scene_fps;
+} DropJobData;
+
+static void prefetch_data_fn(void *custom_data,
+ short *UNUSED(stop),
+ short *UNUSED(do_update),
+ float *UNUSED(progress))
+{
+ DropJobData *job_data = (DropJobData *)custom_data;
+
+ if (job_data->only_audio) {
+#ifdef WITH_AUDASPACE
+ /* Get the sound file length */
+ AUD_Sound *sound = AUD_Sound_file(job_data->path);
+ if (sound != NULL) {
+
+ AUD_SoundInfo info = AUD_getInfo(sound);
+ if ((eSoundChannels)info.specs.channels != SOUND_CHANNELS_INVALID) {
+ g_drop_coords.strip_len = max_ii(1, round((info.length) * job_data->scene_fps));
+ }
+ AUD_Sound_free(sound);
+ return;
+ }
+#endif
+ }
+
+ char colorspace[64] = "\0"; /* 64 == MAX_COLORSPACE_NAME length. */
+ struct anim *anim = openanim(job_data->path, IB_rect, 0, colorspace);
+
+ if (anim != NULL) {
+ g_drop_coords.strip_len = IMB_anim_get_duration(anim, IMB_TC_NONE);
+ IMB_free_anim(anim);
+#ifdef WITH_AUDASPACE
+ /* Try to load sound and see if the video has a sound channel. */
+ AUD_Sound *sound = AUD_Sound_file(job_data->path);
+ if (sound != NULL) {
+
+ AUD_SoundInfo info = AUD_getInfo(sound);
+ if ((eSoundChannels)info.specs.channels != SOUND_CHANNELS_INVALID) {
+ g_drop_coords.channel_len = 2;
+ }
+ AUD_Sound_free(sound);
+ }
+#endif
+ }
+}
+
+static void free_prefetch_data_fn(void *custom_data)
+{
+ DropJobData *job_data = (DropJobData *)custom_data;
+ MEM_freeN(job_data);
+}
+
+static void start_audio_video_job(bContext *C, char *path, bool only_audio)
+{
+ g_drop_coords.strip_len = 0;
+ g_drop_coords.channel_len = 1;
+
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmWindow *win = CTX_wm_window(C);
+ Scene *scene = CTX_data_scene(C);
+
+ wmJob *wm_job = WM_jobs_get(
+ wm, win, NULL, "Load Previews", 0, WM_JOB_TYPE_SEQ_DRAG_DROP_PREVIEW);
+
+ DropJobData *job_data = (DropJobData *)MEM_mallocN(sizeof(DropJobData),
+ "SeqDragDropPreviewData");
+
+ BLI_strncpy(job_data->path, path, FILE_MAX);
+ job_data->only_audio = only_audio;
+ job_data->scene_fps = FPS;
+
+ WM_jobs_customdata_set(wm_job, job_data, free_prefetch_data_fn);
+ WM_jobs_timer(wm_job, 0.1, NC_WINDOW, NC_WINDOW);
+ WM_jobs_callbacks(wm_job, prefetch_data_fn, NULL, NULL, NULL);
+
+ WM_jobs_start(wm, wm_job);
+}
+
+static void video_prefetch(bContext *C, wmDrag *drag)
+{
+ if (drag->type == WM_DRAG_PATH && ELEM(drag->icon, ICON_FILE_MOVIE, ICON_FILE_BLANK)) {
+ start_audio_video_job(C, drag->path, false);
+ }
+}
+
+static void audio_prefetch(bContext *C, wmDrag *drag)
+{
+ if (drag->type == WM_DRAG_PATH && ELEM(drag->icon, ICON_FILE_SOUND, ICON_FILE_BLANK)) {
+ start_audio_video_job(C, drag->path, true);
+ }
+}
+
+static void movie_drop_draw_activate(struct wmDropBox *drop, wmDrag *UNUSED(drag))
+{
+ if (generic_drop_draw_handling(drop)) {
+ return;
+ }
+}
+
+static void sound_drop_draw_activate(struct wmDropBox *drop, wmDrag *UNUSED(drag))
+{
+ if (generic_drop_draw_handling(drop)) {
+ return;
+ }
+}
+
+static void image_drop_draw_activate(struct wmDropBox *drop, wmDrag *UNUSED(drag))
+{
+ if (generic_drop_draw_handling(drop)) {
+ return;
+ }
+
+ SeqDropCoords *coords = drop->draw_data;
+ coords->strip_len = DEFAULT_IMG_STRIP_LENGTH;
+ coords->channel_len = 1;
+}
+
+static void sequencer_drop_draw_deactivate(struct wmDropBox *drop, wmDrag *UNUSED(drag))
+{
+ SeqDropCoords *coords = drop->draw_data;
+ if (coords) {
+ coords->in_use = false;
+ drop->draw_data = NULL;
+ }
+}
+
+static void nop_draw_droptip_fn(bContext *UNUSED(C),
+ wmWindow *UNUSED(win),
+ wmDrag *UNUSED(drag),
+ const int UNUSED(xy[2]))
+{
+ /* Do nothing in here.
+ * This is to prevent the default drag and drop mouse overlay to be drawn.
+ */
+}
+
+/* This region dropbox definition. */
+static void sequencer_dropboxes_add_to_lb(ListBase *lb)
+{
+ struct wmDropBox *drop;
+ drop = WM_dropbox_add(
+ lb, "SEQUENCER_OT_image_strip_add", image_drop_poll, sequencer_drop_copy, NULL, NULL);
+ drop->draw_droptip = nop_draw_droptip_fn;
+ drop->draw_in_view = draw_seq_in_view;
+ drop->draw_activate = image_drop_draw_activate;
+ drop->draw_deactivate = sequencer_drop_draw_deactivate;
+
+ drop->on_drag_start = audio_prefetch;
+
+ drop = WM_dropbox_add(
+ lb, "SEQUENCER_OT_movie_strip_add", movie_drop_poll, sequencer_drop_copy, NULL, NULL);
+ drop->draw_droptip = nop_draw_droptip_fn;
+ drop->draw_in_view = draw_seq_in_view;
+ drop->draw_activate = movie_drop_draw_activate;
+ drop->draw_deactivate = sequencer_drop_draw_deactivate;
+
+ drop->on_drag_start = video_prefetch;
+
+ drop = WM_dropbox_add(
+ lb, "SEQUENCER_OT_sound_strip_add", sound_drop_poll, sequencer_drop_copy, NULL, NULL);
+ drop->draw_droptip = nop_draw_droptip_fn;
+ drop->draw_in_view = draw_seq_in_view;
+ drop->draw_activate = sound_drop_draw_activate;
+ drop->draw_deactivate = sequencer_drop_draw_deactivate;
+}
+
+static bool image_drop_preview_poll(bContext *UNUSED(C),
+ wmDrag *drag,
+ const wmEvent *UNUSED(event))
+{
+ if (drag->type == WM_DRAG_PATH) {
+ if (ELEM(drag->icon, ICON_FILE_IMAGE, ICON_FILE_BLANK)) { /* Rule might not work? */
+ return true;
+ }
+ }
+
+ return WM_drag_is_ID_type(drag, ID_IM);
+}
+
+static bool movie_drop_preview_poll(bContext *UNUSED(C),
+ wmDrag *drag,
+ const wmEvent *UNUSED(event))
+{
+ if (drag->type == WM_DRAG_PATH) {
+ if (ELEM(drag->icon, 0, ICON_FILE_MOVIE, ICON_FILE_BLANK)) { /* Rule might not work? */
+ return true;
+ }
+ }
+
+ return WM_drag_is_ID_type(drag, ID_MC);
+}
+
+static bool sound_drop_preview_poll(bContext *UNUSED(C),
+ wmDrag *drag,
+ const wmEvent *UNUSED(event))
+{
+ if (drag->type == WM_DRAG_PATH) {
+ if (ELEM(drag->icon, ICON_FILE_SOUND, ICON_FILE_BLANK)) { /* Rule might not work? */
+ return true;
+ }
+ }
+
+ return WM_drag_is_ID_type(drag, ID_SO);
+}
+
+static void sequencer_preview_dropboxes_add_to_lb(ListBase *lb)
+{
+ WM_dropbox_add(lb,
+ "SEQUENCER_OT_image_strip_add",
+ image_drop_preview_poll,
+ sequencer_drop_copy,
+ NULL,
+ NULL);
+
+ WM_dropbox_add(lb,
+ "SEQUENCER_OT_movie_strip_add",
+ movie_drop_preview_poll,
+ sequencer_drop_copy,
+ NULL,
+ NULL);
+
+ WM_dropbox_add(lb,
+ "SEQUENCER_OT_sound_strip_add",
+ sound_drop_preview_poll,
+ sequencer_drop_copy,
+ NULL,
+ NULL);
+}
+
+void sequencer_dropboxes(void)
+{
+ ListBase *lb = WM_dropboxmap_find("Sequencer", SPACE_SEQ, RGN_TYPE_WINDOW);
+ sequencer_dropboxes_add_to_lb(lb);
+ lb = WM_dropboxmap_find("Sequencer", SPACE_SEQ, RGN_TYPE_PREVIEW);
+ sequencer_preview_dropboxes_add_to_lb(lb);
+}