Add support to Drag and Drop to FileHandlers #116047
81
doc/python_api/examples/bpy.types.FileHandler.1.py
Normal file
@ -0,0 +1,81 @@
|
||||
"""
|
||||
Basic FileHandler for Operator that imports just one file
|
||||
-----------------
|
||||
|
||||
When creating a ``Operator`` that imports files, you may want to
|
||||
add them 'drag-and-drop' support, File Handlers helps to define
|
||||
a set of files extensions (:class:`FileHandler.bl_file_extensions`)
|
||||
that the ``Operator`` support and a :class:`FileHandler.poll_drop`
|
||||
function that can be used to check in what specific context the ``Operator``
|
||||
can be invoked with 'drag-and-drop' filepath data.
|
||||
|
||||
Same as operators that uses the file select window, this operators
|
||||
required a set of properties, when the ``Operator`` can import just one
|
||||
file per execution it needs to define the following property:
|
||||
|
||||
.. code-block:: python
|
||||
filepath: bpy.props.StringProperty(subtype='FILE_PATH')
|
||||
|
||||
This ``filepath`` property now will be used by the ``FileHandler`` to
|
||||
set the 'drag-and-drop' filepath data.
|
||||
|
||||
"""
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
class CurveTextImport(bpy.types.Operator):
|
||||
""" Test importer that creates a text object from a .txt file """
|
||||
bl_idname = "curve.text_import"
|
||||
bl_label = "Import a text file as text object"
|
||||
|
||||
"""
|
||||
This Operator supports import one .txt file at the time, we need the
|
||||
following filepath property that the file handler will use to set file path data.
|
||||
"""
|
||||
filepath: bpy.props.StringProperty(subtype='FILE_PATH', options={'SKIP_SAVE'})
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (context.area and context.area.type == "VIEW_3D")
|
||||
|
||||
def execute(self, context):
|
||||
""" Calls to this Operator can set unfiltered filepaths, ensure the file extension is .txt. """
|
||||
if not self.filepath or not self.filepath.endswith(".txt"):
|
||||
return {'CANCELLED'}
|
||||
|
||||
with open(self.filepath) as file:
|
||||
text_curve = bpy.data.curves.new(type="FONT", name="Text")
|
||||
text_curve.body = ''.join(file.readlines())
|
||||
text_object = bpy.data.objects.new(name="Text", object_data=text_curve)
|
||||
bpy.context.scene.collection.objects.link(text_object)
|
||||
return {'FINISHED'}
|
||||
guishe marked this conversation as resolved
Outdated
|
||||
|
||||
"""
|
||||
By default the file handler invokes the operator with the filepath property set.
|
||||
In this example if this property is set the operator is executed, if not the
|
||||
file select window is invoked.
|
||||
This depends on setting 'options={'SKIP_SAVE'}' to the property options to avoid
|
||||
to reuse filepath data between operator calls.
|
||||
"""
|
||||
|
||||
def invoke(self, context, event):
|
||||
if self.filepath:
|
||||
return self.execute(context)
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
|
||||
class CURVE_FH_text_import(bpy.types.FileHandler):
|
||||
bl_idname = "CURVE_FH_text_import"
|
||||
bl_label = "File handler for curve text object import"
|
||||
bl_import_operator = "curve.text_import"
|
||||
bl_file_extensions = ".txt"
|
||||
|
||||
@classmethod
|
||||
def poll_drop(cls, context):
|
||||
return (context.area and context.area.type == 'VIEW_3D')
|
||||
|
||||
|
||||
bpy.utils.register_class(CurveTextImport)
|
||||
bpy.utils.register_class(CURVE_FH_text_import)
|
91
doc/python_api/examples/bpy.types.FileHandler.2.py
Normal file
@ -0,0 +1,91 @@
|
||||
"""
|
||||
Basic FileHandler for Operator that imports multiple files
|
||||
-----------------
|
||||
|
||||
Also operators can be invoked with multiple files from 'drag-and-drop',
|
||||
but for this it is require to define the following properties:
|
||||
|
||||
.. code-block:: python
|
||||
directory: StringProperty(subtype='FILE_PATH')
|
||||
files: CollectionProperty(type=bpy.types.OperatorFileListElement)
|
||||
|
||||
This ``directory`` and ``files`` properties now will be used by the
|
||||
``FileHandler`` to set 'drag-and-drop' filepath data.
|
||||
|
||||
"""
|
||||
|
||||
import bpy
|
||||
from mathutils import Vector
|
||||
|
||||
|
||||
class ShaderScriptImport(bpy.types.Operator):
|
||||
"""Test importer that creates scripts nodes from .txt files"""
|
||||
bl_idname = "shader.script_import"
|
||||
bl_label = "Import a text file as a script node"
|
||||
|
||||
"""
|
||||
This Operator can import multiple .txt files, we need following directory and files
|
||||
properties that the file handler will use to set files path data
|
||||
"""
|
||||
directory: bpy.props.StringProperty(subtype='FILE_PATH', options={'SKIP_SAVE'})
|
||||
files: bpy.props.CollectionProperty(type=bpy.types.OperatorFileListElement, options={'SKIP_SAVE'})
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return (context.region and context.region.type == 'WINDOW'
|
||||
and context.area and context.area.ui_type == 'ShaderNodeTree'
|
||||
and context.object and context.object.type == 'MESH'
|
||||
and context.material)
|
||||
|
||||
def execute(self, context):
|
||||
""" The directory property need to be set. """
|
||||
if not self.directory:
|
||||
return {'CANCELLED'}
|
||||
x = 0.0
|
||||
y = 0.0
|
||||
for file in self.files:
|
||||
"""
|
||||
Calls to the operator can set unfiltered file names,
|
||||
ensure the file extension is .txt
|
||||
"""
|
||||
if file.name.endswith(".txt"):
|
||||
node_tree = context.material.node_tree
|
||||
text_node = node_tree.nodes.new(type="ShaderNodeScript")
|
||||
text_node.mode = 'EXTERNAL'
|
||||
import os
|
||||
filepath = os.path.join(self.directory, file.name)
|
||||
text_node.filepath = filepath
|
||||
text_node.location = Vector((x, y))
|
||||
x += 20.0
|
||||
y -= 20.0
|
||||
return {'FINISHED'}
|
||||
|
||||
"""
|
||||
By default the file handler invokes the operator with the directory and files properties set.
|
||||
In this example if this properties are set the operator is executed, if not the
|
||||
file select window is invoked.
|
||||
This depends on setting 'options={'SKIP_SAVE'}' to the properties options to avoid
|
||||
guishe marked this conversation as resolved
Outdated
Jesse Yurkovich
commented
Adjust the casing of the name of this FileHandler too. Adjust the casing of the name of this FileHandler too.
|
||||
to reuse filepath data between operator calls.
|
||||
"""
|
||||
|
||||
def invoke(self, context, event):
|
||||
if self.directory:
|
||||
return self.execute(context)
|
||||
context.window_manager.fileselect_add(self)
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
|
||||
class SHADER_FH_script_import(bpy.types.FileHandler):
|
||||
bl_idname = "SHADER_FH_script_import"
|
||||
bl_label = "File handler for shader script node import"
|
||||
bl_import_operator = "shader.script_import"
|
||||
bl_file_extensions = ".txt"
|
||||
|
||||
@classmethod
|
||||
def poll_drop(cls, context):
|
||||
return (context.region and context.region.type == 'WINDOW'
|
||||
and context.area and context.area.ui_type == 'ShaderNodeTree')
|
||||
|
||||
|
||||
bpy.utils.register_class(ShaderScriptImport)
|
||||
bpy.utils.register_class(SHADER_FH_script_import)
|
@ -35,6 +35,11 @@ struct FileHandlerType {
|
||||
|
||||
/** RNA integration. */
|
||||
ExtensionRNA rna_ext;
|
||||
|
||||
/**
|
||||
* Return a vector of indices in #paths of file paths supported by the file handler.
|
||||
*/
|
||||
blender::Vector<int64_t> filter_supported_paths(const blender::Span<std::string> paths) const;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -58,4 +63,12 @@ void file_handler_remove(FileHandlerType *file_handler);
|
||||
/** Return pointers to all registered file handlers. */
|
||||
Span<std::unique_ptr<FileHandlerType>> file_handlers();
|
||||
|
||||
/**
|
||||
* Return a vector of file handlers that support any file path in `paths` and the call to
|
||||
* `poll_drop` returns #true. Caller must check if each file handler have a valid
|
||||
* `import_operator`.
|
||||
*/
|
||||
blender::Vector<FileHandlerType *> file_handlers_poll_file_drop(
|
||||
const bContext *C, const blender::Span<std::string> paths);
|
||||
|
||||
} // namespace blender::bke
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "BKE_file_handler.hh"
|
||||
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
|
||||
namespace blender::bke {
|
||||
@ -60,4 +61,58 @@ void file_handler_remove(FileHandlerType *file_handler)
|
||||
});
|
||||
}
|
||||
|
||||
blender::Vector<FileHandlerType *> file_handlers_poll_file_drop(
|
||||
const bContext *C, const blender::Span<std::string> paths)
|
||||
{
|
||||
blender::Vector<std::string> 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<FileHandlerType *> result;
|
||||
for (const std::unique_ptr<FileHandlerType> &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;
|
||||
}
|
||||
|
||||
result.append(&file_handler);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
blender::Vector<int64_t> FileHandlerType::filter_supported_paths(
|
||||
const blender::Span<std::string> paths) const
|
||||
{
|
||||
blender::Vector<int64_t> 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;
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
@ -17,6 +17,7 @@ set(INC
|
||||
../../io/wavefront_obj
|
||||
../../makesrna
|
||||
../../windowmanager
|
||||
${CMAKE_BINARY_DIR}/source/blender/makesrna
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
@ -27,6 +28,7 @@ set(SRC
|
||||
io_alembic.cc
|
||||
io_cache.cc
|
||||
io_collada.cc
|
||||
io_drop_import_file.cc
|
||||
io_gpencil_export.cc
|
||||
io_gpencil_import.cc
|
||||
io_gpencil_utils.cc
|
||||
@ -39,6 +41,7 @@ set(SRC
|
||||
io_alembic.hh
|
||||
io_cache.hh
|
||||
io_collada.hh
|
||||
io_drop_import_file.hh
|
||||
io_gpencil.hh
|
||||
io_obj.hh
|
||||
io_ops.hh
|
||||
@ -53,6 +56,7 @@ set(LIB
|
||||
PRIVATE bf::depsgraph
|
||||
PRIVATE bf::dna
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
PRIVATE bf::intern::clog
|
||||
)
|
||||
|
||||
if(WITH_OPENCOLLADA)
|
||||
|
250
source/blender/editors/io/io_drop_import_file.cc
Normal file
@ -0,0 +1,250 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "BKE_file_handler.hh"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_define.hh"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
|
||||
#include "io_drop_import_file.hh"
|
||||
|
||||
static CLG_LogRef LOG = {"io.drop_import_file"};
|
||||
|
||||
/* Retuns the list of file paths stored in #WM_OT_drop_import_file operator properties. */
|
||||
static blender::Vector<std::string> drop_import_file_paths(const wmOperator *op)
|
||||
{
|
||||
blender::Vector<std::string> 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;
|
||||
guishe marked this conversation as resolved
Outdated
Jesse Yurkovich
commented
paths.empty() paths.empty()
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
guishe marked this conversation as resolved
Outdated
Jesse Yurkovich
commented
file_handlers.empty() file_handlers.empty()
|
||||
* handlers have a valid import operator.
|
||||
*/
|
||||
static blender::Vector<blender::bke::FileHandlerType *> drop_import_file_poll_file_handlers(
|
||||
const bContext *C, const blender::Span<std::string> paths, const bool quiet = true)
|
||||
{
|
||||
using namespace blender;
|
||||
auto file_handlers = bke::file_handlers_poll_file_drop(C, paths);
|
||||
file_handlers.remove_if([quiet](const bke::FileHandlerType *file_handler) {
|
||||
return WM_operatortype_find(file_handler->import_operator, quiet) == nullptr;
|
||||
});
|
||||
return file_handlers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a RNA pointer for the `FileHandlerType.import_operator` and sets on it all supported
|
||||
* file paths from `paths`.
|
||||
*/
|
||||
static PointerRNA file_handler_import_operator_create_ptr(
|
||||
const blender::bke::FileHandlerType *file_handler, const blender::Span<std::string> 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);
|
||||
if (filepath_prop) {
|
||||
RNA_property_string_set(&props, filepath_prop, paths[supported_paths[0]].c_str());
|
||||
}
|
||||
|
||||
PropertyRNA *directory_prop = RNA_struct_find_property_check(props, "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);
|
||||
}
|
||||
|
||||
PropertyRNA *files_prop = RNA_struct_find_collection_property_check(
|
||||
props, "files", &RNA_OperatorFileListElement);
|
||||
if (files_prop) {
|
||||
RNA_property_collection_clear(&props, files_prop);
|
||||
for (const auto &index : supported_paths) {
|
||||
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_string_set(&item_ptr, "name", file);
|
||||
}
|
||||
}
|
||||
const bool has_any_filepath_prop = filepath_prop || directory_prop || files_prop;
|
||||
/**
|
||||
* The `directory` and `files` properties are both required for handling multiple files, if
|
||||
* only one is defined means that the other is missing.
|
||||
*/
|
||||
const bool has_missing_filepath_prop = bool(directory_prop) != bool(files_prop);
|
||||
|
||||
if (!has_any_filepath_prop || has_missing_filepath_prop) {
|
||||
const char *message =
|
||||
"Expected operator properties filepath or files and directory not found. Refer to "
|
||||
"FileHandler documentation for details.";
|
||||
CLOG_WARN(&LOG, TIP_(message));
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
static int wm_drop_import_file_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
static int wm_drop_import_file_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
||||
{
|
||||
const 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.size() == 1) {
|
||||
return wm_drop_import_file_exec(C, op);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
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<IDProperty *>(file_props.data),
|
||||
guishe marked this conversation as resolved
Jesse Yurkovich
commented
Wondering if maybe we can just place this inside Wondering if maybe we can just place this inside `ED_keymap_screen` as that's where the .blend file drag-drop box is added. Should use `SPACE_EMPTY` and `RGN_TYPE_WINDOW` values instead of 0 too.
Guillermo Venegas
commented
had to duplicate this was on had to duplicate `drop_import_file_poll_file_handlers` between `io_drop_import_file.cc`
and `screen_ops.cc`, only has the additional check for file handlers with valid operator.
this was on `BKE_file_handlers_poll_file_drop` but removing the `wm` dependency from `bke` dont know the right place to validate this
Jesse Yurkovich
commented
I didn't notice that my suggestion would cause this duplication. In that case I think it's fine to keep the WM_dropbox_add/ED_dropbox_drop_import_file call inside the "io_drop_import_file.cc" file as you originally had it. Other than that, things look fine. I didn't notice that my suggestion would cause this duplication. In that case I think it's fine to keep the WM_dropbox_add/ED_dropbox_drop_import_file call inside the "io_drop_import_file.cc" file as you originally had it.
Other than that, things look fine.
|
||||
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 = "Operator that allows file handlers to receive file drops";
|
||||
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;
|
||||
|
||||
PropertyRNA *prop;
|
||||
|
||||
prop = RNA_def_string_dir_path(
|
||||
ot->srna, "directory", nullptr, FILE_MAX, "Directory", "Directory of the file");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
|
||||
prop = RNA_def_collection_runtime(ot->srna, "files", &RNA_OperatorFileListElement, "Files", "");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
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 !drop_import_file_poll_file_handlers(C, paths, true).is_empty();
|
||||
}
|
||||
|
||||
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 = drop_import_file_poll_file_handlers(C, paths, true);
|
||||
if (file_handlers.size() == 1) {
|
||||
wmOperatorType *ot = WM_operatortype_find(file_handlers[0]->import_operator, false);
|
||||
return BLI_strdup(TIP_(ot->name));
|
||||
}
|
||||
|
||||
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", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
WM_dropbox_add(lb,
|
||||
"WM_OT_drop_import_file",
|
||||
drop_import_file_poll,
|
||||
drop_import_file_copy,
|
||||
nullptr,
|
||||
drop_import_file_tooltip);
|
||||
}
|
8
source/blender/editors/io/io_drop_import_file.hh
Normal file
@ -0,0 +1,8 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
void WM_OT_drop_import_file(wmOperatorType *ot);
|
||||
void ED_dropbox_drop_import_file();
|
@ -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();
|
||||
}
|
||||
|
@ -112,6 +112,22 @@ bool RNA_struct_idprops_contains_datablock(const StructRNA *type);
|
||||
bool RNA_struct_idprops_unset(PointerRNA *ptr, const char *identifier);
|
||||
|
||||
PropertyRNA *RNA_struct_find_property(PointerRNA *ptr, const char *identifier);
|
||||
|
||||
/**
|
||||
* Same as `RNA_struct_find_property` but returns `nullptr` if the property type is no same to
|
||||
* `property_type_check`.
|
||||
*/
|
||||
PropertyRNA *RNA_struct_find_property_check(PointerRNA &props,
|
||||
const char *name,
|
||||
const PropertyType property_type_check);
|
||||
/**
|
||||
* Same as `RNA_struct_find_property` but returns `nullptr` if the property type is not
|
||||
* #PropertyType::PROP_COLLECTION or property struct type is different to `struct_type_check`.
|
||||
*/
|
||||
PropertyRNA *RNA_struct_find_collection_property_check(PointerRNA &props,
|
||||
const char *name,
|
||||
const StructRNA *struct_type_check);
|
||||
|
||||
bool RNA_struct_contains_property(PointerRNA *ptr, PropertyRNA *prop_test);
|
||||
unsigned int RNA_struct_count_properties(StructRNA *srna);
|
||||
|
||||
|
@ -41,6 +41,8 @@
|
||||
#include "BKE_node.hh"
|
||||
#include "BKE_report.h"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_build.hh"
|
||||
|
||||
@ -61,6 +63,8 @@
|
||||
|
||||
const PointerRNA PointerRNA_NULL = {nullptr};
|
||||
|
||||
static CLG_LogRef LOG = {"rna.access"};
|
||||
|
||||
/* Init/Exit */
|
||||
|
||||
void RNA_init()
|
||||
@ -780,6 +784,86 @@ PropertyRNA *RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static const char *rna_property_type_identifier(PropertyType prop_type)
|
||||
{
|
||||
switch (prop_type) {
|
||||
case PROP_BOOLEAN:
|
||||
return RNA_struct_identifier(&RNA_BoolProperty);
|
||||
case PROP_INT:
|
||||
return RNA_struct_identifier(&RNA_IntProperty);
|
||||
case PROP_FLOAT:
|
||||
return RNA_struct_identifier(&RNA_FloatProperty);
|
||||
case PROP_STRING:
|
||||
return RNA_struct_identifier(&RNA_StringProperty);
|
||||
case PROP_ENUM:
|
||||
return RNA_struct_identifier(&RNA_EnumProperty);
|
||||
case PROP_POINTER:
|
||||
return RNA_struct_identifier(&RNA_PointerProperty);
|
||||
case PROP_COLLECTION:
|
||||
return RNA_struct_identifier(&RNA_CollectionProperty);
|
||||
default:
|
||||
return RNA_struct_identifier(&RNA_Property);
|
||||
}
|
||||
}
|
||||
|
||||
PropertyRNA *RNA_struct_find_property_check(PointerRNA &props,
|
||||
const char *name,
|
||||
const PropertyType property_type_check)
|
||||
{
|
||||
PropertyRNA *prop = RNA_struct_find_property(&props, name);
|
||||
if (!prop) {
|
||||
return nullptr;
|
||||
}
|
||||
const PropertyType prop_type = RNA_property_type(prop);
|
||||
if (prop_type == property_type_check) {
|
||||
return prop;
|
||||
}
|
||||
CLOG_WARN(&LOG,
|
||||
TIP_("'%s : %s()' expected, got '%s : %s()'"),
|
||||
name,
|
||||
rna_property_type_identifier(property_type_check),
|
||||
name,
|
||||
rna_property_type_identifier(prop_type));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PropertyRNA *RNA_struct_find_collection_property_check(PointerRNA &props,
|
||||
const char *name,
|
||||
const StructRNA *struct_type_check)
|
||||
{
|
||||
PropertyRNA *prop = RNA_struct_find_property(&props, name);
|
||||
if (!prop) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const PropertyType prop_type = RNA_property_type(prop);
|
||||
const StructRNA *prop_struct_type = RNA_property_pointer_type(&props, prop);
|
||||
if (prop_type == PROP_COLLECTION && prop_struct_type == struct_type_check) {
|
||||
return prop;
|
||||
}
|
||||
|
||||
if (prop_type != PROP_COLLECTION) {
|
||||
CLOG_WARN(&LOG,
|
||||
TIP_("'%s : %s(type = %s)' expected, got '%s : %s()'"),
|
||||
name,
|
||||
rna_property_type_identifier(PROP_COLLECTION),
|
||||
RNA_struct_identifier(struct_type_check),
|
||||
name,
|
||||
rna_property_type_identifier(prop_type));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CLOG_WARN(&LOG,
|
||||
TIP_("'%s : %s(type = %s)' expected, got '%s : %s(type = %s)'."),
|
||||
name,
|
||||
rna_property_type_identifier(PROP_COLLECTION),
|
||||
RNA_struct_identifier(struct_type_check),
|
||||
name,
|
||||
rna_property_type_identifier(PROP_COLLECTION),
|
||||
RNA_struct_identifier(prop_struct_type));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PropertyRNA *rna_struct_find_nested(PointerRNA *ptr, StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *prop = nullptr;
|
||||
|
Should be
CURVE_FH_text_import
(here and a few other places etc.) to prevent "Warning: 'Curve_FH_text_import' doesn't have upper case alpha-numeric prefix"