diff --git a/scripts/startup/bl_operators/wm.py b/scripts/startup/bl_operators/wm.py index 6332634ba21..db4e08fcc98 100644 --- a/scripts/startup/bl_operators/wm.py +++ b/scripts/startup/bl_operators/wm.py @@ -3526,6 +3526,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, @@ -3571,5 +3659,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/blenkernel/BKE_file_handler.hh b/source/blender/blenkernel/BKE_file_handler.hh index 4d70330a1a4..437e220e6f2 100644 --- a/source/blender/blenkernel/BKE_file_handler.hh +++ b/source/blender/blenkernel/BKE_file_handler.hh @@ -29,6 +29,18 @@ struct FileHandlerType { /** RNA integration. */ ExtensionRNA rna_ext; + + /** Returns a vector of indices on #paths of those file paths supported by the file handler. */ + blender::Vector filter_supported_paths(const blender::Span paths) const; + + /** Return the import operator associated to the file handler if set and exists, otherwise + * #null_ptr. */ + wmOperatorType *get_import_operator() const; + + /* Creates a #PointerRNA for the #import_operator and fills it with all file paths supported by + * the file handler. */ + PointerRNA import_operator_file_properties_create_ptr( + const blender::Span paths) const; }; /** @@ -49,3 +61,8 @@ void BKE_file_handler_remove(FileHandlerType *file_handler); /** Return a reference of the #RawVector with all `file_handlers` registered. */ const blender::RawVector> &BKE_file_handlers(); + +/** Return a vector of file handlers that support any file path in #paths, has a valid + * #import_operator assigned and the call to #poll_drop returns #true. */ +blender::Vector BKE_file_handlers_poll_file_drop( + const bContext *C, const blender::Span paths); diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 92645ae1d65..8419bd776cd 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -31,6 +31,8 @@ set(INC ../../../intern/opensubdiv ../../../extern/fmtlib/include + ../windowmanager + # RNA_prototypes.h ${CMAKE_BINARY_DIR}/source/blender/makesrna ) diff --git a/source/blender/blenkernel/intern/blender.cc b/source/blender/blenkernel/intern/blender.cc index dbfbde97c2f..7adc0071d4a 100644 --- a/source/blender/blenkernel/intern/blender.cc +++ b/source/blender/blenkernel/intern/blender.cc @@ -29,6 +29,7 @@ #include "BKE_brush.hh" #include "BKE_cachefile.h" #include "BKE_callbacks.h" +#include "BKE_file_handler.hh" #include "BKE_global.h" #include "BKE_idprop.h" #include "BKE_image.h" @@ -70,7 +71,6 @@ void BKE_blender_free() } BKE_spacetypes_free(); /* after free main, it uses space callbacks */ - IMB_exit(); BKE_cachefiles_exit(); DEG_free_node_types(); diff --git a/source/blender/blenkernel/intern/file_handler.cc b/source/blender/blenkernel/intern/file_handler.cc index 5416f01b8c3..b0c039578b1 100644 --- a/source/blender/blenkernel/intern/file_handler.cc +++ b/source/blender/blenkernel/intern/file_handler.cc @@ -2,9 +2,15 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "WM_api.hh" +#include "WM_types.hh" + #include "BKE_file_handler.hh" -#include "BLI_string.h" +#include "RNA_access.hh" static blender::RawVector> &file_handlers() { @@ -57,3 +63,104 @@ void BKE_file_handler_remove(FileHandlerType *file_handler) return test_file_handler.get() == file_handler; }); } + +blender::Vector BKE_file_handlers_poll_file_drop( + const bContext *C, const blender::Span paths) +{ + + blender::Vector path_extensions; + for (const std::string &path : paths) { + const char *extension = BLI_path_extension(path.c_str()); + if (!extension) { + continue; + } + path_extensions.append_non_duplicates(extension); + } + + blender::Vector result; + + for (const std::unique_ptr &file_handler_ptr : file_handlers()) { + FileHandlerType &file_handler = *file_handler_ptr; + const auto &file_extensions = file_handler.file_extensions; + + const bool support_any_extension = std::any_of( + file_extensions.begin(), + file_extensions.end(), + [&path_extensions](const std::string &test_extension) { + return path_extensions.contains(test_extension); + }); + + if (!support_any_extension) { + continue; + } + + if (!(file_handler.poll_drop && file_handler.poll_drop(C, &file_handler))) { + continue; + } + + /* File handles can be registered without a import operator, or the import operator can be + * removed and the file handler can still be registered, filter too file handles with valid + * import operators. */ + if (!file_handler.get_import_operator()) { + continue; + } + + result.append(&file_handler); + } + return result; +} + +blender::Vector FileHandlerType::filter_supported_paths( + const blender::Span paths) const +{ + blender::Vector indices; + + for (const int idx : paths.index_range()) { + const char *extension = BLI_path_extension(paths[idx].c_str()); + if (!extension) { + continue; + } + if (file_extensions.contains(extension)) { + indices.append(idx); + } + } + return indices; +} + +wmOperatorType *FileHandlerType::get_import_operator() const +{ + return WM_operatortype_find(import_operator, false); +} + +PointerRNA FileHandlerType::import_operator_file_properties_create_ptr( + const blender::Span paths) const +{ + BLI_assert(get_import_operator()); + + PointerRNA props; + WM_operator_properties_create_ptr(&props, get_import_operator()); + + const auto suported_paths = filter_supported_paths(paths); + + if (PropertyRNA *prop = RNA_struct_find_property(&props, "filepath")) { + RNA_string_set(&props, "filepath", paths[suported_paths[0]].c_str()); + } + + if (PropertyRNA *prop = RNA_struct_find_property(&props, "directory")) { + + char dir[FILE_MAX]; + BLI_path_split_dir_part(paths[0].c_str(), dir, sizeof(dir)); + RNA_string_set(&props, "directory", dir); + + RNA_collection_clear(&props, "files"); + for (const auto &index : suported_paths) { + char file[FILE_MAX]; + BLI_path_split_file_part(paths[index].c_str(), file, sizeof(file)); + + PointerRNA itemptr{}; + RNA_collection_add(&props, "files", &itemptr); + RNA_string_set(&itemptr, "name", file); + } + } + return props; +} diff --git a/source/blender/editors/interface/interface_drag.cc b/source/blender/editors/interface/interface_drag.cc index 9a9516ef5b2..10800a2b34d 100644 --- a/source/blender/editors/interface/interface_drag.cc +++ b/source/blender/editors/interface/interface_drag.cc @@ -64,7 +64,7 @@ void UI_but_drag_set_path(uiBut *but, const char *path) if (but->dragflag & UI_BUT_DRAGPOIN_FREE) { WM_drag_data_free(but->dragtype, but->dragpoin); } - but->dragpoin = WM_drag_create_path_data(path); + but->dragpoin = WM_drag_create_path_data(blender::Span(&path, 1)); but->dragflag |= UI_BUT_DRAGPOIN_FREE; } diff --git a/source/blender/editors/io/CMakeLists.txt b/source/blender/editors/io/CMakeLists.txt index ea2917a9809..0ed0fe92e3c 100644 --- a/source/blender/editors/io/CMakeLists.txt +++ b/source/blender/editors/io/CMakeLists.txt @@ -17,6 +17,8 @@ set(INC ../../io/wavefront_obj ../../makesrna ../../windowmanager + + ../../../../extern/fmtlib/include ) set(INC_SYS @@ -35,6 +37,8 @@ set(SRC io_ply_ops.cc io_stl_ops.cc io_usd.cc + io_utils.cc + io_drop_import_file.cc io_alembic.hh io_cache.hh @@ -45,6 +49,8 @@ set(SRC io_ply_ops.hh io_stl_ops.hh io_usd.hh + io_utils.hh + io_drop_import_file.hh ) set(LIB @@ -53,6 +59,7 @@ set(LIB PRIVATE bf::depsgraph PRIVATE bf::dna PRIVATE bf::intern::guardedalloc + extern_fmtlib ) if(WITH_OPENCOLLADA) diff --git a/source/blender/editors/io/io_alembic.cc b/source/blender/editors/io/io_alembic.cc index 46fc47b37a9..5220245676e 100644 --- a/source/blender/editors/io/io_alembic.cc +++ b/source/blender/editors/io/io_alembic.cc @@ -55,6 +55,8 @@ # include "ABC_alembic.h" +# include "io_utils.hh" + const EnumPropertyItem rna_enum_abc_export_evaluation_mode_items[] = { {DAG_EVAL_RENDER, "RENDER", @@ -584,8 +586,9 @@ static void ui_alembic_import_settings(uiLayout *layout, PointerRNA *imfptr) uiItemR(col, imfptr, "always_add_cache_reader", UI_ITEM_NONE, nullptr, ICON_NONE); } -static void wm_alembic_import_draw(bContext * /*C*/, wmOperator *op) +static void wm_alembic_import_draw(bContext *C, wmOperator *op) { + io_util_drop_file_label_draw(C, op, ICON_FILE_3D, ".abc"); ui_alembic_import_settings(op->layout, op->ptr); } @@ -595,7 +598,7 @@ static int wm_alembic_import_invoke(bContext *C, wmOperator *op, const wmEvent * if (!RNA_struct_property_is_set(op->ptr, "as_background_job")) { RNA_boolean_set(op->ptr, "as_background_job", true); } - return WM_operator_filesel(C, op, event); + return io_util_import_invoke(C, op, event); } static int wm_alembic_import_exec(bContext *C, wmOperator *op) @@ -651,7 +654,7 @@ void WM_OT_alembic_import(wmOperatorType *ot) ot->name = "Import Alembic"; ot->description = "Load an Alembic archive"; ot->idname = "WM_OT_alembic_import"; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_PRESET; + ot->flag = OPTYPE_UNDO | OPTYPE_PRESET; ot->invoke = wm_alembic_import_invoke; ot->exec = wm_alembic_import_exec; @@ -665,6 +668,7 @@ void WM_OT_alembic_import(wmOperatorType *ot) WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH | WM_FILESEL_SHOW_PROPS, FILE_DEFAULTDISPLAY, FILE_SORT_DEFAULT); + io_util_skip_save_filesel_props(ot, WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH); PropertyRNA *prop = RNA_def_string(ot->srna, "filter_glob", "*.abc", 0, "", ""); RNA_def_property_flag(prop, PROP_HIDDEN); diff --git a/source/blender/editors/io/io_collada.cc b/source/blender/editors/io/io_collada.cc index 4e725d832ff..c966b4d782d 100644 --- a/source/blender/editors/io/io_collada.cc +++ b/source/blender/editors/io/io_collada.cc @@ -22,6 +22,7 @@ # include "ED_fileselect.hh" # include "ED_object.hh" +# include "ED_outliner.hh" # include "RNA_access.hh" # include "RNA_define.hh" @@ -35,6 +36,7 @@ # include "collada.h" # include "io_collada.hh" +# include "io_utils.hh" static int wm_collada_export_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) { @@ -747,6 +749,11 @@ static int wm_collada_import_exec(bContext *C, wmOperator *op) if (collada_import(C, &import_settings)) { DEG_id_tag_update(&CTX_data_scene(C)->id, ID_RECALC_BASE_FLAGS); + Scene *scene = CTX_data_scene(C); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); + ED_outliner_select_sync_from_object_tag(C); return OPERATOR_FINISHED; } @@ -782,8 +789,9 @@ static void uiCollada_importSettings(uiLayout *layout, PointerRNA *imfptr) uiItemR(box, imfptr, "keep_bind_info", UI_ITEM_NONE, nullptr, ICON_NONE); } -static void wm_collada_import_draw(bContext * /*C*/, wmOperator *op) +static void wm_collada_import_draw(bContext *C, wmOperator *op) { + io_util_drop_file_label_draw(C, op, ICON_FILE_3D, "dae"); uiCollada_importSettings(op->layout, op->ptr); } @@ -792,9 +800,9 @@ void WM_OT_collada_import(wmOperatorType *ot) ot->name = "Import COLLADA"; ot->description = "Load a Collada file"; ot->idname = "WM_OT_collada_import"; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_PRESET; + ot->flag = OPTYPE_UNDO | OPTYPE_PRESET; - ot->invoke = WM_operator_filesel; + ot->invoke = io_util_import_invoke; ot->exec = wm_collada_import_exec; ot->poll = WM_operator_winactive; @@ -807,6 +815,7 @@ void WM_OT_collada_import(wmOperatorType *ot) WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS, FILE_DEFAULTDISPLAY, FILE_SORT_DEFAULT); + io_util_skip_save_filesel_props(ot, WM_FILESEL_FILEPATH); PropertyRNA *prop = RNA_def_string(ot->srna, "filter_glob", "*.dae", 0, "", ""); RNA_def_property_flag(prop, PROP_HIDDEN); diff --git a/source/blender/editors/io/io_drop_import_file.cc b/source/blender/editors/io/io_drop_import_file.cc new file mode 100644 index 00000000000..6a80ed8b479 --- /dev/null +++ b/source/blender/editors/io/io_drop_import_file.cc @@ -0,0 +1,186 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "DNA_space_types.h" + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" + +#include "BLI_path_util.h" + +#include "BLT_translation.h" + +#include "BKE_context.hh" +#include "BKE_file_handler.hh" +#include "BKE_main.hh" +#include "BKE_preferences.h" + +#include "ED_fileselect.hh" + +#include + +#include "RNA_access.hh" +#include "RNA_define.hh" + +#include "WM_api.hh" +#include "WM_types.hh" + +#include "UI_interface.hh" + +#include "io_drop_import_file.hh" + +/* Retuns the list of file paths stored in #WM_OT_drop_import_file operator properties. */ +static blender::Vector drop_import_file_paths(const wmOperator *op) +{ + blender::Vector result; + char dir[FILE_MAX], file[FILE_MAX]; + + RNA_string_get(op->ptr, "directory", dir); + + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "files"); + int files_len = RNA_property_collection_length(op->ptr, prop); + + for (int i = 0; i < files_len; i++) { + PointerRNA fileptr; + RNA_property_collection_lookup_int(op->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; +} + +static int wm_drop_import_file_exec(bContext *C, wmOperator *op) +{ + auto paths = drop_import_file_paths(op); + if (paths.size() < 1) { + return OPERATOR_CANCELLED; + } + + auto file_handlers = BKE_file_handlers_poll_file_drop(C, paths); + if (file_handlers.size() == 0) { + return OPERATOR_CANCELLED; + } + + PointerRNA file_props = file_handlers[0]->import_operator_file_properties_create_ptr(paths); + + WM_operator_name_call_ptr( + C, file_handlers[0]->get_import_operator(), WM_OP_INVOKE_DEFAULT, &file_props, nullptr); + WM_operator_properties_free(&file_props); + return OPERATOR_FINISHED; +} + +static int wm_drop_import_file_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) +{ + const auto paths = drop_import_file_paths(op); + if (paths.size() < 1) { + return OPERATOR_CANCELLED; + } + + auto file_handlers = BKE_file_handlers_poll_file_drop(C, paths); + /** Execute directly if the files are only supported by a file handler. */ + if (file_handlers.size() == 1) { + return wm_drop_import_file_exec(C, op); + } + + /** Create a menu with file handler import operators that can support the files and let user + * decide what to use. */ + + uiPopupMenu *pup = UI_popup_menu_begin(C, "", ICON_NONE); + uiLayout *layout = UI_popup_menu_layout(pup); + uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT); + + for (auto *file_handler : file_handlers) { + PointerRNA file_props = file_handler->import_operator_file_properties_create_ptr(paths); + std::string label = fmt::format(TIP_("Import {}"), file_handler->label); + uiItemFullO_ptr(layout, + file_handler->get_import_operator(), + label.c_str(), + ICON_NONE, + static_cast(file_props.data), + WM_OP_INVOKE_DEFAULT, + UI_ITEM_NONE, + nullptr); + } + + UI_popup_menu_end(C, pup); + return OPERATOR_INTERFACE; +} + +void WM_OT_drop_import_file(wmOperatorType *ot) +{ + ot->name = "Drop to Import File"; + ot->description = "Manages file "; + ot->idname = "WM_OT_drop_import_file"; + ot->flag = OPTYPE_INTERNAL; + ot->exec = wm_drop_import_file_exec; + ot->invoke = wm_drop_import_file_invoke; + + WM_operator_properties_filesel(ot, + FILE_TYPE_FOLDER, + FILE_BLENDER, + FILE_OPENFILE, + WM_FILESEL_FILEPATH | WM_FILESEL_FILES | WM_FILESEL_DIRECTORY | + WM_FILESEL_SHOW_PROPS, + FILE_DEFAULTDISPLAY, + FILE_SORT_DEFAULT); +} + +void drop_import_file_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop) +{ + const auto paths = WM_drag_get_paths(drag); + + char dir[FILE_MAX]; + 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); + } +} + +static bool drop_import_file_poll(bContext *C, wmDrag *drag, const wmEvent * /*event*/) +{ + if (drag->type != WM_DRAG_PATH) { + return false; + } + const auto paths = WM_drag_get_paths(drag); + return BKE_file_handlers_poll_file_drop(C, paths).size() > 0; +} + +static char *drop_import_file_tooltip(bContext *C, + wmDrag *drag, + const int /*xy*/[2], + wmDropBox * /*drop*/) +{ + const auto paths = WM_drag_get_paths(drag); + + const auto file_handlers = BKE_file_handlers_poll_file_drop(C, paths); + if (file_handlers.size() == 0) { + return nullptr; + } + + if (file_handlers.size() == 1) { + std::string label = fmt::format(TIP_("Import {}"), file_handlers[0]->label); + return BLI_strdup(label.c_str()); + } + + return BLI_strdup(TIP_("Multiple file handlers can be used, drop to pick which to use")); +} + +void ED_dropbox_drop_import_file() +{ + ListBase *lb = WM_dropboxmap_find("Window", 0, 0); + WM_dropbox_add(lb, + "WM_OT_drop_import_file", + drop_import_file_poll, + drop_import_file_copy, + nullptr, + drop_import_file_tooltip); +} diff --git a/source/blender/editors/io/io_drop_import_file.hh b/source/blender/editors/io/io_drop_import_file.hh new file mode 100644 index 00000000000..edd5011ac4e --- /dev/null +++ b/source/blender/editors/io/io_drop_import_file.hh @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +struct wmOperatorType; + +void WM_OT_drop_import_file(wmOperatorType *ot); +void ED_dropbox_drop_import_file(); diff --git a/source/blender/editors/io/io_gpencil_import.cc b/source/blender/editors/io/io_gpencil_import.cc index f6b164bdaa3..c10c42a1669 100644 --- a/source/blender/editors/io/io_gpencil_import.cc +++ b/source/blender/editors/io/io_gpencil_import.cc @@ -36,6 +36,7 @@ # include "ED_gpencil_legacy.hh" # include "io_gpencil.hh" +# include "io_utils.hh" # include "gpencil_io.h" @@ -55,13 +56,6 @@ static bool wm_gpencil_import_svg_common_check(bContext * /*C*/, wmOperator *op) return false; } -static int wm_gpencil_import_svg_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) -{ - WM_event_add_fileselect(C, op); - - return OPERATOR_RUNNING_MODAL; -} - static int wm_gpencil_import_svg_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); @@ -136,13 +130,15 @@ static void ui_gpencil_import_svg_settings(uiLayout *layout, PointerRNA *imfptr) { uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); - uiLayout *col = uiLayoutColumn(layout, false); + uiLayout *box = uiLayoutBox(layout); + uiLayout *col = uiLayoutColumn(box, false); uiItemR(col, imfptr, "resolution", UI_ITEM_NONE, nullptr, ICON_NONE); uiItemR(col, imfptr, "scale", UI_ITEM_NONE, nullptr, ICON_NONE); } -static void wm_gpencil_import_svg_draw(bContext * /*C*/, wmOperator *op) +static void wm_gpencil_import_svg_draw(bContext *C, wmOperator *op) { + io_util_drop_file_label_draw(C, op, ICON_FILE_3D, ".svg"); ui_gpencil_import_svg_settings(op->layout, op->ptr); } @@ -161,7 +157,7 @@ void WM_OT_gpencil_import_svg(wmOperatorType *ot) ot->description = "Import SVG into grease pencil"; ot->idname = "WM_OT_gpencil_import_svg"; - ot->invoke = wm_gpencil_import_svg_invoke; + ot->invoke = io_util_import_invoke; ot->exec = wm_gpencil_import_svg_exec; ot->poll = wm_gpencil_import_svg_poll; ot->ui = wm_gpencil_import_svg_draw; @@ -175,6 +171,8 @@ void WM_OT_gpencil_import_svg(wmOperatorType *ot) WM_FILESEL_DIRECTORY | WM_FILESEL_FILES, FILE_DEFAULTDISPLAY, FILE_SORT_DEFAULT); + io_util_skip_save_filesel_props( + ot, WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILES); RNA_def_int(ot->srna, "resolution", diff --git a/source/blender/editors/io/io_obj.cc b/source/blender/editors/io/io_obj.cc index 8a1d62e6ee1..0a36566615f 100644 --- a/source/blender/editors/io/io_obj.cc +++ b/source/blender/editors/io/io_obj.cc @@ -41,6 +41,7 @@ # include "IO_wavefront_obj.hh" # include "io_obj.hh" +# include "io_utils.hh" static const EnumPropertyItem io_obj_export_evaluation_mode[] = { {DAG_EVAL_RENDER, "DAG_EVAL_RENDER", 0, "Render", "Export objects as they appear in render"}, @@ -385,12 +386,6 @@ void WM_OT_obj_export(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN); } -static int wm_obj_import_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) -{ - WM_event_add_fileselect(C, op); - return OPERATOR_RUNNING_MODAL; -} - static int wm_obj_import_exec(bContext *C, wmOperator *op) { OBJImportParams import_params{}; @@ -485,9 +480,9 @@ void WM_OT_obj_import(wmOperatorType *ot) ot->name = "Import Wavefront OBJ"; ot->description = "Load a Wavefront OBJ scene"; ot->idname = "WM_OT_obj_import"; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_PRESET; + ot->flag = OPTYPE_UNDO | OPTYPE_PRESET; - ot->invoke = wm_obj_import_invoke; + ot->invoke = io_util_import_invoke; ot->exec = wm_obj_import_exec; ot->poll = WM_operator_winactive; ot->ui = wm_obj_import_draw; @@ -501,6 +496,9 @@ void WM_OT_obj_import(wmOperatorType *ot) FILE_DEFAULTDISPLAY, FILE_SORT_DEFAULT); + io_util_skip_save_filesel_props(ot, + WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILES); + RNA_def_float( ot->srna, "global_scale", diff --git a/source/blender/editors/io/io_ops.cc b/source/blender/editors/io/io_ops.cc index b255f43c7ce..50592214fab 100644 --- a/source/blender/editors/io/io_ops.cc +++ b/source/blender/editors/io/io_ops.cc @@ -23,6 +23,7 @@ #endif #include "io_cache.hh" +#include "io_drop_import_file.hh" #include "io_gpencil.hh" #include "io_obj.hh" #include "io_ply_ops.hh" @@ -74,4 +75,6 @@ void ED_operatortypes_io() WM_operatortype_append(WM_OT_stl_import); WM_operatortype_append(WM_OT_stl_export); #endif + WM_operatortype_append(WM_OT_drop_import_file); + ED_dropbox_drop_import_file(); } diff --git a/source/blender/editors/io/io_ply_ops.cc b/source/blender/editors/io/io_ply_ops.cc index c489769f1d3..c8d35ab94d5 100644 --- a/source/blender/editors/io/io_ply_ops.cc +++ b/source/blender/editors/io/io_ply_ops.cc @@ -37,6 +37,7 @@ # include "IO_ply.hh" # include "io_ply_ops.hh" +# include "io_utils.hh" static const EnumPropertyItem ply_vertex_colors_mode[] = { {PLY_VERTEX_COLOR_NONE, "NONE", 0, "None", "Do not import/export color attributes"}, @@ -236,11 +237,6 @@ void WM_OT_ply_export(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN); } -static int wm_ply_import_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - return WM_operator_filesel(C, op, event); -} - static int wm_ply_import_exec(bContext *C, wmOperator *op) { PLYImportParams params{}; @@ -286,6 +282,29 @@ static int wm_ply_import_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static void ui_ply_import_settings(uiLayout *layout, PointerRNA *imfptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + + uiLayout *box = uiLayoutBox(layout); + uiLayout *col = uiLayoutColumn(box, false); + uiItemR(col, imfptr, "global_scale", UI_ITEM_NONE, NULL, ICON_NONE); + uiItemR(col, imfptr, "use_scene_unit", UI_ITEM_NONE, NULL, ICON_NONE); + uiItemR(col, imfptr, "forward_axis", UI_ITEM_NONE, IFACE_("Forward Axis"), ICON_NONE); + uiItemR(col, imfptr, "up_axis", UI_ITEM_NONE, NULL, ICON_NONE); + uiItemR(col, imfptr, "merge_verts", UI_ITEM_NONE, NULL, ICON_NONE); + uiItemR(col, imfptr, "import_colors", UI_ITEM_NONE, NULL, ICON_NONE); +} + +static void wm_ply_import_draw(bContext *C, wmOperator *op) +{ + wmWindowManager *wm = CTX_wm_manager(C); + PointerRNA ptr = RNA_pointer_create(&wm->id, op->type->srna, op->properties); + io_util_drop_file_label_draw(C, op, ICON_FILE_3D, ".ply"); + ui_ply_import_settings(op->layout, &ptr); +} + void WM_OT_ply_import(wmOperatorType *ot) { PropertyRNA *prop; @@ -294,10 +313,11 @@ void WM_OT_ply_import(wmOperatorType *ot) ot->description = "Import an PLY file as an object"; ot->idname = "WM_OT_ply_import"; - ot->invoke = wm_ply_import_invoke; + ot->invoke = io_util_import_invoke; ot->exec = wm_ply_import_exec; ot->poll = WM_operator_winactive; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_PRESET; + ot->ui = wm_ply_import_draw; + ot->flag = OPTYPE_UNDO | OPTYPE_PRESET; WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER, @@ -307,6 +327,8 @@ void WM_OT_ply_import(wmOperatorType *ot) WM_FILESEL_SHOW_PROPS, FILE_DEFAULTDISPLAY, FILE_SORT_DEFAULT); + io_util_skip_save_filesel_props(ot, + WM_FILESEL_FILEPATH | WM_FILESEL_FILES | WM_FILESEL_DIRECTORY); RNA_def_float(ot->srna, "global_scale", 1.0f, 1e-6f, 1e6f, "Scale", "", 0.001f, 1000.0f); RNA_def_boolean(ot->srna, diff --git a/source/blender/editors/io/io_stl_ops.cc b/source/blender/editors/io/io_stl_ops.cc index 93447394bca..5afc509d491 100644 --- a/source/blender/editors/io/io_stl_ops.cc +++ b/source/blender/editors/io/io_stl_ops.cc @@ -29,6 +29,7 @@ # include "IO_stl.hh" # include "io_stl_ops.hh" +# include "io_utils.hh" static int wm_stl_export_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/) { @@ -237,6 +238,29 @@ static bool wm_stl_import_check(bContext * /*C*/, wmOperator *op) return false; } +static void ui_stl_import_settings(uiLayout *layout, PointerRNA *imfptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + + uiLayout *box = uiLayoutBox(layout); + uiLayout *col = uiLayoutColumn(box, false); + uiItemR(col, imfptr, "global_scale", UI_ITEM_NONE, NULL, ICON_NONE); + uiItemR(col, imfptr, "use_scene_unit", UI_ITEM_NONE, NULL, ICON_NONE); + uiItemR(col, imfptr, "use_facet_normal", UI_ITEM_NONE, NULL, ICON_NONE); + uiItemR(col, imfptr, "forward_axis", UI_ITEM_NONE, IFACE_("Forward Axis"), ICON_NONE); + uiItemR(col, imfptr, "up_axis", UI_ITEM_NONE, NULL, ICON_NONE); + uiItemR(col, imfptr, "use_mesh_validate", UI_ITEM_NONE, NULL, ICON_NONE); +} + +static void wm_stl_import_draw(bContext *C, wmOperator *op) +{ + wmWindowManager *wm = CTX_wm_manager(C); + PointerRNA ptr = RNA_pointer_create(&wm->id, op->type->srna, op->properties); + io_util_drop_file_label_draw(C, op, ICON_FILE_3D, ".stl"); + ui_stl_import_settings(op->layout, &ptr); +} + void WM_OT_stl_import(wmOperatorType *ot) { PropertyRNA *prop; @@ -249,7 +273,8 @@ void WM_OT_stl_import(wmOperatorType *ot) ot->exec = wm_stl_import_exec; ot->poll = WM_operator_winactive; ot->check = wm_stl_import_check; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_PRESET; + ot->ui = wm_stl_import_draw; + ot->flag = OPTYPE_UNDO | OPTYPE_PRESET; WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER, @@ -259,6 +284,8 @@ void WM_OT_stl_import(wmOperatorType *ot) WM_FILESEL_SHOW_PROPS, FILE_DEFAULTDISPLAY, FILE_SORT_DEFAULT); + io_util_skip_save_filesel_props(ot, + WM_FILESEL_FILEPATH | WM_FILESEL_FILES | WM_FILESEL_DIRECTORY); RNA_def_float(ot->srna, "global_scale", 1.0f, 1e-6f, 1e6f, "Scale", "", 0.001f, 1000.0f); RNA_def_boolean(ot->srna, diff --git a/source/blender/editors/io/io_usd.cc b/source/blender/editors/io/io_usd.cc index a7a2f58a1b4..28b7b789b0a 100644 --- a/source/blender/editors/io/io_usd.cc +++ b/source/blender/editors/io/io_usd.cc @@ -42,6 +42,7 @@ # include "DEG_depsgraph.hh" # include "io_usd.hh" +# include "io_utils.hh" # include "usd.h" # include @@ -393,7 +394,7 @@ static int wm_usd_import_invoke(bContext *C, wmOperator *op, const wmEvent *even options->as_background_job = true; op->customdata = options; - return WM_operator_filesel(C, op, event); + return io_util_import_invoke(C, op, event); } static int wm_usd_import_exec(bContext *C, wmOperator *op) @@ -532,11 +533,13 @@ static void wm_usd_import_cancel(bContext * /*C*/, wmOperator *op) free_operator_customdata(op); } -static void wm_usd_import_draw(bContext * /*C*/, wmOperator *op) +static void wm_usd_import_draw(bContext *C, wmOperator *op) { uiLayout *layout = op->layout; PointerRNA *ptr = op->ptr; + io_util_drop_file_label_draw(C, op, ICON_FILE_3D, ".usd"); + uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); @@ -608,7 +611,7 @@ void WM_OT_usd_import(wmOperatorType *ot) ot->poll = WM_operator_winactive; ot->ui = wm_usd_import_draw; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_PRESET; + ot->flag = OPTYPE_UNDO | OPTYPE_PRESET; WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_USD, @@ -617,6 +620,7 @@ void WM_OT_usd_import(wmOperatorType *ot) WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH | WM_FILESEL_SHOW_PROPS, FILE_DEFAULTDISPLAY, FILE_SORT_DEFAULT); + io_util_skip_save_filesel_props(ot, WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH); PropertyRNA *prop = RNA_def_string(ot->srna, "filter_glob", "*.usd", 0, "", ""); RNA_def_property_flag(prop, PROP_HIDDEN); diff --git a/source/blender/editors/io/io_utils.cc b/source/blender/editors/io/io_utils.cc new file mode 100644 index 00000000000..2e3baf50db2 --- /dev/null +++ b/source/blender/editors/io/io_utils.cc @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_context.hh" + +#include "BLI_path_util.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "BLT_translation.h" + +#include "DNA_space_types.h" + +#include "ED_fileselect.hh" + +#include "RNA_access.hh" +#include "RNA_define.hh" + +#include "UI_interface.hh" + +#include "WM_api.hh" + +#include "io_utils.hh" + +int io_util_import_invoke(bContext *C, wmOperator *op, const wmEvent * /* event */) +{ + + PropertyRNA *filepath_prop = RNA_struct_find_property(op->ptr, "filepath"); + PropertyRNA *directory_prop = RNA_struct_find_property(op->ptr, "directory"); + if ((filepath_prop && RNA_property_is_set(op->ptr, filepath_prop)) || + (directory_prop && RNA_property_is_set(op->ptr, directory_prop))) + { + return WM_operator_props_dialog_popup(C, op, 300); + } + + WM_event_add_fileselect(C, op); + return OPERATOR_RUNNING_MODAL; +} + +void io_util_skip_save_filesel_props(wmOperatorType *ot, const eFileSel_Flag flag) +{ + PropertyRNA *prop; + if (flag & WM_FILESEL_FILEPATH) { + prop = RNA_struct_type_find_property(ot->srna, "filepath"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + } + if (flag & WM_FILESEL_FILENAME) { + prop = RNA_struct_type_find_property(ot->srna, "filename"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + } + if (flag & WM_FILESEL_DIRECTORY) { + prop = RNA_struct_type_find_property(ot->srna, "directory"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + } + if (flag & WM_FILESEL_FILES) { + prop = RNA_struct_type_find_property(ot->srna, "files"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + } + if (flag & WM_FILESEL_RELPATH) { + prop = RNA_struct_type_find_property(ot->srna, "relative_path"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + } +} + +void io_util_drop_file_label_draw(bContext *C, wmOperator *op, int icon, const char *extension) +{ + ScrArea *area = CTX_wm_area(C); + + if (area->spacetype == SPACE_FILE) { + return; + } + + char label[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", label); + + if (RNA_struct_find_property(op->ptr, "directory") && + RNA_collection_length(op->ptr, "files") > 1) { + sprintf(label, "%d %s files dropped.", RNA_collection_length(op->ptr, "files"), extension); + } + + uiLayout *box = uiLayoutBox(op->layout); + uiItemL(box, label, icon); +} diff --git a/source/blender/editors/io/io_utils.hh b/source/blender/editors/io/io_utils.hh new file mode 100644 index 00000000000..76713d40fd6 --- /dev/null +++ b/source/blender/editors/io/io_utils.hh @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "WM_types.hh" + +struct wmOperator; +struct wmOperatorType; +struct wmDrag; +struct wmDropBox; + +int io_util_import_invoke(bContext *C, wmOperator *op, const wmEvent *event); +void io_util_skip_save_filesel_props(wmOperatorType *ot, const eFileSel_Flag flag); +void io_util_drop_file_label_draw(bContext *C, wmOperator *op, int icon, const char *extension); diff --git a/source/blender/editors/screen/screen_ops.cc b/source/blender/editors/screen/screen_ops.cc index d252ec011a4..5730fc22f9d 100644 --- a/source/blender/editors/screen/screen_ops.cc +++ b/source/blender/editors/screen/screen_ops.cc @@ -5944,7 +5944,7 @@ static bool blend_file_drop_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * static void blend_file_drop_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop) { /* copy drag path to properties */ - RNA_string_set(drop->ptr, "filepath", WM_drag_get_path(drag)); + RNA_string_set(drop->ptr, "filepath", WM_drag_get_single_path(drag)); } void ED_keymap_screen(wmKeyConfig *keyconf) diff --git a/source/blender/editors/space_clip/space_clip.cc b/source/blender/editors/space_clip/space_clip.cc index fff564cc7ac..1fc04ba3ba7 100644 --- a/source/blender/editors/space_clip/space_clip.cc +++ b/source/blender/editors/space_clip/space_clip.cc @@ -536,7 +536,7 @@ static void clip_drop_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop) PointerRNA itemptr; char dir[FILE_MAX], file[FILE_MAX]; - BLI_path_split_dir_file(WM_drag_get_path(drag), dir, sizeof(dir), file, sizeof(file)); + BLI_path_split_dir_file(WM_drag_get_single_path(drag), dir, sizeof(dir), file, sizeof(file)); RNA_string_set(drop->ptr, "directory", dir); diff --git a/source/blender/editors/space_console/space_console.cc b/source/blender/editors/space_console/space_console.cc index aeee63af694..9bce3be7877 100644 --- a/source/blender/editors/space_console/space_console.cc +++ b/source/blender/editors/space_console/space_console.cc @@ -174,7 +174,7 @@ static bool path_drop_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*eve static void path_drop_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop) { char pathname[FILE_MAX + 2]; - SNPRINTF(pathname, "\"%s\"", WM_drag_get_path(drag)); + SNPRINTF(pathname, "\"%s\"", WM_drag_get_single_path(drag)); RNA_string_set(drop->ptr, "text", pathname); } diff --git a/source/blender/editors/space_file/space_file.cc b/source/blender/editors/space_file/space_file.cc index fed15290320..424e9e43850 100644 --- a/source/blender/editors/space_file/space_file.cc +++ b/source/blender/editors/space_file/space_file.cc @@ -797,7 +797,7 @@ static bool filepath_drop_poll(bContext *C, wmDrag *drag, const wmEvent * /*even static void filepath_drop_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop) { - RNA_string_set(drop->ptr, "filepath", WM_drag_get_path(drag)); + RNA_string_set(drop->ptr, "filepath", WM_drag_get_single_path(drag)); } /* region dropbox definition */ diff --git a/source/blender/editors/space_image/space_image.cc b/source/blender/editors/space_image/space_image.cc index 64cc8b79774..ecca3d2dab2 100644 --- a/source/blender/editors/space_image/space_image.cc +++ b/source/blender/editors/space_image/space_image.cc @@ -268,7 +268,7 @@ static bool image_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) static void image_drop_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop) { /* copy drag path to properties */ - RNA_string_set(drop->ptr, "filepath", WM_drag_get_path(drag)); + RNA_string_set(drop->ptr, "filepath", WM_drag_get_single_path(drag)); } /* area+region dropbox definition */ diff --git a/source/blender/editors/space_node/space_node.cc b/source/blender/editors/space_node/space_node.cc index c1321dd723a..db74db961f4 100644 --- a/source/blender/editors/space_node/space_node.cc +++ b/source/blender/editors/space_node/space_node.cc @@ -897,7 +897,7 @@ static void node_id_path_drop_copy(bContext *C, wmDrag *drag, wmDropBox *drop) return; } - const char *path = WM_drag_get_path(drag); + const char *path = WM_drag_get_single_path(drag); if (path) { RNA_string_set(drop->ptr, "filepath", path); RNA_struct_property_unset(drop->ptr, "name"); diff --git a/source/blender/editors/space_sequencer/sequencer_drag_drop.cc b/source/blender/editors/space_sequencer/sequencer_drag_drop.cc index 38b23e7748c..8718d241688 100644 --- a/source/blender/editors/space_sequencer/sequencer_drag_drop.cc +++ b/source/blender/editors/space_sequencer/sequencer_drag_drop.cc @@ -249,7 +249,7 @@ static void sequencer_drop_copy(bContext *C, wmDrag *drag, wmDropBox *drop) return; } - const char *path = WM_drag_get_path(drag); + const char *path = WM_drag_get_single_path(drag); /* Path dropped. */ if (path) { if (RNA_struct_find_property(drop->ptr, "filepath")) { @@ -335,7 +335,7 @@ static void get_drag_path(const bContext *C, wmDrag *drag, char r_path[FILE_MAX] BLI_path_abs(r_path, BKE_main_blendfile_path_from_global()); } else { - BLI_strncpy(r_path, WM_drag_get_path(drag), FILE_MAX); + BLI_strncpy(r_path, WM_drag_get_single_path(drag), FILE_MAX); } } diff --git a/source/blender/editors/space_text/space_text.cc b/source/blender/editors/space_text/space_text.cc index 938ac4bd5d2..45fde446df3 100644 --- a/source/blender/editors/space_text/space_text.cc +++ b/source/blender/editors/space_text/space_text.cc @@ -316,7 +316,7 @@ static bool text_drop_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*eve static void text_drop_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop) { /* copy drag path to properties */ - RNA_string_set(drop->ptr, "filepath", WM_drag_get_path(drag)); + RNA_string_set(drop->ptr, "filepath", WM_drag_get_single_path(drag)); } static bool text_drop_paste_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/) diff --git a/source/blender/editors/space_view3d/space_view3d.cc b/source/blender/editors/space_view3d/space_view3d.cc index 4cba9425ce4..dc7bd693d00 100644 --- a/source/blender/editors/space_view3d/space_view3d.cc +++ b/source/blender/editors/space_view3d/space_view3d.cc @@ -893,7 +893,7 @@ static void view3d_id_path_drop_copy(bContext *C, wmDrag *drag, wmDropBox *drop) RNA_struct_property_unset(drop->ptr, "filepath"); return; } - const char *path = WM_drag_get_path(drag); + const char *path = WM_drag_get_single_path(drag); if (path) { RNA_string_set(drop->ptr, "filepath", path); RNA_struct_property_unset(drop->ptr, "image"); diff --git a/source/blender/windowmanager/WM_api.hh b/source/blender/windowmanager/WM_api.hh index f060c912da1..24a538b8fbe 100644 --- a/source/blender/windowmanager/WM_api.hh +++ b/source/blender/windowmanager/WM_api.hh @@ -1424,13 +1424,14 @@ const ListBase *WM_drag_asset_list_get(const wmDrag *drag); const char *WM_drag_get_item_name(wmDrag *drag); -/* Path drag and drop. */ +/* Paths drag and drop. */ /** - * \param path: The path to drag. Value will be copied into the drag data so the passed string may - * be destructed. + * \param paths: The paths to drag. Values will be copied into the drag data so the passed strings + * may be destructed. Only paths that share the same extension of the first file will be copied. */ -wmDragPath *WM_drag_create_path_data(const char *path); -const char *WM_drag_get_path(const wmDrag *drag); +wmDragPath *WM_drag_create_path_data(blender::Span paths); +const char *WM_drag_get_single_path(const wmDrag *drag); +const blender::Span WM_drag_get_paths(const wmDrag *drag); /** * Note that even though the enum return type uses bit-flags, this should never have multiple * type-bits set, so `ELEM()` like comparison is possible. diff --git a/source/blender/windowmanager/WM_types.hh b/source/blender/windowmanager/WM_types.hh index 90a89188ba8..5636af8c641 100644 --- a/source/blender/windowmanager/WM_types.hh +++ b/source/blender/windowmanager/WM_types.hh @@ -111,6 +111,7 @@ struct wmWindowManager; #include "BLI_compiler_attrs.h" #include "BLI_utildefines.h" +#include "BLI_vector.hh" #include "DNA_listBase.h" #include "DNA_uuid_types.h" #include "DNA_vec_types.h" @@ -1179,10 +1180,15 @@ struct wmDragAssetListItem { }; struct wmDragPath { - char *path; + const blender::Vector paths; /* Note that even though the enum type uses bit-flags, this should never have multiple type-bits * set, so `ELEM()` like comparison is possible. */ - int file_type; /* eFileSel_File_Types */ + const int file_type; /* eFileSel_File_Types */ + const std::string tooltip; + wmDragPath(const blender::Vector &paths, const std::string &tooltip, int file_type) + : paths(std::move(paths)), file_type(file_type), tooltip(tooltip) + { + } }; struct wmDragGreasePencilLayer { diff --git a/source/blender/windowmanager/intern/wm_dragdrop.cc b/source/blender/windowmanager/intern/wm_dragdrop.cc index 9eb739e7c20..2d9a19b8192 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.cc +++ b/source/blender/windowmanager/intern/wm_dragdrop.cc @@ -762,29 +762,57 @@ const ListBase *WM_drag_asset_list_get(const wmDrag *drag) return &drag->asset_items; } -wmDragPath *WM_drag_create_path_data(const char *path) +wmDragPath *WM_drag_create_path_data(blender::Span paths) { - wmDragPath *path_data = MEM_new("wmDragPath"); - path_data->path = BLI_strdup(path); - path_data->file_type = ED_path_extension_type(path); + const char *extension = BLI_path_extension(paths[0]); + blender::Vector filtered_paths; + for (auto path : paths) { + const char *test_ext = BLI_path_extension(path); + if (extension == test_ext || (extension && test_ext && STREQ(extension, test_ext))) { + filtered_paths.append(path); + } + } + const char *tooltip = paths[0]; + char tooltip_buffer[256]; + if (filtered_paths.size() > 1) { + BLI_snprintf(tooltip_buffer, + ARRAY_SIZE(tooltip_buffer), + TIP_("Dragging %d %s files."), + filtered_paths.size(), + extension ? extension : TIP_("Folder")); + tooltip = tooltip_buffer; + } + + wmDragPath *path_data = MEM_new( + "wmDragPath", filtered_paths, tooltip, ED_path_extension_type(paths[0])); + return path_data; } static void wm_drag_free_path_data(wmDragPath **path_data) { - MEM_freeN((*path_data)->path); MEM_delete(*path_data); *path_data = nullptr; } -const char *WM_drag_get_path(const wmDrag *drag) +const char *WM_drag_get_single_path(const wmDrag *drag) { if (drag->type != WM_DRAG_PATH) { - return nullptr; + nullptr; } const wmDragPath *path_data = static_cast(drag->poin); - return path_data->path; + return path_data->paths[0].c_str(); +} + +const blender::Span WM_drag_get_paths(const wmDrag *drag) +{ + if (drag->type != WM_DRAG_PATH) { + return blender::Span(); + } + + const wmDragPath *path_data = static_cast(drag->poin); + return path_data->paths.as_span(); } int WM_drag_get_path_file_type(const wmDrag *drag) @@ -848,7 +876,7 @@ const char *WM_drag_get_item_name(wmDrag *drag) } case WM_DRAG_PATH: { const wmDragPath *path_drag_data = static_cast(drag->poin); - return path_drag_data->path; + return path_drag_data->tooltip.c_str(); } case WM_DRAG_NAME: return static_cast(drag->poin); diff --git a/source/blender/windowmanager/intern/wm_init_exit.cc b/source/blender/windowmanager/intern/wm_init_exit.cc index 41455d75098..e63b663f89a 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.cc +++ b/source/blender/windowmanager/intern/wm_init_exit.cc @@ -54,6 +54,7 @@ #include "BKE_addon.h" #include "BKE_appdir.h" +#include "BKE_file_handler.hh" #include "BKE_mask.h" /* free mask clipboard */ #include "BKE_material.h" /* BKE_material_copybuf_clear */ #include "BKE_studiolight.h" diff --git a/source/blender/windowmanager/intern/wm_window.cc b/source/blender/windowmanager/intern/wm_window.cc index 1e1830d22ce..489445f8ab1 100644 --- a/source/blender/windowmanager/intern/wm_window.cc +++ b/source/blender/windowmanager/intern/wm_window.cc @@ -1571,11 +1571,12 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_pt if (ddd->dataType == GHOST_kDragnDropTypeFilenames) { const GHOST_TStringArray *stra = static_cast(ddd->data); - for (int a = 0; a < stra->count; a++) { - printf("drop file %s\n", stra->strings[a]); + if (stra->count) { + printf("drop file %s\n", stra->strings[0]); /* try to get icon type from extension */ - int icon = ED_file_extension_icon((char *)stra->strings[a]); - wmDragPath *path_data = WM_drag_create_path_data((char *)stra->strings[a]); + int icon = ED_file_extension_icon((char *)stra->strings[0]); + wmDragPath *path_data = WM_drag_create_path_data( + blender::Span((char **)stra->strings, stra->count)); WM_event_start_drag(C, icon, WM_DRAG_PATH, path_data, 0.0, WM_DRAG_NOP); /* Void pointer should point to string, it makes a copy. */ break; /* only one drop element supported now */