From 10933d0d27342ddbdd924bca758d760148af0153 Mon Sep 17 00:00:00 2001 From: guishe Date: Wed, 12 Apr 2023 10:46:08 -0600 Subject: [PATCH 1/3] UI: Import obj files by drag and drop The patch allows users to quickly import obj files by drag and drop. For quick setup of the import, a popup dialog is displayed allowing the user to configure the import or use a operator preset. --- source/blender/editors/io/CMakeLists.txt | 2 + source/blender/editors/io/io_obj.c | 31 +++++-- source/blender/editors/io/io_obj.h | 1 + source/blender/editors/io/io_ops.c | 7 ++ source/blender/editors/io/io_utils.c | 103 +++++++++++++++++++++++ source/blender/editors/io/io_utils.h | 27 ++++++ 6 files changed, 163 insertions(+), 8 deletions(-) create mode 100644 source/blender/editors/io/io_utils.c create mode 100644 source/blender/editors/io/io_utils.h diff --git a/source/blender/editors/io/CMakeLists.txt b/source/blender/editors/io/CMakeLists.txt index e5368b6a792..c78a9b8b428 100644 --- a/source/blender/editors/io/CMakeLists.txt +++ b/source/blender/editors/io/CMakeLists.txt @@ -26,6 +26,7 @@ set(INC_SYS ) set(SRC + io_utils.c io_alembic.c io_cache.c io_collada.c @@ -38,6 +39,7 @@ set(SRC io_stl_ops.c io_usd.c + io_utils.h io_alembic.h io_cache.h io_collada.h diff --git a/source/blender/editors/io/io_obj.c b/source/blender/editors/io/io_obj.c index cf20f4bc768..bc55330ce09 100644 --- a/source/blender/editors/io/io_obj.c +++ b/source/blender/editors/io/io_obj.c @@ -38,6 +38,7 @@ # include "IO_path_util_types.h" # include "IO_wavefront_obj.h" # include "io_obj.h" +# include "io_utils.h" static const EnumPropertyItem io_obj_export_evaluation_mode[] = { {DAG_EVAL_RENDER, "DAG_EVAL_RENDER", 0, "Render", "Export objects as they appear in render"}, @@ -370,12 +371,6 @@ void WM_OT_obj_export(struct wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN); } -static int wm_obj_import_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - WM_event_add_fileselect(C, op); - return OPERATOR_RUNNING_MODAL; -} - static int wm_obj_import_exec(bContext *C, wmOperator *op) { struct OBJImportParams import_params; @@ -457,6 +452,7 @@ static void wm_obj_import_draw(bContext *C, wmOperator *op) PointerRNA ptr; wmWindowManager *wm = CTX_wm_manager(C); RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); + files_drop_info_draw(C, op, ICON_FILE_3D); ui_obj_import_settings(op->layout, &ptr); } @@ -467,9 +463,9 @@ void WM_OT_obj_import(struct 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 = wm_io_import_invoke; ot->exec = wm_obj_import_exec; ot->poll = WM_operator_winactive; ot->ui = wm_obj_import_draw; @@ -482,6 +478,7 @@ void WM_OT_obj_import(struct wmOperatorType *ot) WM_FILESEL_DIRECTORY | WM_FILESEL_FILES, FILE_DEFAULTDISPLAY, FILE_SORT_DEFAULT); + skip_save_import_paths_props(ot, WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILES); RNA_def_float( ot->srna, "global_scale", @@ -533,4 +530,22 @@ void WM_OT_obj_import(struct wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN); } +bool obj_file_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) +{ + if (drag->type == WM_DRAG_PATH) { + const eFileSel_File_Types file_type = WM_drag_get_path_file_type(drag); + if (file_type == FILE_TYPE_OBJECT_IO && + BLI_path_extension_check(WM_drag_get_path(drag), ".obj")) { + return true; + } + } + return false; +} + +void WM_obj_dropbox_add() +{ + ListBase *lb = WM_dropboxmap_find("Window", 0, 0); + WM_dropbox_add(lb, "WM_OT_obj_import", obj_file_drop_poll, files_drop_copy, NULL, NULL); +} + #endif /* WITH_IO_WAVEFRONT_OBJ */ diff --git a/source/blender/editors/io/io_obj.h b/source/blender/editors/io/io_obj.h index 30857b33f21..3b6cdc3cc1e 100644 --- a/source/blender/editors/io/io_obj.h +++ b/source/blender/editors/io/io_obj.h @@ -10,3 +10,4 @@ struct wmOperatorType; void WM_OT_obj_export(struct wmOperatorType *ot); void WM_OT_obj_import(struct wmOperatorType *ot); +void WM_obj_dropbox_add(); diff --git a/source/blender/editors/io/io_ops.c b/source/blender/editors/io/io_ops.c index d8c4d140ad2..e01c32ec5a0 100644 --- a/source/blender/editors/io/io_ops.c +++ b/source/blender/editors/io/io_ops.c @@ -33,18 +33,22 @@ void ED_operatortypes_io(void) /* Collada operators: */ WM_operatortype_append(WM_OT_collada_export); WM_operatortype_append(WM_OT_collada_import); + // WM_collada_dropbox_add(); #endif #ifdef WITH_ALEMBIC WM_operatortype_append(WM_OT_alembic_import); WM_operatortype_append(WM_OT_alembic_export); + // WM_alembic_dropbox_add(); #endif #ifdef WITH_USD WM_operatortype_append(WM_OT_usd_import); WM_operatortype_append(WM_OT_usd_export); + // WM_usd_dropbox_add(); #endif #ifdef WITH_IO_GPENCIL WM_operatortype_append(WM_OT_gpencil_import_svg); + // WM_gpencil_dropbox_add(); # ifdef WITH_PUGIXML WM_operatortype_append(WM_OT_gpencil_export_svg); # endif @@ -63,14 +67,17 @@ void ED_operatortypes_io(void) #ifdef WITH_IO_WAVEFRONT_OBJ WM_operatortype_append(WM_OT_obj_export); WM_operatortype_append(WM_OT_obj_import); + WM_obj_dropbox_add(); #endif #ifdef WITH_IO_PLY WM_operatortype_append(WM_OT_ply_export); WM_operatortype_append(WM_OT_ply_import); + // WM_ply_dropbox_add(); #endif #ifdef WITH_IO_STL WM_operatortype_append(WM_OT_stl_import); + // WM_stl_dropbox_add(); #endif } diff --git a/source/blender/editors/io/io_utils.c b/source/blender/editors/io/io_utils.c new file mode 100644 index 00000000000..deee0ae3e70 --- /dev/null +++ b/source/blender/editors/io/io_utils.c @@ -0,0 +1,103 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#if defined(WITH_COLLADA) || defined(WITH_IO_GPENCIL) || defined(WITH_IO_WAVEFRONT_OBJ) || \ + defined(WITH_IO_PLY) || defined(WITH_IO_STL) || defined(WITH_USD) + +# include "DNA_space_types.h" + +# include "BKE_context.h" + +# include "BLI_path_util.h" +# include "BLI_string.h" +# include "BLI_utildefines.h" +# include "BLT_translation.h" + +# include "ED_fileselect.h" + +# include "RNA_access.h" +# include "RNA_define.h" + +# include "UI_interface.h" + +# include "WM_api.h" +# include "io_utils.h" + +int wm_io_import_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + char filepath[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", filepath); + if (filepath[0]) { + return WM_operator_props_dialog_popup(C, op, 300); + } + WM_event_add_fileselect(C, op); + return OPERATOR_RUNNING_MODAL; +} + +void skip_save_import_paths_props(wmOperatorType *ot, const eFileSel_Flag flag) +{ + if (flag & WM_FILESEL_FILEPATH) { + RNA_def_property_flag(RNA_struct_type_find_property(ot->srna, "filepath"), PROP_SKIP_SAVE); + } + if (flag & WM_FILESEL_FILENAME) { + RNA_def_property_flag(RNA_struct_type_find_property(ot->srna, "filename"), PROP_SKIP_SAVE); + } + if (flag & WM_FILESEL_DIRECTORY) { + RNA_def_property_flag(RNA_struct_type_find_property(ot->srna, "directory"), PROP_SKIP_SAVE); + } + if (flag & WM_FILESEL_FILES) { + RNA_def_property_flag(RNA_struct_type_find_property(ot->srna, "files"), PROP_SKIP_SAVE); + } + if (flag & WM_FILESEL_RELPATH) { + RNA_def_property_flag(RNA_struct_type_find_property(ot->srna, "relative_path"), + PROP_SKIP_SAVE); + } +} + +void files_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *drop) +{ + + RNA_string_set(drop->ptr, "filepath", WM_drag_get_path(drag)); + + // TODO(@guishe): Add support for multiple drag&drop files import + char dir[FILE_MAX], file[FILE_MAX]; + BLI_split_dirfile(WM_drag_get_path(drag), dir, file, sizeof(dir), sizeof(file)); + + RNA_string_set(drop->ptr, "directory", dir); + + RNA_collection_clear(drop->ptr, "files"); + PointerRNA itemptr; + RNA_collection_add(drop->ptr, "files", &itemptr); + RNA_string_set(&itemptr, "name", file); +} + +void file_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *drop) +{ + RNA_string_set(drop->ptr, "filepath", WM_drag_get_path(drag)); +} + +void file_drop_info_draw(bContext *C, wmOperator *op, int icon) +{ + ScrArea *area = CTX_wm_area(C); + if (area->spacetype != SPACE_FILE) { + char filepath[FILE_MAX]; + RNA_string_get(op->ptr, "filepath", filepath); + uiLayout *box = uiLayoutBox(op->layout); + uiItemL(box, filepath, icon); + } +} + +void files_drop_info_draw(bContext *C, wmOperator *op, int icon) +{ + ScrArea *area = CTX_wm_area(C); + if (area->spacetype != SPACE_FILE) { + char label[FILE_MAX] = "Multiple files selected."; + + if (RNA_collection_length(op->ptr, "files") == 1) { + RNA_string_get(op->ptr, "filepath", label); + } + uiLayout *box = uiLayoutBox(op->layout); + uiItemL(box, label, icon); + } +} + +#endif diff --git a/source/blender/editors/io/io_utils.h b/source/blender/editors/io/io_utils.h new file mode 100644 index 00000000000..efd2768a00b --- /dev/null +++ b/source/blender/editors/io/io_utils.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#if defined(WITH_COLLADA) || defined(WITH_IO_GPENCIL) || defined(WITH_IO_WAVEFRONT_OBJ) || \ + defined(WITH_IO_PLY) || defined(WITH_IO_STL) || defined(WITH_USD) + +# include "WM_types.h" + +struct wmOperator; +struct wmOperatorType; +struct wmDrag; +struct wmDropBox; + +int wm_io_import_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)); + +void skip_save_import_paths_props(wmOperatorType *ot, const eFileSel_Flag flag); + +void file_drop_copy(bContext *C, wmDrag *drag, wmDropBox *drop); +// Function that copies file directories for operators that support importing multiple files +void files_drop_copy(bContext *C, wmDrag *drag, wmDropBox *drop); + +void file_drop_info_draw(bContext *C, wmOperator *op, int icon); +// Function that summarizes file information for operators that support importing multiple files +void files_drop_info_draw(bContext *C, wmOperator *op, int icon); + +#endif -- 2.30.2 From 2650becbb76e05c728f8fc1d91690eeb4fa72214 Mon Sep 17 00:00:00 2001 From: guishe Date: Fri, 21 Apr 2023 15:38:49 -0600 Subject: [PATCH 2/3] IO: Add support for multiple DragAndDrop files There are operators in blender that allow the user to import multiple files at the same time, however this functionality is only implemented when importing with blender's file browser, drag and drop only imports the first selected file. The patch adds support for drag and drop multiple files from external windows. Notes: 1. The files are filtered according to the extension of the first selected file. 2. Not all operators that import files support importing multiple files, so they will still import one. --- .../editors/interface/interface_drag.cc | 2 +- source/blender/editors/io/io_obj.c | 2 +- source/blender/editors/io/io_utils.c | 21 ++++--- source/blender/editors/screen/screen_ops.c | 4 +- .../blender/editors/space_clip/space_clip.cc | 2 +- .../editors/space_console/space_console.c | 2 +- .../blender/editors/space_file/space_file.c | 2 +- .../blender/editors/space_image/space_image.c | 2 +- .../blender/editors/space_node/space_node.cc | 2 +- .../space_sequencer/sequencer_drag_drop.c | 4 +- .../blender/editors/space_text/space_text.c | 2 +- .../editors/space_view3d/space_view3d.cc | 2 +- source/blender/windowmanager/WM_api.h | 12 ++-- source/blender/windowmanager/WM_types.h | 4 +- .../windowmanager/intern/wm_dragdrop.cc | 55 ++++++++++++++++--- .../blender/windowmanager/intern/wm_window.c | 7 +-- 16 files changed, 86 insertions(+), 39 deletions(-) diff --git a/source/blender/editors/interface/interface_drag.cc b/source/blender/editors/interface/interface_drag.cc index fc478ac106d..d8e161c9541 100644 --- a/source/blender/editors/interface/interface_drag.cc +++ b/source/blender/editors/interface/interface_drag.cc @@ -70,7 +70,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(&path, 1); but->dragflag |= UI_BUT_DRAGPOIN_FREE; } diff --git a/source/blender/editors/io/io_obj.c b/source/blender/editors/io/io_obj.c index bc55330ce09..7e1dd882279 100644 --- a/source/blender/editors/io/io_obj.c +++ b/source/blender/editors/io/io_obj.c @@ -535,7 +535,7 @@ bool obj_file_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED if (drag->type == WM_DRAG_PATH) { const eFileSel_File_Types file_type = WM_drag_get_path_file_type(drag); if (file_type == FILE_TYPE_OBJECT_IO && - BLI_path_extension_check(WM_drag_get_path(drag), ".obj")) { + BLI_path_extension_check(WM_drag_get_paths(drag)[0], ".obj")) { return true; } } diff --git a/source/blender/editors/io/io_utils.c b/source/blender/editors/io/io_utils.c index deee0ae3e70..2bfd8766d41 100644 --- a/source/blender/editors/io/io_utils.c +++ b/source/blender/editors/io/io_utils.c @@ -55,24 +55,27 @@ void skip_save_import_paths_props(wmOperatorType *ot, const eFileSel_Flag flag) void files_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *drop) { + const char **paths = WM_drag_get_paths(drag); + RNA_string_set(drop->ptr, "filepath", paths[0]); - RNA_string_set(drop->ptr, "filepath", WM_drag_get_path(drag)); - - // TODO(@guishe): Add support for multiple drag&drop files import char dir[FILE_MAX], file[FILE_MAX]; - BLI_split_dirfile(WM_drag_get_path(drag), dir, file, sizeof(dir), sizeof(file)); + BLI_split_dirfile(paths[0], dir, file, sizeof(dir), sizeof(file)); RNA_string_set(drop->ptr, "directory", dir); - RNA_collection_clear(drop->ptr, "files"); - PointerRNA itemptr; - RNA_collection_add(drop->ptr, "files", &itemptr); - RNA_string_set(&itemptr, "name", file); + + const int path_count = WM_drag_get_path_count(drag); + for (int i = 0; i < path_count; i++) { + BLI_split_dirfile(paths[i], dir, file, sizeof(dir), sizeof(file)); + PointerRNA itemptr; + RNA_collection_add(drop->ptr, "files", &itemptr); + RNA_string_set(&itemptr, "name", file); + } } void file_drop_copy(bContext *UNUSED(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_paths(drag)[0]); } void file_drop_info_draw(bContext *C, wmOperator *op, int icon) diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index cc33c6b847c..e5ea6a2019d 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -4564,7 +4564,7 @@ static void screen_animation_region_tag_redraw( ED_region_tag_redraw(region); } -//#define PROFILE_AUDIO_SYNCH +// #define PROFILE_AUDIO_SYNCH static int screen_animation_step_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) { @@ -5756,7 +5756,7 @@ static bool blend_file_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEven static void blend_file_drop_copy(bContext *UNUSED(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_paths(drag)[0]); } 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 5543730ec19..fd082e60b7b 100644 --- a/source/blender/editors/space_clip/space_clip.cc +++ b/source/blender/editors/space_clip/space_clip.cc @@ -527,7 +527,7 @@ static void clip_drop_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop) PointerRNA itemptr; char dir[FILE_MAX], file[FILE_MAX]; - BLI_split_dirfile(WM_drag_get_path(drag), dir, file, sizeof(dir), sizeof(file)); + BLI_split_dirfile(WM_drag_get_paths(drag)[0], dir, file, sizeof(dir), sizeof(file)); RNA_string_set(drop->ptr, "directory", dir); diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c index cf27155deb2..8283544d79f 100644 --- a/source/blender/editors/space_console/space_console.c +++ b/source/blender/editors/space_console/space_console.c @@ -172,7 +172,7 @@ static bool path_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNU static void path_drop_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *drop) { char pathname[FILE_MAX + 2]; - BLI_snprintf(pathname, sizeof(pathname), "\"%s\"", WM_drag_get_path(drag)); + BLI_snprintf(pathname, sizeof(pathname), "\"%s\"", WM_drag_get_paths(drag)[0]); RNA_string_set(drop->ptr, "text", pathname); } diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index fe7179f8b8a..c603993052e 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -783,7 +783,7 @@ static bool filepath_drop_poll(bContext *C, wmDrag *drag, const wmEvent *UNUSED( static void filepath_drop_copy(bContext *UNUSED(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_paths(drag)[0]); } /* region dropbox definition */ diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index 62f2de591ce..eafa35a8b89 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -268,7 +268,7 @@ static bool image_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) static void image_drop_copy(bContext *UNUSED(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_paths(drag)[0]); } /* 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 05a7a60fc7e..ff09fc72c1b 100644 --- a/source/blender/editors/space_node/space_node.cc +++ b/source/blender/editors/space_node/space_node.cc @@ -711,7 +711,7 @@ static void node_id_path_drop_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *dr return; } - const char *path = WM_drag_get_path(drag); + const char *path = WM_drag_get_paths(drag)[0]; 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.c b/source/blender/editors/space_sequencer/sequencer_drag_drop.c index 3f3737c5c6e..23c5695ee67 100644 --- a/source/blender/editors/space_sequencer/sequencer_drag_drop.c +++ b/source/blender/editors/space_sequencer/sequencer_drag_drop.c @@ -247,7 +247,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_paths(drag)[0]; /* Path dropped. */ if (path) { if (RNA_struct_find_property(drop->ptr, "filepath")) { @@ -335,7 +335,7 @@ static void get_drag_path(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_paths(drag)[0], FILE_MAX); } } diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c index 2b60ba768b0..66aca4607fd 100644 --- a/source/blender/editors/space_text/space_text.c +++ b/source/blender/editors/space_text/space_text.c @@ -312,7 +312,7 @@ static bool text_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNU static void text_drop_copy(bContext *UNUSED(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_paths(drag)[0]); } static bool text_drop_paste_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) diff --git a/source/blender/editors/space_view3d/space_view3d.cc b/source/blender/editors/space_view3d/space_view3d.cc index 08aae53128e..7d7c0414cc3 100644 --- a/source/blender/editors/space_view3d/space_view3d.cc +++ b/source/blender/editors/space_view3d/space_view3d.cc @@ -903,7 +903,7 @@ static void view3d_id_path_drop_copy(bContext * /*C*/, wmDrag *drag, wmDropBox * RNA_struct_property_unset(drop->ptr, "filepath"); return; } - const char *path = WM_drag_get_path(drag); + const char *path = WM_drag_get_paths(drag)[0]; if (path) { RNA_string_set(drop->ptr, "filepath", path); RNA_struct_property_unset(drop->ptr, "image"); diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 751eeccec96..db80ae9846c 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -1445,13 +1445,15 @@ const ListBase *WM_drag_asset_list_get(const wmDrag *drag); const char *WM_drag_get_item_name(struct 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. Value 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. + * \param path_count: the number of paths in #paths */ -wmDragPath *WM_drag_create_path_data(const char *path); -const char *WM_drag_get_path(const wmDrag *drag); +wmDragPath *WM_drag_create_path_data(const char **paths, int path_count); +const char **WM_drag_get_paths(const wmDrag *drag); +const int WM_drag_get_path_count(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.h b/source/blender/windowmanager/WM_types.h index 14ebbf5cef1..70b96c101ac 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -1133,7 +1133,9 @@ typedef struct wmDragAssetListItem { } wmDragAssetListItem; typedef struct wmDragPath { - char *path; + char **paths; + char *tooltip; + int path_count; /* 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 */ diff --git a/source/blender/windowmanager/intern/wm_dragdrop.cc b/source/blender/windowmanager/intern/wm_dragdrop.cc index 6575a2fc91d..a2f29f1b42d 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.cc +++ b/source/blender/windowmanager/intern/wm_dragdrop.cc @@ -750,29 +750,70 @@ 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(const char **paths, int path_count) { wmDragPath *path_data = MEM_new("wmDragPath"); - path_data->path = BLI_strdup(path); - path_data->file_type = ED_path_extension_type(path); + + const char *ext = BLI_path_extension(paths[0]); + + int valid_path_count = 1; + for (int i = 1; i < path_count; i++) { + if ((ext == nullptr && BLI_path_extension(paths[i]) == nullptr) || + (ext != nullptr && BLI_path_extension_check(paths[i], ext))) { + valid_path_count++; + } + } + path_data->paths = (char **)MEM_mallocN(valid_path_count * sizeof(char *), "paths"); + + int path_index = 0; + for (int i = 0; i < path_count; i++) { + if ((ext == nullptr && BLI_path_extension(paths[i]) == nullptr) || + (ext != nullptr && BLI_path_extension_check(paths[i], ext))) { + path_data->paths[path_index] = BLI_strdup(paths[i]); + path_index++; + } + } + + path_data->file_type = ED_path_extension_type(paths[0]); + path_data->path_count = valid_path_count; + if (path_count == 1) { + path_data->tooltip = BLI_strdup(paths[0]); + } + else { + const char *tooltip = TIP_("Dragging %d %s files."); + path_data->tooltip = BLI_sprintfN(tooltip, valid_path_count, ext); + } return path_data; } static void wm_drag_free_path_data(wmDragPath **path_data) { - MEM_freeN((*path_data)->path); + for (int i = 0; i < (*path_data)->path_count; i++) { + MEM_freeN((*path_data)->paths[i]); + } + MEM_freeN((*path_data)->paths); + MEM_freeN((*path_data)->tooltip); MEM_delete(*path_data); *path_data = nullptr; } -const char *WM_drag_get_path(const wmDrag *drag) +const char **WM_drag_get_paths(const wmDrag *drag) { if (drag->type != WM_DRAG_PATH) { return nullptr; } const wmDragPath *path_data = static_cast(drag->poin); - return path_data->path; + return const_cast(path_data->paths); +} +const int WM_drag_get_path_count(const wmDrag *drag) +{ + if (drag->type != WM_DRAG_PATH) { + return 0; + } + + const wmDragPath *path_data = static_cast(drag->poin); + return path_data->path_count; } int WM_drag_get_path_file_type(const wmDrag *drag) @@ -836,7 +877,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; } case WM_DRAG_NAME: return static_cast(drag->poin); diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 1ea110dfc28..000f333bf8b 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -1487,11 +1487,10 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_pt if (ddd->dataType == GHOST_kDragnDropTypeFilenames) { GHOST_TStringArray *stra = ddd->data; - for (int a = 0; a < stra->count; a++) { - printf("drop file %s\n", stra->strings[a]); + if (stra->count > 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((char **)stra->strings, stra->count); WM_event_start_drag(C, icon, WM_DRAG_PATH, path_data, 0.0, WM_DRAG_NOP); /* void poin should point to string, it makes a copy */ break; /* only one drop element supported now */ -- 2.30.2 From a402e5c94dea4bef0c1daa6e2a3b9e2476b64f34 Mon Sep 17 00:00:00 2001 From: guishe Date: Sat, 15 Apr 2023 01:40:52 -0600 Subject: [PATCH 3/3] UI: Add Drag & Drop Feedback on Windows and x11 On Windows and x11, Drag & Drop data can be obtained when the mouse enters in a window. This data can be used to provide feedback to the user, for example, if the data contains a file path, it can be displayed if an operation can be performed on the file on drop. --- extern/xdnd/CMakeLists.txt | 4 + extern/xdnd/xdnd.c | 432 +++++++++--------- extern/xdnd/xdnd.h | 4 +- intern/ghost/intern/GHOST_DropTargetWin32.cc | 3 +- intern/ghost/intern/GHOST_DropTargetX11.cc | 33 +- .../blender/windowmanager/intern/wm_window.c | 121 +++-- 6 files changed, 328 insertions(+), 269 deletions(-) diff --git a/extern/xdnd/CMakeLists.txt b/extern/xdnd/CMakeLists.txt index 9133fb14feb..0aae9a5c2a1 100644 --- a/extern/xdnd/CMakeLists.txt +++ b/extern/xdnd/CMakeLists.txt @@ -21,4 +21,8 @@ add_definitions( -DHAVE_SYS_TIME_H ) +if(WITH_INPUT_NDOF) + add_definitions(-DWITH_INPUT_NDOF) +endif() + blender_add_lib(extern_xdnd "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/extern/xdnd/xdnd.c b/extern/xdnd/xdnd.c index e8c51377e27..76048926007 100644 --- a/extern/xdnd/xdnd.c +++ b/extern/xdnd/xdnd.c @@ -627,7 +627,7 @@ static void xdnd_send_status (DndClass * dnd, Window window, Window from, int wi xevent.xany.display = dnd->display; xevent.xclient.window = window; xevent.xclient.message_type = dnd->XdndStatus; - xevent.xclient.format = 32; + xevent.xclient.format = 32; XDND_STATUS_TARGET_WIN (&xevent) = from; XDND_STATUS_WILL_ACCEPT_SET (&xevent, will_accept); @@ -1254,183 +1254,6 @@ Atom xdnd_drag (DndClass * dnd, Window from, Atom action, Atom * typelist) return result; } -/* returns non-zero if event is handled */ -int xdnd_handle_drop_events (DndClass * dnd, XEvent * xevent) -{ - int result = 0; - if (xevent->type == SelectionNotify) { - dnd_debug1 ("got SelectionNotify"); - if (xevent->xselection.property == dnd->Xdnd_NON_PROTOCOL_ATOM && dnd->stage == XDND_DROP_STAGE_CONVERTING) { - int error; - dnd_debug1 (" property is Xdnd_NON_PROTOCOL_ATOM - getting selection"); - error = xdnd_get_selection (dnd, dnd->dragger_window, xevent->xselection.property, xevent->xany.window); -/* error is not actually used, i think future versions of the protocol maybe should return - an error status to the calling window with the XdndFinished client message */ - if (dnd_version_at_least (dnd->dragging_version, 2)) { -#if XDND_VERSION >= 3 - xdnd_send_finished (dnd, dnd->dragger_window, dnd->dropper_toplevel, error); -#else - xdnd_send_finished (dnd, dnd->dragger_window, dnd->dropper_window, error); -#endif - dnd_debug1 (" sending finished"); - } - xdnd_xfree (dnd->dragger_typelist); - xdnd_reset (dnd); - dnd->stage = XDND_DROP_STAGE_IDLE; - result = 1; - } else { - dnd_debug1 (" property is not Xdnd_NON_PROTOCOL_ATOM - ignoring"); - } - } else if (xevent->type == ClientMessage) { - dnd_debug2 ("got ClientMessage to xevent->xany.window = %ld", xevent->xany.window); - if (xevent->xclient.message_type == dnd->XdndEnter) { - dnd_debug2 (" message_type is XdndEnter, version = %ld", XDND_ENTER_VERSION (xevent)); -#if XDND_VERSION >= 3 - if (XDND_ENTER_VERSION (xevent) < 3) - return 0; -#endif - xdnd_reset (dnd); - dnd->dragger_window = XDND_ENTER_SOURCE_WIN (xevent); -#if XDND_VERSION >= 3 - dnd->dropper_toplevel = xevent->xany.window; - dnd->dropper_window = 0; /* enter goes to the top level window only, - so we don't really know what the - sub window is yet */ -#else - dnd->dropper_window = xevent->xany.window; -#endif - xdnd_xfree (dnd->dragger_typelist); - if (XDND_ENTER_THREE_TYPES (xevent)) { - dnd_debug1 (" three types only"); - xdnd_get_three_types (dnd, xevent, &dnd->dragger_typelist); - } else { - dnd_debug1 (" more than three types - getting list"); - xdnd_get_type_list (dnd, dnd->dragger_window, &dnd->dragger_typelist); - } - if (dnd->dragger_typelist) - dnd->stage = XDND_DROP_STAGE_ENTERED; - else - dnd_debug1 (" typelist returned as zero!"); - dnd->dragging_version = XDND_ENTER_VERSION (xevent); - result = 1; - } else if (xevent->xclient.message_type == dnd->XdndLeave) { -#if XDND_VERSION >= 3 - if (xevent->xany.window == dnd->dropper_toplevel && dnd->dropper_window) - xevent->xany.window = dnd->dropper_window; -#endif - dnd_debug1 (" message_type is XdndLeave"); - if (dnd->dragger_window == XDND_LEAVE_SOURCE_WIN (xevent) && dnd->stage == XDND_DROP_STAGE_ENTERED) { - dnd_debug1 (" leaving"); - if (dnd->widget_apply_leave) - (*dnd->widget_apply_leave) (dnd, xevent->xany.window); - dnd->stage = XDND_DROP_STAGE_IDLE; - xdnd_xfree (dnd->dragger_typelist); - result = 1; - dnd->dropper_toplevel = dnd->dropper_window = 0; - } else { - dnd_debug1 (" wrong stage or from wrong window"); - } - } else if (xevent->xclient.message_type == dnd->XdndPosition) { - dnd_debug2 (" message_type is XdndPosition to %ld", xevent->xany.window); - if (dnd->dragger_window == XDND_POSITION_SOURCE_WIN (xevent) && dnd->stage == XDND_DROP_STAGE_ENTERED) { - int want_position; - Atom action; - XRectangle rectangle; - Window last_window; - last_window = dnd->dropper_window; -#if XDND_VERSION >= 3 -/* version 3 gives us the top-level window only. WE have to find the child that the pointer is over: */ - if (1 || xevent->xany.window != dnd->dropper_toplevel || !dnd->dropper_window) { - Window parent, child, new_child = 0; - dnd->dropper_toplevel = xevent->xany.window; - parent = dnd->root_window; - child = dnd->dropper_toplevel; - for (;;) { - int xd, yd; - new_child = 0; - if (!XTranslateCoordinates (dnd->display, parent, child, - XDND_POSITION_ROOT_X (xevent), XDND_POSITION_ROOT_Y (xevent), - &xd, &yd, &new_child)) - break; - if (!new_child) - break; - child = new_child; - } - dnd->dropper_window = xevent->xany.window = child; - dnd_debug2 (" child window translates to %ld", dnd->dropper_window); - } else if (xevent->xany.window == dnd->dropper_toplevel && dnd->dropper_window) { - xevent->xany.window = dnd->dropper_window; - dnd_debug2 (" child window previously found: %ld", dnd->dropper_window); - } -#endif - action = dnd->XdndActionCopy; - dnd->supported_action = dnd->XdndActionCopy; - dnd->x = XDND_POSITION_ROOT_X (xevent); - dnd->y = XDND_POSITION_ROOT_Y (xevent); - dnd->time = CurrentTime; - if (dnd_version_at_least (dnd->dragging_version, 1)) - dnd->time = XDND_POSITION_TIME (xevent); - if (dnd_version_at_least (dnd->dragging_version, 1)) - action = XDND_POSITION_ACTION (xevent); -#if XDND_VERSION >= 3 - if (last_window && last_window != xevent->xany.window) - if (dnd->widget_apply_leave) - (*dnd->widget_apply_leave) (dnd, last_window); -#endif - dnd->will_accept = (*dnd->widget_apply_position) (dnd, xevent->xany.window, dnd->dragger_window, - action, dnd->x, dnd->y, dnd->time, dnd->dragger_typelist, - &want_position, &dnd->supported_action, &dnd->desired_type, &rectangle); - dnd_debug2 (" will accept = %d", dnd->will_accept); -#if XDND_VERSION >= 3 - dnd_debug2 (" sending status of %ld", dnd->dropper_toplevel); - xdnd_send_status (dnd, dnd->dragger_window, dnd->dropper_toplevel, dnd->will_accept, - want_position, rectangle.x, rectangle.y, rectangle.width, rectangle.height, dnd->supported_action); -#else - dnd_debug2 (" sending status of %ld", xevent->xany.window); - xdnd_send_status (dnd, dnd->dragger_window, xevent->xany.window, dnd->will_accept, - want_position, rectangle.x, rectangle.y, rectangle.width, rectangle.height, dnd->supported_action); -#endif - result = 1; - } else { - dnd_debug1 (" wrong stage or from wrong window"); - } - } else if (xevent->xclient.message_type == dnd->XdndDrop) { -#if XDND_VERSION >= 3 - if (xevent->xany.window == dnd->dropper_toplevel && dnd->dropper_window) - xevent->xany.window = dnd->dropper_window; -#endif - dnd_debug1 (" message_type is XdndDrop"); - if (dnd->dragger_window == XDND_DROP_SOURCE_WIN (xevent) && dnd->stage == XDND_DROP_STAGE_ENTERED) { - dnd->time = CurrentTime; - if (dnd_version_at_least (dnd->dragging_version, 1)) - dnd->time = XDND_DROP_TIME (xevent); - if (dnd->will_accept) { - dnd_debug1 (" will_accept is true - converting selectiong"); - dnd_debug2 (" my window is %ld", dnd->dropper_window); - dnd_debug2 (" source window is %ld", dnd->dragger_window); - xdnd_convert_selection (dnd, dnd->dragger_window, dnd->dropper_window, dnd->desired_type); - dnd->stage = XDND_DROP_STAGE_CONVERTING; - } else { - dnd_debug1 (" will_accept is false - sending finished"); - if (dnd_version_at_least (dnd->dragging_version, 2)) { -#if XDND_VERSION >= 3 - xdnd_send_finished (dnd, dnd->dragger_window, dnd->dropper_toplevel, 1); -#else - xdnd_send_finished (dnd, dnd->dragger_window, xevent->xany.window, 1); -#endif - } - xdnd_xfree (dnd->dragger_typelist); - xdnd_reset (dnd); - dnd->stage = XDND_DROP_STAGE_IDLE; - } - result = 1; - } else { - dnd_debug1 (" wrong stage or from wrong window"); - } - } - } - return result; -} /* Following here is a sample implementation: Suppose we want a window @@ -1550,50 +1373,241 @@ static int widget_apply_position (DndClass * dnd, Window widgets_window, Window return 1; } -Atom xdnd_get_drop (Display * display, XEvent * xevent, Atom * typelist, Atom * actionlist, +void handle_update(DndClass * dnd, XEvent * xevent){ + dnd_debug2 (" message_type is XdndPosition to %ld", xevent->xany.window); + if (dnd->dragger_window == XDND_POSITION_SOURCE_WIN (xevent)) { + int want_position; + Atom action; + XRectangle rectangle; + Window last_window; + last_window = dnd->dropper_window; +#if XDND_VERSION >= 3 +/* version 3 gives us the top-level window only. WE have to find the child that the pointer is over: */ + if (1 || xevent->xany.window != dnd->dropper_toplevel || !dnd->dropper_window) { + Window parent, child, new_child = 0; + dnd->dropper_toplevel = xevent->xany.window; + parent = dnd->root_window; + child = dnd->dropper_toplevel; + for (;;) { + int xd, yd; + new_child = 0; + if (!XTranslateCoordinates (dnd->display, parent, child, + XDND_POSITION_ROOT_X (xevent), XDND_POSITION_ROOT_Y (xevent), + &xd, &yd, &new_child)) + break; + if (!new_child) + break; + child = new_child; + } + dnd->dropper_window = xevent->xany.window = child; + dnd_debug2 (" child window translates to %ld", dnd->dropper_window); + } else if (xevent->xany.window == dnd->dropper_toplevel && dnd->dropper_window) { + xevent->xany.window = dnd->dropper_window; + dnd_debug2 (" child window previously found: %ld", dnd->dropper_window); + } +#endif + action = dnd->XdndActionCopy; + dnd->supported_action = dnd->XdndActionCopy; + dnd->x = XDND_POSITION_ROOT_X (xevent); + dnd->y = XDND_POSITION_ROOT_Y (xevent); + dnd->time = CurrentTime; + if (dnd_version_at_least (dnd->dragging_version, 1)) + dnd->time = XDND_POSITION_TIME (xevent); + if (dnd_version_at_least (dnd->dragging_version, 1)) + action = XDND_POSITION_ACTION (xevent); +#if XDND_VERSION >= 3 + if (last_window && last_window != xevent->xany.window) + if (dnd->widget_apply_leave) + (*dnd->widget_apply_leave) (dnd, last_window); +#endif + dnd->will_accept = (*dnd->widget_apply_position) (dnd, xevent->xany.window, dnd->dragger_window, + action, dnd->x, dnd->y, dnd->time, dnd->dragger_typelist, + &want_position, &dnd->supported_action, &dnd->desired_type, &rectangle); + dnd_debug2 (" will accept = %d", dnd->will_accept); +#if XDND_VERSION >= 3 + dnd_debug2 (" sending status of %ld", dnd->dropper_toplevel); + xdnd_send_status (dnd, dnd->dragger_window, dnd->dropper_toplevel, dnd->will_accept, + want_position, rectangle.x, rectangle.y, rectangle.width, rectangle.height, dnd->supported_action); +#else + dnd_debug2 (" sending status of %ld", xevent->xany.window); + xdnd_send_status (dnd, dnd->dragger_window, xevent->xany.window, dnd->will_accept, + want_position, rectangle.x, rectangle.y, rectangle.width, rectangle.height, dnd->supported_action); +#endif + } else { + dnd_debug1 (" wrong stage or from wrong window"); + } +} + +void handle_enter(DndClass * dnd, XEvent * xevent){ + dnd_debug2 (" message_type is XdndEnter, version = %ld", XDND_ENTER_VERSION (xevent)); +#if XDND_VERSION >= 3 + if (XDND_ENTER_VERSION (xevent) < 3) + return; +#endif + xdnd_reset (dnd); + dnd->dragger_window = XDND_ENTER_SOURCE_WIN (xevent); +#if XDND_VERSION >= 3 + dnd->dropper_toplevel = xevent->xany.window; + dnd->dropper_window = 0; /* enter goes to the top level window only, + so we don't really know what the + sub window is yet */ +#else + dnd->dropper_window = xevent->xany.window; +#endif + xdnd_xfree (dnd->dragger_typelist); + if (XDND_ENTER_THREE_TYPES (xevent)) { + dnd_debug1 (" three types only"); + xdnd_get_three_types (dnd, xevent, &dnd->dragger_typelist); + } else { + dnd_debug1 (" more than three types - getting list"); + xdnd_get_type_list (dnd, dnd->dragger_window, &dnd->dragger_typelist); + } + dnd->dragging_version = XDND_ENTER_VERSION (xevent); + if (!dnd->dragger_typelist){ + return; + } + for (;xevent->xclient.message_type != dnd->XdndPosition;) + { + XNextEvent(dnd->display, xevent); + } + handle_update(dnd, xevent); + /* Send request for obtain selection data. */ + xdnd_convert_selection (dnd, dnd->dragger_window, dnd->dropper_window, dnd->desired_type); + /* Wait to receive selection. */ + for (;xevent->type != SelectionNotify;) + { + XNextEvent(dnd->display, xevent); + } + if (xevent->xselection.property == dnd->Xdnd_NON_PROTOCOL_ATOM ) { + xdnd_get_selection (dnd, dnd->dragger_window, xevent->xselection.property, xevent->xany.window); + } +} + +void handle_exit(DndClass * dnd, XEvent * xevent){ +#if XDND_VERSION >= 3 + if (xevent->xany.window == dnd->dropper_toplevel && dnd->dropper_window) + xevent->xany.window = dnd->dropper_window; +#endif + dnd_debug1 (" message_type is XdndLeave"); + if (dnd->dragger_window == XDND_LEAVE_SOURCE_WIN (xevent) ) { + dnd_debug1 (" leaving"); + if (dnd->widget_apply_leave) + (*dnd->widget_apply_leave) (dnd, xevent->xany.window); + xdnd_xfree (dnd->dragger_typelist); + dnd->dropper_toplevel = dnd->dropper_window = 0; + } else { + dnd_debug1 (" wrong stage or from wrong window"); + } +} + +void handle_drop(DndClass * dnd, XEvent * xevent){ +#if XDND_VERSION >= 3 + if (xevent->xany.window == dnd->dropper_toplevel && dnd->dropper_window) + xevent->xany.window = dnd->dropper_window; +#endif + dnd_debug1 (" message_type is XdndDrop"); + if (dnd->dragger_window == XDND_DROP_SOURCE_WIN (xevent)) { + dnd->time = CurrentTime; + if (dnd_version_at_least (dnd->dragging_version, 1)) + dnd->time = XDND_DROP_TIME (xevent); + if (dnd->will_accept) { + dnd_debug1 (" will_accept is true - converting selectiong"); + dnd_debug2 (" my window is %ld", dnd->dropper_window); + dnd_debug2 (" source window is %ld", dnd->dragger_window); + xdnd_convert_selection (dnd, dnd->dragger_window, dnd->dropper_window, dnd->desired_type); + } else { + dnd_debug1 (" will_accept is false - sending finished"); + if (dnd_version_at_least (dnd->dragging_version, 2)) { +#if XDND_VERSION >= 3 + xdnd_send_finished (dnd, dnd->dragger_window, dnd->dropper_toplevel, 1); +#else + xdnd_send_finished (dnd, dnd->dragger_window, xevent->xany.window, 1); +#endif + } + xdnd_xfree (dnd->dragger_typelist); + xdnd_reset (dnd); + } + } else { + dnd_debug1 (" wrong stage or from wrong window"); + } + /* Wait to receive selection. */ + for (;xevent->type != SelectionNotify;) + { + XNextEvent(dnd->display, xevent); + } + if (xevent->xselection.property == dnd->Xdnd_NON_PROTOCOL_ATOM ) { + int error; + dnd_debug1 (" property is Xdnd_NON_PROTOCOL_ATOM - getting selection"); + error = xdnd_get_selection (dnd, dnd->dragger_window, xevent->xselection.property, xevent->xany.window); +/* error is not actually used, i think future versions of the protocol maybe should return +an error status to the calling window with the XdndFinished client message */ + if (dnd_version_at_least (dnd->dragging_version, 2)) { +#if XDND_VERSION >= 3 + xdnd_send_finished (dnd, dnd->dragger_window, dnd->dropper_toplevel, error); +#else + xdnd_send_finished (dnd, dnd->dragger_window, dnd->dropper_window, error); +#endif + dnd_debug1 (" sending finished"); + } + xdnd_xfree (dnd->dragger_typelist); + xdnd_reset (dnd); + } +} + +GHOST_TEventType xdnd_handle_event (Display * display, XEvent * xevent, Atom * typelist, Atom * actionlist, unsigned char **data, int *length, Atom * type, int *x, int *y) { - Atom action = 0; + GHOST_TEventType event_type = 0; static int initialised = 0; static DndClass dnd; - if (!initialised) { - xdnd_init (&dnd, display); + if (!initialised) + { + xdnd_init(&dnd, display); initialised = 1; } - if (xevent->type != ClientMessage || xevent->xclient.message_type != dnd.XdndEnter) { + if (xevent->type != ClientMessage) + { return 0; - } else { - struct xdnd_get_drop_info i; - -/* setup user structure */ - memset (&i, 0, sizeof (i)); + } + static struct xdnd_get_drop_info i; + if (xevent->xclient.message_type == dnd.XdndEnter) + { + /* setup user structure */ + memset(&i, 0, sizeof(i)); i.actionlist = actionlist; i.typelist = typelist; dnd.user_hook1 = &i; -/* setup methods */ + /* setup methods */ dnd.widget_insert_drop = widget_insert_drop; dnd.widget_apply_position = widget_apply_position; -/* main loop */ - for (;;) { - xdnd_handle_drop_events (&dnd, xevent); - if (dnd.stage == XDND_DROP_STAGE_IDLE) - break; - XNextEvent (dnd.display, xevent); - } - -/* return results */ - if (i.drop_data) { - *length = i.drop_data_length; - *data = i.drop_data; - action = i.return_action; - *type = i.return_type; - *x = i.x; - *y = i.y; - } + event_type = GHOST_kEventDraggingEntered; + handle_enter(&dnd, xevent); } - return action; + else if (xevent->xclient.message_type == dnd.XdndPosition) + { + event_type = GHOST_kEventDraggingUpdated; + handle_update(&dnd, xevent); + } + else if (xevent->xclient.message_type == dnd.XdndDrop) + { + event_type = GHOST_kEventDraggingDropDone; + handle_drop(&dnd, xevent); + } + else if (xevent->xclient.message_type == dnd.XdndLeave) + { + event_type = GHOST_kEventDraggingExited; + handle_exit(&dnd, xevent); + } + /* return results */ + *length = i.drop_data_length; + i.drop_data_length=0; + *data = i.drop_data; + /* data wil be free outside */ + i.drop_data=NULL; + *type = i.return_type; + *x = i.x; + *y = i.y; + return event_type; } - - diff --git a/extern/xdnd/xdnd.h b/extern/xdnd/xdnd.h index c903b51c8d0..506ba2b9ee2 100644 --- a/extern/xdnd/xdnd.h +++ b/extern/xdnd/xdnd.h @@ -16,7 +16,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ - +#include "../../intern/ghost/GHOST_Types.h" #ifndef _X_DND_H #define _X_DND_H @@ -208,7 +208,7 @@ Atom xdnd_drag (DndClass * dnd, Window from, Atom action, Atom * typelist); libraries main event loop and be called if the event type is ClientMessage or SelectionNotify */ int xdnd_handle_drop_events (DndClass * dnd, XEvent * xevent); -Atom xdnd_get_drop (Display * display, XEvent * xevent, Atom * typelist, Atom * actionlist, +GHOST_TEventType xdnd_handle_event (Display * display, XEvent * xevent, Atom * typelist, Atom * actionlist, unsigned char **data, int *length, Atom * type, int *x, int *y); diff --git a/intern/ghost/intern/GHOST_DropTargetWin32.cc b/intern/ghost/intern/GHOST_DropTargetWin32.cc index b21bd5fabad..9f4d42390e8 100644 --- a/intern/ghost/intern/GHOST_DropTargetWin32.cc +++ b/intern/ghost/intern/GHOST_DropTargetWin32.cc @@ -81,12 +81,13 @@ HRESULT __stdcall GHOST_DropTargetWin32::DragEnter(IDataObject *p_data_object, DWORD *pdw_effect) { /* We accept all drop by default. */ + void *data = getGhostData(p_data_object); m_window->setAcceptDragOperation(true); *pdw_effect = DROPEFFECT_NONE; m_draggedObjectType = getGhostType(p_data_object); m_system->pushDragDropEvent( - GHOST_kEventDraggingEntered, m_draggedObjectType, m_window, pt.x, pt.y, NULL); + GHOST_kEventDraggingEntered, m_draggedObjectType, m_window, pt.x, pt.y, data); return S_OK; } diff --git a/intern/ghost/intern/GHOST_DropTargetX11.cc b/intern/ghost/intern/GHOST_DropTargetX11.cc index f3b42a1da93..31fa5f9a970 100644 --- a/intern/ghost/intern/GHOST_DropTargetX11.cc +++ b/intern/ghost/intern/GHOST_DropTargetX11.cc @@ -202,24 +202,25 @@ bool GHOST_DropTargetX11::GHOST_HandleClientMessage(XEvent *event) uchar *dropBuffer; int dropBufferSize, dropX, dropY; - if (xdnd_get_drop(m_system->getXDisplay(), - event, - m_dndTypes, - m_dndActions, - &dropBuffer, - &dropBufferSize, - &dropType, - &dropX, - &dropY)) { - void *data = getGhostData(dropType, dropBuffer, dropBufferSize); + GHOST_TEventType event_type = xdnd_handle_event(m_system->getXDisplay(), + event, + m_dndTypes, + m_dndActions, + &dropBuffer, + &dropBufferSize, + &dropType, + &dropX, + &dropY); + if (event_type > 0) { - if (data) { - m_system->pushDragDropEvent( - GHOST_kEventDraggingDropDone, m_draggedObjectType, m_window, dropX, dropY, data); + void *data = nullptr; + if (dropBufferSize > 0) { + data = getGhostData(dropType, dropBuffer, dropBufferSize); + } + m_system->pushDragDropEvent(event_type, m_draggedObjectType, m_window, dropX, dropY, data); + if (dropBufferSize > 0) { + free(dropBuffer); } - - free(dropBuffer); - m_draggedObjectType = GHOST_kDragnDropTypeUnknown; return true; diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 000f333bf8b..a9fd65d6d38 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -1192,6 +1192,39 @@ void wm_window_reset_drawable(void) } } +void update_mouse_position_from_draganddrop(wmWindowManager *wm, + wmWindow *win, + wmEvent *event, + GHOST_TEventDragnDropData *ddd) +{ + /* Ensure the event state matches modifiers (window was inactive). */ + wm_window_update_eventstate_modifiers(wm, win); + /* Entering window, update mouse position (without sending an event). */ + wm_window_update_eventstate(win); + + wm_event_init_from_window(win, event); /* copy last state, like mouse coords */ + + /* activate region */ + event->type = MOUSEMOVE; + event->val = KM_NOTHING; + copy_v2_v2_int(event->prev_xy, event->xy); + + wm_cursor_position_from_ghost_screen_coords(win, &ddd->x, &ddd->y); + event->xy[0] = ddd->x; + event->xy[1] = ddd->y; + + /* The values from #wm_window_update_eventstate may not match (under WAYLAND they don't) + * Write this into the event state. */ + copy_v2_v2_int(win->eventstate->xy, event->xy); + + event->flag = 0; + + /* No context change! `C->wm->windrawable` is drawable, or for area queues. */ + wm->winactive = win; + win->active = 1; + wm_event_add(win, event); +} + /** * Called by ghost, here we handle events for windows themselves or send to event system. * @@ -1439,51 +1472,15 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_pt } break; } - case GHOST_kEventDraggingDropDone: { + case GHOST_kEventDraggingEntered: { + WM_drag_free_list(&wm->drags); + wm_drags_exit(wm, win); GHOST_TEventDragnDropData *ddd = GHOST_GetEventData(evt); - /* Ensure the event state matches modifiers (window was inactive). */ - wm_window_update_eventstate_modifiers(wm, win); - /* Entering window, update mouse position (without sending an event). */ - wm_window_update_eventstate(win); - wmEvent event; - wm_event_init_from_window(win, &event); /* copy last state, like mouse coords */ - - /* activate region */ - event.type = MOUSEMOVE; - event.val = KM_NOTHING; - copy_v2_v2_int(event.prev_xy, event.xy); - - wm_cursor_position_from_ghost_screen_coords(win, &ddd->x, &ddd->y); - event.xy[0] = ddd->x; - event.xy[1] = ddd->y; - - /* The values from #wm_window_update_eventstate may not match (under WAYLAND they don't) - * Write this into the event state. */ - copy_v2_v2_int(win->eventstate->xy, event.xy); - - event.flag = 0; - - /* No context change! `C->wm->windrawable` is drawable, or for area queues. */ - wm->winactive = win; - win->active = 1; - - wm_event_add(win, &event); - - /* make blender drop event with custom data pointing to wm drags */ - event.type = EVT_DROP; - event.val = KM_RELEASE; - event.custom = EVT_DATA_DRAGDROP; - event.customdata = &wm->drags; - event.customdata_free = true; - - wm_event_add(win, &event); - - // printf("Drop detected\n"); + update_mouse_position_from_draganddrop(wm, win, &event, ddd); /* add drag data to wm for paths: */ - if (ddd->dataType == GHOST_kDragnDropTypeFilenames) { GHOST_TStringArray *stra = ddd->data; @@ -1496,9 +1493,51 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_pt break; /* only one drop element supported now */ } } - break; } + case GHOST_kEventDraggingUpdated: { + GHOST_TEventDragnDropData *ddd = GHOST_GetEventData(evt); + wmEvent event; + update_mouse_position_from_draganddrop(wm, win, &event, ddd); + break; + } + case GHOST_kEventDraggingDropDone: { + GHOST_TEventDragnDropData *ddd = GHOST_GetEventData(evt); + + wmEvent event; + update_mouse_position_from_draganddrop(wm, win, &event, ddd); + + /* make blender drop event with custom data pointing to wm drags */ + event.type = EVT_DROP; + event.val = KM_RELEASE; + event.custom = EVT_DATA_DRAGDROP; + event.customdata = &wm->drags; + event.customdata_free = true; + + wm_event_add(win, &event); + if (wm->drags.first != NULL && ddd->dataType == GHOST_kDragnDropTypeFilenames) { + GHOST_TStringArray *stra = ddd->data; + + if (stra->count > 0) { + /* try to get icon type from extension */ + int icon = ED_file_extension_icon((char *)stra->strings[0]); + wmDragPath *path_data = WM_drag_create_path_data((char **)stra->strings, stra->count); + WM_event_start_drag(C, icon, WM_DRAG_PATH, path_data, 0.0, WM_DRAG_NOP); + /* void poin should point to string, it makes a copy */ + break; /* only one drop element supported now */ + } + } + break; + } + case GHOST_kEventDraggingExited: { + WM_drag_free_list(&wm->drags); + wm_drags_exit(wm, win); + GHOST_TEventDragnDropData *ddd = GHOST_GetEventData(evt); + wmEvent event; + update_mouse_position_from_draganddrop(wm, win, &event, ddd); + break; + } + case GHOST_kEventNativeResolutionChange: { /* Only update if the actual pixel size changes. */ float prev_pixelsize = U.pixelsize; -- 2.30.2