diff --git a/scripts/startup/bl_operators/wm.py b/scripts/startup/bl_operators/wm.py index 2371dac1a45..ef9f87ff321 100644 --- a/scripts/startup/bl_operators/wm.py +++ b/scripts/startup/bl_operators/wm.py @@ -3524,6 +3524,94 @@ class WM_OT_drop_blend_file(Operator): col.operator("wm.append", text="Append...", icon='APPEND_BLEND').filepath = self.filepath +class WM_FH_alembic(bpy.types.FileHandler): + bl_idname = "WM_FH_alembic" + bl_label = "Alembic" + bl_import_operator = "WM_OT_alembic_import" + bl_file_extensions = ".abc" + + @classmethod + def poll_drop(cls, context): + return context.area.type == "VIEW_3D" + + +class WM_FH_collada(bpy.types.FileHandler): + bl_idname = "WM_FH_collada" + bl_label = "COLLADA" + bl_import_operator = "WM_OT_collada_import" + bl_file_extensions = ".dae" + + @classmethod + def poll_drop(cls, context): + return context.area.type == "VIEW_3D" + + +class WM_FH_gpencil(bpy.types.FileHandler): + bl_idname = "WM_FH_gpencil" + bl_label = "gpencil" + bl_import_operator = "WM_OT_gpencil_import_svg" + bl_file_extensions = ".svg" + + @classmethod + def poll_drop(cls, context): + return context.area.type == "VIEW_3D" + + +class WM_FH_obj(bpy.types.FileHandler): + bl_idname = "WM_FH_obj" + bl_label = "Wavefront OBJ" + bl_import_operator = "WM_OT_obj_import" + bl_file_extensions = ".obj" + + @classmethod + def poll_drop(cls, context): + return context.area.type == "VIEW_3D" + + +class WM_FH_ply(bpy.types.FileHandler): + bl_idname = "WM_FH_ply" + bl_label = "PLY" + bl_import_operator = "WM_OT_ply_import" + bl_file_extensions = ".ply" + + @classmethod + def poll_drop(cls, context): + return context.area.type == "VIEW_3D" + + +class WM_FH_stl(bpy.types.FileHandler): + bl_idname = "WM_FH_stl" + bl_label = "STL (experimental)" + bl_import_operator = "WM_OT_stl_import" + bl_file_extensions = ".stl" + + @classmethod + def poll_drop(cls, context): + return context.area.type == "VIEW_3D" + + +class WM_FH_import_mesh_stl(bpy.types.FileHandler): + bl_idname = "WM_FH_import_mesh_stl" + bl_label = "STL" + bl_import_operator = "import_mesh.stl" + bl_file_extensions = ".stl" + + @classmethod + def poll_drop(cls, context): + return context.area.type == "VIEW_3D" + + +class WM_FH_usd(bpy.types.FileHandler): + bl_idname = "WM_FH_usd" + bl_label = "USD" + bl_import_operator = "WM_OT_usd_import" + bl_file_extensions = ".usd;.usda;.usdc;.usdz" + + @classmethod + def poll_drop(cls, context): + return context.area.type == "VIEW_3D" + + classes = ( WM_OT_context_collection_boolean_set, WM_OT_context_cycle_array, @@ -3569,5 +3657,14 @@ classes = ( WM_MT_splash_quick_setup, WM_MT_splash, WM_MT_splash_about, - WM_MT_region_toggle_pie + WM_MT_region_toggle_pie, + + WM_FH_alembic, + WM_FH_collada, + WM_FH_gpencil, + WM_FH_obj, + WM_FH_ply, + WM_FH_stl, + WM_FH_import_mesh_stl, + WM_FH_usd, ) diff --git a/source/blender/editors/interface/interface_drag.cc b/source/blender/editors/interface/interface_drag.cc index 10800a2b34d..42516a91fb5 100644 --- a/source/blender/editors/interface/interface_drag.cc +++ b/source/blender/editors/interface/interface_drag.cc @@ -125,4 +125,8 @@ void ui_but_drag_start(bContext *C, uiBut *but) if (ELEM(but->dragtype, WM_DRAG_ASSET, WM_DRAG_ID)) { WM_event_start_drag(C, ICON_NONE, WM_DRAG_ASSET_LIST, nullptr, 0, WM_DRAG_NOP); } + + if (but->dragtype == WM_DRAG_PATH) { + WM_event_drag_space_file_paths(C, drag); + } } diff --git a/source/blender/editors/io/io_drop_import_file.cc b/source/blender/editors/io/io_drop_import_file.cc index 30dc7472b4a..8eea7700bbb 100644 --- a/source/blender/editors/io/io_drop_import_file.cc +++ b/source/blender/editors/io/io_drop_import_file.cc @@ -5,9 +5,10 @@ #include "BLI_path_util.h" #include "BLI_string.h" -#include "BLT_translation.h" - #include "BKE_file_handler.hh" +#include "BKE_screen.hh" +#include "BLT_translation.h" +#include "ED_fileselect.hh" #include "CLG_log.h" @@ -15,10 +16,12 @@ #include "RNA_access.hh" #include "RNA_define.hh" +#include "RNA_enum_types.hh" #include "RNA_prototypes.h" #include "WM_api.hh" #include "WM_types.hh" +#include "wm_event_system.h" #include "UI_interface.hh" @@ -47,6 +50,29 @@ static blender::Vector drop_import_file_paths(const wmOperator *op) } return result; } + +/* Retuns the list of file paths stored in #WM_OT_drop_import_file operator properties. */ +static blender::Vector drop_import_file_paths(PointerRNA *ptr) +{ + blender::Vector result; + char dir[FILE_MAX], file[FILE_MAX]; + + RNA_string_get(ptr, "directory", dir); + + PropertyRNA *prop = RNA_struct_find_property(ptr, "files"); + int files_len = RNA_property_collection_length(ptr, prop); + + for (int i = 0; i < files_len; i++) { + PointerRNA fileptr; + RNA_property_collection_lookup_int(ptr, prop, i, &fileptr); + RNA_string_get(&fileptr, "name", file); + char file_path[FILE_MAX]; + BLI_path_join(file_path, sizeof(file_path), dir, file); + result.append(file_path); + } + return result; +} + /** * Return a vector of file handlers that support any file path in `paths` and the call to * `poll_drop` returns #true. Unlike `BKE_file_handlers_poll_file_drop`, it ensures that file @@ -63,41 +89,34 @@ static blender::Vector drop_import_file_poll_file_handlers( } /** - * Creates a RNA pointer for the `FileHandlerType.import_operator` and sets on it all supported - * file paths from `paths`. + * Sets in PointerRNA `ptr` all paths, returns`true` if pointer supports multiple paths. */ -static PointerRNA file_handler_import_operator_create_ptr(const FileHandlerType *file_handler, - const blender::Span paths) +static bool file_handler_import_operator_create_ptr(PointerRNA &ptr, + const blender::Span paths) { - wmOperatorType *ot = WM_operatortype_find(file_handler->import_operator, false); - BLI_assert(ot != nullptr); - PointerRNA props; - WM_operator_properties_create_ptr(&props, ot); - const auto supported_paths = file_handler->filter_supported_paths(paths); - - PropertyRNA *filepath_prop = RNA_struct_find_property_check(props, "filepath", PROP_STRING); + PropertyRNA *filepath_prop = RNA_struct_find_property_check(ptr, "filepath", PROP_STRING); if (filepath_prop) { - RNA_property_string_set(&props, filepath_prop, paths[supported_paths[0]].c_str()); + RNA_property_string_set(&ptr, filepath_prop, paths[0].c_str()); } - PropertyRNA *directory_prop = RNA_struct_find_property_check(props, "directory", PROP_STRING); + PropertyRNA *directory_prop = RNA_struct_find_property_check(ptr, "directory", PROP_STRING); if (directory_prop) { char dir[FILE_MAX]; BLI_path_split_dir_part(paths[0].c_str(), dir, sizeof(dir)); - RNA_property_string_set(&props, directory_prop, dir); + RNA_property_string_set(&ptr, directory_prop, dir); } PropertyRNA *files_prop = RNA_struct_find_collection_property_check( - props, "files", &RNA_OperatorFileListElement); + ptr, "files", &RNA_OperatorFileListElement); if (files_prop) { - RNA_property_collection_clear(&props, files_prop); - for (const auto &index : supported_paths) { + RNA_property_collection_clear(&ptr, files_prop); + for (const auto &index : paths.index_range()) { char file[FILE_MAX]; BLI_path_split_file_part(paths[index].c_str(), file, sizeof(file)); PointerRNA item_ptr{}; - RNA_property_collection_add(&props, files_prop, &item_ptr); + RNA_property_collection_add(&ptr, files_prop, &item_ptr); RNA_string_set(&item_ptr, "name", file); } } @@ -114,65 +133,292 @@ static PointerRNA file_handler_import_operator_create_ptr(const FileHandlerType "FileHandler documentation for details."; CLOG_WARN(&LOG, TIP_(message)); } - return props; + return directory_prop && files_prop; +} + +struct DropImportData { + /** #WM_OT_drop_import_file source operator pointer. */ + wmOperator *source = nullptr; + + /** Mouse values. */ + int x{0}, y{0}; + /** Selected file_handler. */ + FileHandlerType *file_handler = nullptr; + /** Operator for `file_handler.import_operator`. */ + wmOperator *op = nullptr; + /** Number of elements supported by the file handler. */ + int64_t top; + /** Remaining paths to include, first #top elements are supported by the active file handler. */ + blender::Vector paths; + + void reset() + { + if (!file_handler) { + return; + } + WM_operator_free(op); + op = nullptr; + file_handler = nullptr; + } + + ~DropImportData() + { + reset(); + } +}; + +static DropImportData *active_drop_import_data = nullptr; + +static void wm_drop_import_file_cancel(bContext * /*C*/, wmOperator *op) +{ + DropImportData *data = static_cast(op->customdata); + MEM_delete(data); + op->customdata = nullptr; + active_drop_import_data = nullptr; +} + +static int wm_drop_import_file_exec(bContext * /*C*/, wmOperator *op) +{ + wm_drop_import_file_cancel(nullptr, op); + return OPERATOR_FINISHED; +} + +static void drop_import_file_draw_import_operator(const bContext *C, + uiLayout *layout, + wmOperator *op) +{ + /** Hack: temporary hide, same as in space file. */ + const char *hide[] = {"filepath", "files", "directory", "filename"}; + for (int i = 0; i < ARRAY_SIZE(hide); i++) { + PropertyRNA *prop = RNA_struct_find_property(op->ptr, hide[i]); + if (prop) { + RNA_def_property_flag(prop, PROP_HIDDEN); + } + } + + uiTemplateOperatorPropertyButs( + C, layout, op, UI_BUT_LABEL_ALIGN_NONE, UI_TEMPLATE_OP_PROPS_SHOW_EMPTY); + + /** Revert hack. */ + for (int i = 0; i < ARRAY_SIZE(hide); i++) { + PropertyRNA *prop = RNA_struct_find_property(op->ptr, hide[i]); + if (prop) { + RNA_def_property_clear_flag(prop, PROP_HIDDEN); + } + } +} + +static void wm_drop_import_file_reset_paths(PointerRNA *ptr, + const blender::Span paths, + const int64_t top) +{ + RNA_collection_clear(ptr, "files"); + for (const int64_t idx : paths.index_range()) { + char file[FILE_MAX]; + BLI_path_split_file_part(paths[idx].c_str(), file, sizeof(file)); + PointerRNA itemptr{}; + RNA_collection_add(ptr, "files", &itemptr); + RNA_string_set(&itemptr, "name", file); + RNA_boolean_set(&itemptr, "select", idx < top); + } +} + +static void drop_import_file_handler_update(bContext *C, + PointerRNA * /*ptr*/, + PropertyRNA * /*prop*/); + +static void drop_import_file_exec_import(bContext &C, wmOperator *op) +{ + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "files"); + int files_len = RNA_property_collection_length(op->ptr, prop); + blender::Vector selected; + for (int x = 0; x < files_len; x++) { + PointerRNA fileptr; + RNA_property_collection_lookup_int(op->ptr, prop, x, &fileptr); + selected.append(RNA_boolean_get(&fileptr, "select")); + } + /** Do nothing if there is no selection. */ + if (selected.is_empty()) { + return; + } + DropImportData &data = *active_drop_import_data; + auto &paths = data.paths; + /** Get wich files are selected. */ + blender::Vector selected_paths; + for (int x = paths.size() - 1; x > -1; x--) { + if (selected[x]) { + selected_paths.append(paths[x]); + paths.remove(x); + } + } + /** Consume selected paths, operators can use all in one call, other may use one file for each + * call. */ + while (!selected_paths.is_empty()) { + PointerRNA &props = *data.op->ptr; + const bool all = file_handler_import_operator_create_ptr(props, selected_paths); + if (all) { + selected_paths.clear(); + } + else { + selected_paths.remove(0); + } + WM_operator_name_call_ptr(&C, data.op->type, WM_OP_EXEC_DEFAULT, &props, nullptr); + } + + /** No more paths, remove modal handler. */ + if (paths.is_empty()) { + UI_popup_handlers_remove_all(&C, &CTX_wm_window(&C)->modalhandlers); + return; + } + auto file_handlers = drop_import_file_poll_file_handlers(&C, paths, false); + /** Remaining files can't be used if there is no file handler. */ + if (file_handlers.is_empty()) { + UI_popup_handlers_remove_all(&C, &CTX_wm_window(&C)->modalhandlers); + return; + } + /***/ + + wm_drop_import_file_reset_paths(op->ptr, paths, 0); + /** Some file valid to the file handlres can remain unselected and file hanlder can still be + * usable, other file. */ + int file_handler_idx = file_handlers.first_index_of_try(data.file_handler); + if (file_handler_idx > -1) { + RNA_enum_set(op->ptr, "file_handler", file_handler_idx); + data.top = file_handlers[file_handler_idx]->filter_supported_paths(paths).size(); + return; + } + RNA_enum_set(op->ptr, "file_handler", 0); + drop_import_file_handler_update(&C, nullptr, nullptr); } -static int wm_drop_import_file_exec(bContext *C, wmOperator *op) +static void wm_drop_import_file_draw(bContext *C, wmOperator *op) +{ + uiItemL(op->layout, op->type->name, ICON_NONE); + uiLayout *row = uiLayoutRow(op->layout, false); + + uiLayout *col = uiLayoutColumn(row, false); + uiLayout *box = uiLayoutBox(col); + + { + char directory[FILE_MAX]; + RNA_string_get(op->ptr, "directory", directory); + uiItemL(box, directory, ICON_FILE_FOLDER); + } + + DropImportData &data = *active_drop_import_data; + + uiTemplateList(box, + C, + "WM_UL_drop_import_files", + "", + op->ptr, + "files", + op->ptr, + "active_file_name", + nullptr, + 5, + 15, + UILST_LAYOUT_DEFAULT, + 1, + UI_TEMPLATE_LIST_FLAG_NONE); + + col = uiLayoutColumn(row, false); + box = uiLayoutBox(col); + uiItemR(box, op->ptr, "file_handler", eUI_Item_Flag(0), "", ICON_NONE); + + drop_import_file_draw_import_operator(C, box, data.op); + + uiLayout *sub_row = uiLayoutRow(op->layout, true); + uiBlock *block = uiLayoutGetBlock(sub_row); + uiBut *but = uiDefBut(block, + UI_BTYPE_BUT, + 0, + IFACE_(data.op->type->name), + 50, + 50, + 50, + UI_UNIT_Y, + nullptr, + 0, + 0, + 0, + 0, + ""); + UI_but_flag_enable(but, UI_BUT_ACTIVE_DEFAULT); + UI_but_func_set(but, [op](bContext &C) -> void { drop_import_file_exec_import(C, op); }); +} +static int wm_drop_import_file_invoke(bContext *C, wmOperator *op, const wmEvent *event) { auto paths = drop_import_file_paths(op); if (paths.is_empty()) { return OPERATOR_CANCELLED; } - auto file_handlers = drop_import_file_poll_file_handlers(C, paths, false); if (file_handlers.is_empty()) { return OPERATOR_CANCELLED; } - wmOperatorType *ot = WM_operatortype_find(file_handlers[0]->import_operator, false); - PointerRNA file_props = file_handler_import_operator_create_ptr(file_handlers[0], paths); - - WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &file_props, nullptr); - WM_operator_properties_free(&file_props); - return OPERATOR_FINISHED; + DropImportData *data = MEM_new(""); + data->x = event->mval[0]; + data->y = event->mval[1]; + op->customdata = active_drop_import_data = data; + data->paths = paths; + data->source = op; + drop_import_file_handler_update(C, nullptr, nullptr); + return WM_operator_ui_popup(C, op, 600); } -static int wm_drop_import_file_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) +const EnumPropertyItem *RNA_text_itemf(bContext *C, + PointerRNA * /*ptr*/, + PropertyRNA * /*prop*/, + bool *r_free) { - const auto paths = drop_import_file_paths(op); - if (paths.is_empty()) { - return OPERATOR_CANCELLED; + auto fhs = drop_import_file_poll_file_handlers( + C, active_drop_import_data->paths.as_span(), false); + + int totitem = 0; + int i = 0; + EnumPropertyItem *item = nullptr; + for (auto fh : fhs) { + EnumPropertyItem item_tmp = {0}; + item_tmp.identifier = item_tmp.name = WM_operatortype_find(fh->import_operator, false)->name; + item_tmp.value = i++; + RNA_enum_item_add(&item, &totitem, &item_tmp); } + RNA_enum_item_end(&item, &totitem); + *r_free = true; + return item; +} - auto file_handlers = drop_import_file_poll_file_handlers(C, paths, false); - if (file_handlers.size() == 1) { - return wm_drop_import_file_exec(C, op); - } +static void drop_import_file_handler_update(bContext *C, + PointerRNA * /*ptr*/, + PropertyRNA * /*prop*/) +{ - /** - * Create a menu with all file handler import operators that can support any files in paths and - * let user decide which to use. - */ - uiPopupMenu *pup = UI_popup_menu_begin(C, "", ICON_NONE); - uiLayout *layout = UI_popup_menu_layout(pup); - uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT); + DropImportData &data = *active_drop_import_data; + wmOperator *op = data.source; - for (auto *file_handler : file_handlers) { - const PointerRNA file_props = file_handler_import_operator_create_ptr(file_handler, paths); - wmOperatorType *ot = WM_operatortype_find(file_handler->import_operator, false); - uiItemFullO_ptr(layout, - ot, - TIP_(ot->name), - ICON_NONE, - static_cast(file_props.data), - WM_OP_INVOKE_DEFAULT, - UI_ITEM_NONE, - nullptr); + const int file_handler_enum = RNA_enum_get(op->ptr, "file_handler"); + data.reset(); + auto &paths = data.paths; + auto file_handlers = drop_import_file_poll_file_handlers(C, paths, false); + data.file_handler = file_handlers[file_handler_enum]; + wmOperatorType *ot = WM_operatortype_find(file_handlers[file_handler_enum]->import_operator, + false); + data.op = wm_operator_create(CTX_wm_manager(C), ot, nullptr, CTX_wm_reports(C)); + + auto supported_paths = data.file_handler->filter_supported_paths(paths); + /** Sort paths, to show at firts all supported by the active file handler. */ + for (const auto idx : supported_paths.index_range()) { + std::rotate(paths.begin() + idx, + paths.begin() + supported_paths[idx], + paths.begin() + supported_paths[idx] + 1); } + data.top = supported_paths.size(); - UI_popup_menu_end(C, pup); - return OPERATOR_INTERFACE; -} + /** Update #WM_OT_drop_import_file.files to apply sorting. */ + wm_drop_import_file_reset_paths(op->ptr, paths, supported_paths.size()); +}; void WM_OT_drop_import_file(wmOperatorType *ot) { @@ -181,6 +427,8 @@ void WM_OT_drop_import_file(wmOperatorType *ot) ot->idname = "WM_OT_drop_import_file"; ot->flag = OPTYPE_INTERNAL; ot->exec = wm_drop_import_file_exec; + ot->ui = wm_drop_import_file_draw; + ot->cancel = wm_drop_import_file_cancel; ot->invoke = wm_drop_import_file_invoke; PropertyRNA *prop; @@ -191,6 +439,14 @@ void WM_OT_drop_import_file(wmOperatorType *ot) prop = RNA_def_collection_runtime(ot->srna, "files", &RNA_OperatorFileListElement, "Files", ""); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + prop = RNA_def_int(ot->srna, "active_file_name", 0, -1, 10000, "", "", -1, 10000); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + prop = RNA_def_enum(ot->srna, "file_handler", rna_enum_dummy_NULL_items, 0, "File Handler", ""); + RNA_def_enum_funcs(prop, RNA_text_itemf); + RNA_def_property_update_runtime_with_context_and_property(prop, drop_import_file_handler_update); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } void drop_import_file_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop) @@ -201,15 +457,7 @@ void drop_import_file_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop) BLI_path_split_dir_part(paths[0].c_str(), dir, sizeof(dir)); RNA_string_set(drop->ptr, "directory", dir); - RNA_collection_clear(drop->ptr, "files"); - for (const auto &path : paths) { - char file[FILE_MAX]; - BLI_path_split_file_part(path.c_str(), file, sizeof(file)); - - PointerRNA itemptr{}; - RNA_collection_add(drop->ptr, "files", &itemptr); - RNA_string_set(&itemptr, "name", file); - } + wm_drop_import_file_reset_paths(drop->ptr, paths, 0); } static bool drop_import_file_poll(bContext *C, wmDrag *drag, const wmEvent * /*event*/) @@ -236,6 +484,34 @@ static char *drop_import_file_tooltip(bContext *C, return BLI_strdup(TIP_("Multiple file handlers can be used, drop to pick which to use")); } +static void segment_list_item(uiList * /*ui_list*/, + const bContext * /*C*/, + uiLayout *layout, + PointerRNA *idataptr, + PointerRNA *itemptr, + int /*icon*/, + PointerRNA * /*active_dataptr*/, + const char * /*active_propname*/, + int index, + int /*flt_flag*/) +{ + /** Avoid use of this ui_list in other places. */ + DropImportData *data = active_drop_import_data; + if (!data || idataptr->owner_id != data->source->ptr->owner_id) { + return; + } + + uiLayout *row = uiLayoutRow(layout, true); + uiLayoutSetEnabled(row, index < data->top); + uiItemR(row, itemptr, "select", UI_ITEM_NONE, "", ICON_NONE); + uiItemR(row, + itemptr, + "name", + UI_ITEM_R_NO_BG, + "", + ED_file_extension_icon(data->paths[index].c_str())); +} + void ED_dropbox_drop_import_file() { ListBase *lb = WM_dropboxmap_find("Window", SPACE_EMPTY, RGN_TYPE_WINDOW); @@ -245,4 +521,9 @@ void ED_dropbox_drop_import_file() drop_import_file_copy, nullptr, drop_import_file_tooltip); + + uiListType *list_type = MEM_new("WM_UL_drop_import_files"); + STRNCPY(list_type->idname, "WM_UL_drop_import_files"); + list_type->draw_item = segment_list_item; + WM_uilisttype_add(list_type); } diff --git a/source/blender/makesrna/intern/rna_wm.cc b/source/blender/makesrna/intern/rna_wm.cc index 1440b90badd..dd7b11b7349 100644 --- a/source/blender/makesrna/intern/rna_wm.cc +++ b/source/blender/makesrna/intern/rna_wm.cc @@ -2102,6 +2102,9 @@ static void rna_def_operator_filelist_element(BlenderRNA *brna) prop = RNA_def_property(srna, "name", PROP_STRING, PROP_FILENAME); RNA_def_property_flag(prop, PROP_IDPROPERTY); RNA_def_property_ui_text(prop, "Name", "Name of a file or directory within a file list"); + prop = RNA_def_property(srna, "select", PROP_BOOLEAN, 0); + RNA_def_property_flag(prop, PROP_IDPROPERTY); + RNA_def_property_ui_text(prop, "Select", "Select flag"); } static void rna_def_event(BlenderRNA *brna) diff --git a/source/blender/windowmanager/WM_api.hh b/source/blender/windowmanager/WM_api.hh index 95063dffba1..d36b1e6dc96 100644 --- a/source/blender/windowmanager/WM_api.hh +++ b/source/blender/windowmanager/WM_api.hh @@ -1354,6 +1354,11 @@ wmDrag *WM_drag_data_create( */ void WM_event_start_prepared_drag(bContext *C, wmDrag *drag); void WM_event_drag_image(wmDrag *, const ImBuf *, float scale); +/** + * Updates the `drag` event to include all selected files in the space file where the event + * started. + */ +void WM_event_drag_space_file_paths(const bContext *, wmDrag *drag); void WM_drag_free(wmDrag *drag); void WM_drag_data_free(eWM_DragDataType dragtype, void *poin); void WM_drag_free_list(ListBase *lb); diff --git a/source/blender/windowmanager/intern/wm_dragdrop.cc b/source/blender/windowmanager/intern/wm_dragdrop.cc index a0f94fce155..cf7326318cd 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.cc +++ b/source/blender/windowmanager/intern/wm_dragdrop.cc @@ -289,6 +289,31 @@ void WM_event_drag_image(wmDrag *drag, const ImBuf *imb, float scale) drag->imbuf_scale = scale; } +void WM_event_drag_space_file_paths(const bContext *C, wmDrag *drag) +{ + if (!CTX_wm_space_file(C)) { + return; + } + char dirpath[FILE_MAX]; + BLI_path_split_dir_part(WM_drag_get_single_path(drag), dirpath, FILE_MAX); + + blender::Vector paths; + blender::Vector c_paths; + + ListBase file_links = CTX_data_collection_get(C, "selected_files"); + LISTBASE_FOREACH (const CollectionPointerLink *, link, &file_links) { + const FileDirEntry *file = static_cast(link->ptr.data); + char filepath[FILE_MAX]; + BLI_path_join(filepath, sizeof(filepath), dirpath, file->name); + + paths.append(filepath); + c_paths.append(paths.last().c_str()); + } + BLI_freelistN(&file_links); + WM_drag_data_free(drag->type, drag->poin); + drag->poin = WM_drag_create_path_data(c_paths); +} + void WM_drag_data_free(eWM_DragDataType dragtype, void *poin) { /* Don't require all the callers to have a nullptr-check, just allow passing nullptr. */ diff --git a/source/blender/windowmanager/intern/wm_event_system.cc b/source/blender/windowmanager/intern/wm_event_system.cc index 16b35b5ab3c..e1c1b33b1c9 100644 --- a/source/blender/windowmanager/intern/wm_event_system.cc +++ b/source/blender/windowmanager/intern/wm_event_system.cc @@ -1364,10 +1364,10 @@ bool WM_operator_is_repeat(const bContext *C, const wmOperator *op) return (op_prev && (op->type == op_prev->type)); } -static wmOperator *wm_operator_create(wmWindowManager *wm, - wmOperatorType *ot, - PointerRNA *properties, - ReportList *reports) +wmOperator *wm_operator_create(wmWindowManager *wm, + wmOperatorType *ot, + PointerRNA *properties, + ReportList *reports) { /* Operator-type names are static still. pass to allocation name for debugging. */ wmOperator *op = MEM_cnew(ot->idname); diff --git a/source/blender/windowmanager/wm_event_system.h b/source/blender/windowmanager/wm_event_system.h index d4be459533b..3f30a4571a7 100644 --- a/source/blender/windowmanager/wm_event_system.h +++ b/source/blender/windowmanager/wm_event_system.h @@ -193,3 +193,7 @@ wmOperatorCallContext wm_drop_operator_context_get(const wmDropBox *drop); * Called in #wm_draw_window_onscreen. */ void wm_drags_draw(bContext *C, wmWindow *win); +wmOperator *wm_operator_create(wmWindowManager *wm, + wmOperatorType *ot, + PointerRNA *properties, + ReportList *reports);