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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors')
-rw-r--r--source/blender/editors/CMakeLists.txt1
-rw-r--r--source/blender/editors/animation/anim_ops.c2
-rw-r--r--source/blender/editors/armature/armature_edit.c2
-rw-r--r--source/blender/editors/armature/editarmature_undo.c2
-rw-r--r--source/blender/editors/asset/CMakeLists.txt39
-rw-r--r--source/blender/editors/asset/asset_edit.c69
-rw-r--r--source/blender/editors/asset/asset_ops.c238
-rw-r--r--source/blender/editors/curve/editcurve.c2
-rw-r--r--source/blender/editors/gpencil/drawgpencil.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_add_monkey.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_add_stroke.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_convert.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_data.c22
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_fill.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_interpolate.c69
-rw-r--r--source/blender/editors/gpencil/gpencil_merge.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_ops_versioning.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_sculpt_paint.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_select.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_vertex_ops.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_vertex_paint.c1
-rw-r--r--source/blender/editors/include/ED_armature.h1
-rw-r--r--source/blender/editors/include/ED_asset.h39
-rw-r--r--source/blender/editors/include/ED_fileselect.h9
-rw-r--r--source/blender/editors/include/ED_gpencil.h1
-rw-r--r--source/blender/editors/include/ED_image.h2
-rw-r--r--source/blender/editors/include/ED_info.h8
-rw-r--r--source/blender/editors/include/ED_object.h1
-rw-r--r--source/blender/editors/include/ED_screen.h1
-rw-r--r--source/blender/editors/include/ED_transform.h2
-rw-r--r--source/blender/editors/include/ED_transform_snap_object_context.h1
-rw-r--r--source/blender/editors/include/ED_util.h2
-rw-r--r--source/blender/editors/include/ED_util_imbuf.h1
-rw-r--r--source/blender/editors/include/ED_uvedit.h9
-rw-r--r--source/blender/editors/include/ED_view3d.h10
-rw-r--r--source/blender/editors/include/UI_interface.h9
-rw-r--r--source/blender/editors/interface/interface.c46
-rw-r--r--source/blender/editors/interface/interface_context_menu.c27
-rw-r--r--source/blender/editors/interface/interface_eyedropper_gpencil_color.c1
-rw-r--r--source/blender/editors/interface/interface_handlers.c14
-rw-r--r--source/blender/editors/interface/interface_icons.c48
-rw-r--r--source/blender/editors/interface/interface_layout.c7
-rw-r--r--source/blender/editors/interface/interface_style.c8
-rw-r--r--source/blender/editors/interface/interface_templates.c9
-rw-r--r--source/blender/editors/interface/view2d_ops.c72
-rw-r--r--source/blender/editors/mesh/editmesh_bevel.c2
-rw-r--r--source/blender/editors/mesh/editmesh_knife_project.c2
-rw-r--r--source/blender/editors/mesh/editmesh_select.c3
-rw-r--r--source/blender/editors/mesh/editmesh_select_similar.c1
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c8
-rw-r--r--source/blender/editors/mesh/editmesh_undo.c1
-rw-r--r--source/blender/editors/mesh/editmesh_utils.c6
-rw-r--r--source/blender/editors/metaball/editmball_undo.c2
-rw-r--r--source/blender/editors/object/CMakeLists.txt3
-rw-r--r--source/blender/editors/object/object_add.c9
-rw-r--r--source/blender/editors/object/object_bake.c1
-rw-r--r--source/blender/editors/object/object_gpencil_modifier.c4
-rw-r--r--source/blender/editors/object/object_intern.h1
-rw-r--r--source/blender/editors/object/object_ops.c1
-rw-r--r--source/blender/editors/object/object_relations.c25
-rw-r--r--source/blender/editors/object/object_shader_fx.c54
-rw-r--r--source/blender/editors/object/object_vgroup.c3
-rw-r--r--source/blender/editors/physics/particle_object.c3
-rw-r--r--source/blender/editors/physics/rigidbody_constraint.c1
-rw-r--r--source/blender/editors/physics/rigidbody_object.c1
-rw-r--r--source/blender/editors/render/render_internal.c18
-rw-r--r--source/blender/editors/render/render_preview.c301
-rw-r--r--source/blender/editors/screen/screen_context.c4
-rw-r--r--source/blender/editors/screen/screen_edit.c186
-rw-r--r--source/blender/editors/screen/screen_ops.c4
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt1
-rw-r--r--source/blender/editors/sculpt_paint/paint_image.c3
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_intern.h1
-rw-r--r--source/blender/editors/sculpt_paint/paint_mask.c10
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c197
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_cloth.c37
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_detail.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_face_set.c91
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_color.c4
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mask.c4
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mesh.c6
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h11
-rw-r--r--source/blender/editors/space_action/action_edit.c2
-rw-r--r--source/blender/editors/space_action/action_select.c4
-rw-r--r--source/blender/editors/space_api/spacetypes.c2
-rw-r--r--source/blender/editors/space_buttons/CMakeLists.txt1
-rw-r--r--source/blender/editors/space_buttons/buttons_context.c11
-rw-r--r--source/blender/editors/space_buttons/buttons_intern.h1
-rw-r--r--source/blender/editors/space_buttons/buttons_ops.c2
-rw-r--r--source/blender/editors/space_clip/clip_ops.c2
-rw-r--r--source/blender/editors/space_console/space_console.c4
-rw-r--r--source/blender/editors/space_file/file_draw.c186
-rw-r--r--source/blender/editors/space_file/file_intern.h4
-rw-r--r--source/blender/editors/space_file/file_ops.c50
-rw-r--r--source/blender/editors/space_file/file_utils.c17
-rw-r--r--source/blender/editors/space_file/filelist.c643
-rw-r--r--source/blender/editors/space_file/filelist.h19
-rw-r--r--source/blender/editors/space_file/filesel.c191
-rw-r--r--source/blender/editors/space_file/space_file.c247
-rw-r--r--source/blender/editors/space_graph/CMakeLists.txt2
-rw-r--r--source/blender/editors/space_graph/graph_edit.c4
-rw-r--r--source/blender/editors/space_graph/graph_select.c4
-rw-r--r--source/blender/editors/space_graph/graph_view.c2
-rw-r--r--source/blender/editors/space_info/info_report.c2
-rw-r--r--source/blender/editors/space_node/CMakeLists.txt2
-rw-r--r--source/blender/editors/space_node/drawnode.c62
-rw-r--r--source/blender/editors/space_node/node_draw.c61
-rw-r--r--source/blender/editors/space_node/node_edit.c344
-rw-r--r--source/blender/editors/space_node/node_view.c2
-rw-r--r--source/blender/editors/space_node/space_node.c8
-rw-r--r--source/blender/editors/space_outliner/CMakeLists.txt7
-rw-r--r--source/blender/editors/space_outliner/outliner_context.c73
-rw-r--r--source/blender/editors/space_outliner/outliner_dragdrop.c22
-rw-r--r--source/blender/editors/space_outliner/outliner_intern.h7
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.c29
-rw-r--r--source/blender/editors/space_outliner/outliner_tree.c6
-rw-r--r--source/blender/editors/space_outliner/space_outliner.c1
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display_libraries.cc2
-rw-r--r--source/blender/editors/space_outliner/tree/tree_display_view_layer.cc1
-rw-r--r--source/blender/editors/space_outliner/tree/tree_element_nla.hh1
-rw-r--r--source/blender/editors/space_script/script_edit.c2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_add.c29
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c287
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c292
-rw-r--r--source/blender/editors/space_sequencer/sequencer_intern.h3
-rw-r--r--source/blender/editors/space_sequencer/sequencer_ops.c2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_view.c4
-rw-r--r--source/blender/editors/space_sequencer/space_sequencer.c6
-rw-r--r--source/blender/editors/space_text/space_text.c2
-rw-r--r--source/blender/editors/space_text/text_ops.c2
-rw-r--r--source/blender/editors/space_userpref/userpref_ops.c63
-rw-r--r--source/blender/editors/space_view3d/CMakeLists.txt2
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c26
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c5
-rw-r--r--source/blender/editors/space_view3d/view3d_utils.c35
-rw-r--r--source/blender/editors/space_view3d/view3d_view.c28
-rw-r--r--source/blender/editors/transform/CMakeLists.txt2
-rw-r--r--source/blender/editors/transform/transform.h2
-rw-r--r--source/blender/editors/transform/transform_convert.h3
-rw-r--r--source/blender/editors/transform/transform_mode_translate.c3
-rw-r--r--source/blender/editors/transform/transform_ops.c2
-rw-r--r--source/blender/editors/transform/transform_snap.h2
-rw-r--r--source/blender/editors/undo/memfile_undo.c1
-rw-r--r--source/blender/editors/util/CMakeLists.txt1
-rw-r--r--source/blender/editors/util/ed_util.c105
-rw-r--r--source/blender/editors/uvedit/CMakeLists.txt2
-rw-r--r--source/blender/editors/uvedit/uvedit_intern.h1
-rw-r--r--source/blender/editors/uvedit/uvedit_ops.c2
-rw-r--r--source/blender/editors/uvedit/uvedit_smart_stitch.c2
152 files changed, 3556 insertions, 1189 deletions
diff --git a/source/blender/editors/CMakeLists.txt b/source/blender/editors/CMakeLists.txt
index 1f5dc73f732..a2ae350ce4b 100644
--- a/source/blender/editors/CMakeLists.txt
+++ b/source/blender/editors/CMakeLists.txt
@@ -22,6 +22,7 @@ if(WITH_BLENDER)
add_subdirectory(animation)
add_subdirectory(armature)
+ add_subdirectory(asset)
add_subdirectory(curve)
add_subdirectory(geometry)
add_subdirectory(gizmo_library)
diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c
index 9e622aea6ab..0db3e984b60 100644
--- a/source/blender/editors/animation/anim_ops.c
+++ b/source/blender/editors/animation/anim_ops.c
@@ -503,7 +503,7 @@ static void ANIM_OT_previewrange_clear(wmOperatorType *ot)
/* identifiers */
ot->name = "Clear Preview Range";
ot->idname = "ANIM_OT_previewrange_clear";
- ot->description = "Clear Preview Range";
+ ot->description = "Clear preview range";
/* api callbacks */
ot->exec = previewrange_clear_exec;
diff --git a/source/blender/editors/armature/armature_edit.c b/source/blender/editors/armature/armature_edit.c
index b20d2738bda..f2cb00f67f0 100644
--- a/source/blender/editors/armature/armature_edit.c
+++ b/source/blender/editors/armature/armature_edit.c
@@ -1014,7 +1014,7 @@ void ARMATURE_OT_switch_direction(wmOperatorType *ot)
/* identifiers */
ot->name = "Switch Direction";
ot->idname = "ARMATURE_OT_switch_direction";
- ot->description = "Change the direction that a chain of bones points in (head <-> tail swap)";
+ ot->description = "Change the direction that a chain of bones points in (head and tail swap)";
/* api callbacks */
ot->exec = armature_switch_direction_exec;
diff --git a/source/blender/editors/armature/editarmature_undo.c b/source/blender/editors/armature/editarmature_undo.c
index c217b615db6..7c11c5e537e 100644
--- a/source/blender/editors/armature/editarmature_undo.c
+++ b/source/blender/editors/armature/editarmature_undo.c
@@ -26,7 +26,9 @@
#include "CLG_log.h"
#include "DNA_armature_types.h"
+#include "DNA_layer_types.h"
#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
#include "BLI_array_utils.h"
#include "BLI_listbase.h"
diff --git a/source/blender/editors/asset/CMakeLists.txt b/source/blender/editors/asset/CMakeLists.txt
new file mode 100644
index 00000000000..63a1761b264
--- /dev/null
+++ b/source/blender/editors/asset/CMakeLists.txt
@@ -0,0 +1,39 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+ ../include
+ ../../blenlib
+ ../../blenkernel
+ ../../makesdna
+ ../../makesrna
+ ../../windowmanager
+ ../../../../intern/guardedalloc
+)
+
+set(INC_SYS
+)
+
+set(SRC
+ asset_edit.c
+ asset_ops.c
+)
+
+set(LIB
+)
+
+blender_add_lib(bf_editor_asset "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
diff --git a/source/blender/editors/asset/asset_edit.c b/source/blender/editors/asset/asset_edit.c
new file mode 100644
index 00000000000..5333c08c66a
--- /dev/null
+++ b/source/blender/editors/asset/asset_edit.c
@@ -0,0 +1,69 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup edasset
+ */
+
+#include "BKE_asset.h"
+#include "BKE_context.h"
+#include "BKE_idtype.h"
+#include "BKE_lib_id.h"
+
+#include "DNA_ID.h"
+#include "DNA_asset_types.h"
+
+#include "UI_interface_icons.h"
+
+#include "RNA_access.h"
+
+#include "ED_asset.h"
+
+bool ED_asset_mark_id(const bContext *C, ID *id)
+{
+ if (id->asset_data) {
+ return false;
+ }
+ if (!BKE_id_can_be_asset(id)) {
+ return false;
+ }
+
+ id_fake_user_set(id);
+
+ id->asset_data = BKE_asset_metadata_create();
+
+ UI_icon_render_id(C, NULL, id, true, true);
+
+ return true;
+}
+
+bool ED_asset_clear_id(ID *id)
+{
+ if (!id->asset_data) {
+ return false;
+ }
+ BKE_asset_metadata_free(&id->asset_data);
+ /* Don't clear fake user here, there's no guarantee that it was actually set by
+ * #ED_asset_mark_id(), it might have been something/someone else. */
+
+ return true;
+}
+
+bool ED_asset_can_make_single_from_context(const bContext *C)
+{
+ /* Context needs a "id" pointer to be set for #ASSET_OT_mark()/#ASSET_OT_clear() to use. */
+ return CTX_data_pointer_get_type_silent(C, "id", &RNA_ID).data != NULL;
+}
diff --git a/source/blender/editors/asset/asset_ops.c b/source/blender/editors/asset/asset_ops.c
new file mode 100644
index 00000000000..929d49e19fa
--- /dev/null
+++ b/source/blender/editors/asset/asset_ops.c
@@ -0,0 +1,238 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup edasset
+ */
+
+#include <string.h>
+
+#include "BKE_asset.h"
+#include "BKE_context.h"
+#include "BKE_report.h"
+
+#include "BLI_listbase.h"
+#include "BLI_string_utils.h"
+
+#include "DNA_asset_types.h"
+
+#include "ED_asset.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+/* -------------------------------------------------------------------- */
+
+struct AssetMarkResultStats {
+ int tot_created;
+ int tot_already_asset;
+ ID *last_id;
+};
+
+/**
+ * Return the IDs to operate on as list of #CollectionPointerLink links. Needs freeing.
+ */
+static ListBase /* CollectionPointerLink */ asset_operation_get_ids_from_context(const bContext *C)
+{
+ ListBase list = {0};
+
+ PointerRNA idptr = CTX_data_pointer_get_type(C, "id", &RNA_ID);
+
+ if (idptr.data) {
+ CollectionPointerLink *ctx_link = MEM_callocN(sizeof(*ctx_link), __func__);
+ ctx_link->ptr = idptr;
+ BLI_addtail(&list, ctx_link);
+ }
+ else {
+ CTX_data_selected_ids(C, &list);
+ }
+
+ return list;
+}
+
+static void asset_mark_for_idptr_list(const bContext *C,
+ const ListBase /* CollectionPointerLink */ *ids,
+ struct AssetMarkResultStats *r_stats)
+{
+ memset(r_stats, 0, sizeof(*r_stats));
+
+ LISTBASE_FOREACH (CollectionPointerLink *, ctx_id, ids) {
+ BLI_assert(RNA_struct_is_ID(ctx_id->ptr.type));
+
+ ID *id = ctx_id->ptr.data;
+ if (id->asset_data) {
+ r_stats->tot_already_asset++;
+ continue;
+ }
+
+ if (ED_asset_mark_id(C, id)) {
+ r_stats->last_id = id;
+ r_stats->tot_created++;
+ }
+ }
+}
+
+static bool asset_mark_results_report(const struct AssetMarkResultStats *stats,
+ ReportList *reports)
+{
+ /* User feedback on failure. */
+ if ((stats->tot_created < 1) && (stats->tot_already_asset > 0)) {
+ BKE_report(reports,
+ RPT_ERROR,
+ "Selected data-blocks are already assets (or do not support use as assets)");
+ return false;
+ }
+ if (stats->tot_created < 1) {
+ BKE_report(reports,
+ RPT_ERROR,
+ "No data-blocks to create assets for found (or do not support use as assets)");
+ return false;
+ }
+
+ /* User feedback on success. */
+ if (stats->tot_created == 1) {
+ /* If only one data-block: Give more useful message by printing asset name. */
+ BKE_reportf(reports, RPT_INFO, "Data-block '%s' is now an asset", stats->last_id->name + 2);
+ }
+ else {
+ BKE_reportf(reports, RPT_INFO, "%i data-blocks are now assets", stats->tot_created);
+ }
+
+ return true;
+}
+
+static int asset_mark_exec(bContext *C, wmOperator *op)
+{
+ ListBase ids = asset_operation_get_ids_from_context(C);
+
+ struct AssetMarkResultStats stats;
+ asset_mark_for_idptr_list(C, &ids, &stats);
+ BLI_freelistN(&ids);
+
+ if (!asset_mark_results_report(&stats, op->reports)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ WM_main_add_notifier(NC_ID | NA_EDITED, NULL);
+ WM_main_add_notifier(NC_ASSET | NA_ADDED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+static void ASSET_OT_mark(wmOperatorType *ot)
+{
+ ot->name = "Mark Asset";
+ ot->description =
+ "Enable easier reuse of selected data-blocks through the Asset Browser, with the help of "
+ "customizable metadata (like previews, descriptions and tags)";
+ ot->idname = "ASSET_OT_mark";
+
+ ot->exec = asset_mark_exec;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* -------------------------------------------------------------------- */
+
+struct AssetClearResultStats {
+ int tot_removed;
+ ID *last_id;
+};
+
+static void asset_clear_from_idptr_list(const ListBase /* CollectionPointerLink */ *ids,
+ struct AssetClearResultStats *r_stats)
+{
+ memset(r_stats, 0, sizeof(*r_stats));
+
+ LISTBASE_FOREACH (CollectionPointerLink *, ctx_id, ids) {
+ BLI_assert(RNA_struct_is_ID(ctx_id->ptr.type));
+
+ ID *id = ctx_id->ptr.data;
+ if (!id->asset_data) {
+ continue;
+ }
+
+ if (ED_asset_clear_id(id)) {
+ r_stats->tot_removed++;
+ r_stats->last_id = id;
+ }
+ }
+}
+
+static bool asset_clear_result_report(const struct AssetClearResultStats *stats,
+ ReportList *reports)
+
+{
+ if (stats->tot_removed < 1) {
+ BKE_report(reports, RPT_ERROR, "No asset data-blocks selected/focused");
+ return false;
+ }
+
+ if (stats->tot_removed == 1) {
+ /* If only one data-block: Give more useful message by printing asset name. */
+ BKE_reportf(
+ reports, RPT_INFO, "Data-block '%s' is no asset anymore", stats->last_id->name + 2);
+ }
+ else {
+ BKE_reportf(reports, RPT_INFO, "%i data-blocks are no assets anymore", stats->tot_removed);
+ }
+
+ return true;
+}
+
+static int asset_clear_exec(bContext *C, wmOperator *op)
+{
+ ListBase ids = asset_operation_get_ids_from_context(C);
+
+ struct AssetClearResultStats stats;
+ asset_clear_from_idptr_list(&ids, &stats);
+ BLI_freelistN(&ids);
+
+ if (!asset_clear_result_report(&stats, op->reports)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ WM_main_add_notifier(NC_ID | NA_EDITED, NULL);
+ WM_main_add_notifier(NC_ASSET | NA_REMOVED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+static void ASSET_OT_clear(wmOperatorType *ot)
+{
+ ot->name = "Clear Asset";
+ ot->description =
+ "Delete all asset metadata and turn the selected asset data-blocks back into normal "
+ "data-blocks";
+ ot->idname = "ASSET_OT_clear";
+
+ ot->exec = asset_clear_exec;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* -------------------------------------------------------------------- */
+
+void ED_operatortypes_asset(void)
+{
+ WM_operatortype_append(ASSET_OT_mark);
+ WM_operatortype_append(ASSET_OT_clear);
+}
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c
index db472c9ffa7..2b627971286 100644
--- a/source/blender/editors/curve/editcurve.c
+++ b/source/blender/editors/curve/editcurve.c
@@ -3838,7 +3838,7 @@ void CURVE_OT_subdivide(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
- prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 1000, "Number of cuts", "", 1, 10);
+ prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 1000, "Number of Cuts", "", 1, 10);
/* Avoid re-using last var because it can cause _very_ high poly meshes
* and annoy users (or worse crash). */
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c
index 93767127cc7..4e2951c3571 100644
--- a/source/blender/editors/gpencil/drawgpencil.c
+++ b/source/blender/editors/gpencil/drawgpencil.c
@@ -41,6 +41,7 @@
#include "DNA_brush_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
diff --git a/source/blender/editors/gpencil/gpencil_add_monkey.c b/source/blender/editors/gpencil/gpencil_add_monkey.c
index 315b3c281da..65141442237 100644
--- a/source/blender/editors/gpencil/gpencil_add_monkey.c
+++ b/source/blender/editors/gpencil/gpencil_add_monkey.c
@@ -25,6 +25,7 @@
#include "BLI_utildefines.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
diff --git a/source/blender/editors/gpencil/gpencil_add_stroke.c b/source/blender/editors/gpencil/gpencil_add_stroke.c
index f26fd936d40..0c8cc621a3b 100644
--- a/source/blender/editors/gpencil/gpencil_add_stroke.c
+++ b/source/blender/editors/gpencil/gpencil_add_stroke.c
@@ -25,6 +25,7 @@
#include "BLI_utildefines.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c
index 63aa242275a..09b57029350 100644
--- a/source/blender/editors/gpencil/gpencil_convert.c
+++ b/source/blender/editors/gpencil/gpencil_convert.c
@@ -41,6 +41,7 @@
#include "DNA_collection_types.h"
#include "DNA_curve_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c
index 33a1469beab..aff109eb98e 100644
--- a/source/blender/editors/gpencil/gpencil_data.c
+++ b/source/blender/editors/gpencil/gpencil_data.c
@@ -42,6 +42,7 @@
#include "DNA_anim_types.h"
#include "DNA_brush_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
@@ -1330,19 +1331,24 @@ static int gpencil_merge_layer_exec(bContext *C, wmOperator *op)
BLI_ghash_insert(gh_frames_dst, POINTER_FROM_INT(gpf_dst->framenum), gpf_dst);
}
- /* Read all frames from merge layer and add any missing in destination layer. */
+ /* Read all frames from merge layer and add any missing in destination layer,
+ * copying all previous strokes to keep the image equals.
+ * Need to do it in a separated loop to avoid strokes accumulation. */
LISTBASE_FOREACH (bGPDframe *, gpf_src, &gpl_src->frames) {
/* Try to find frame in destination layer hash table. */
bGPDframe *gpf_dst = BLI_ghash_lookup(gh_frames_dst, POINTER_FROM_INT(gpf_src->framenum));
if (!gpf_dst) {
- gpf_dst = BKE_gpencil_frame_addnew(gpl_dst, gpf_src->framenum);
- /* Duplicate strokes into destination frame. */
- if (gpf_dst) {
- BKE_gpencil_frame_copy_strokes(gpf_src, gpf_dst);
- }
+ gpf_dst = BKE_gpencil_layer_frame_get(gpl_dst, gpf_src->framenum, GP_GETFRAME_ADD_COPY);
+ BLI_ghash_insert(gh_frames_dst, POINTER_FROM_INT(gpf_src->framenum), gpf_dst);
}
- else {
- /* Add to tail all strokes. */
+ }
+
+ /* Read all frames from merge layer and add strokes. */
+ LISTBASE_FOREACH (bGPDframe *, gpf_src, &gpl_src->frames) {
+ /* Try to find frame in destination layer hash table. */
+ bGPDframe *gpf_dst = BLI_ghash_lookup(gh_frames_dst, POINTER_FROM_INT(gpf_src->framenum));
+ /* Add to tail all strokes. */
+ if (gpf_dst) {
BLI_movelisttolist(&gpf_dst->strokes, &gpf_src->strokes);
}
}
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index 95c94f8cfed..36e383cf3c2 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -41,6 +41,7 @@
#include "DNA_gpencil_modifier_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c
index 93941ea3766..39968aac9fa 100644
--- a/source/blender/editors/gpencil/gpencil_fill.c
+++ b/source/blender/editors/gpencil/gpencil_fill.c
@@ -35,6 +35,7 @@
#include "DNA_brush_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_image_types.h"
+#include "DNA_material_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_windowmanager_types.h"
diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c
index 3617f20763e..9bca294cf30 100644
--- a/source/blender/editors/gpencil/gpencil_interpolate.c
+++ b/source/blender/editors/gpencil/gpencil_interpolate.c
@@ -283,8 +283,8 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
tgpil = MEM_callocN(sizeof(tGPDinterpolate_layer), "GPencil Interpolate Layer");
tgpil->gpl = gpl;
- tgpil->prevFrame = gpl->actframe;
- tgpil->nextFrame = gpl->actframe->next;
+ tgpil->prevFrame = BKE_gpencil_frame_duplicate(gpl->actframe);
+ tgpil->nextFrame = BKE_gpencil_frame_duplicate(gpl->actframe->next);
BLI_addtail(&tgpi->ilayers, tgpil);
@@ -326,24 +326,25 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
valid = false;
}
- /* create new stroke */
- new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
-
if (valid) {
/* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */
if (gps_from->totpoints > gps_to->totpoints) {
- new_stroke->points = MEM_recallocN(new_stroke->points,
- sizeof(*new_stroke->points) * gps_to->totpoints);
- if (new_stroke->dvert != NULL) {
- new_stroke->dvert = MEM_recallocN(new_stroke->dvert,
- sizeof(*new_stroke->dvert) * gps_to->totpoints);
- }
- new_stroke->totpoints = gps_to->totpoints;
+ BKE_gpencil_stroke_uniform_subdivide(gpd, gps_to, gps_from->totpoints, true);
}
- /* update points position */
+ if (gps_to->totpoints > gps_from->totpoints) {
+ BKE_gpencil_stroke_uniform_subdivide(gpd, gps_from, gps_to->totpoints, true);
+ }
+
+ /* Create new stroke. */
+ new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
+
+ /* Update points position. */
gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, tgpil->factor);
}
else {
+ /* Create new stroke. */
+ new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
+
/* need an empty stroke to keep index correct for lookup, but resize to smallest size */
new_stroke->totpoints = 0;
new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points));
@@ -443,12 +444,16 @@ static void gpencil_interpolate_exit(bContext *C, wmOperator *op)
/* finally, free memory used by temp data */
LISTBASE_FOREACH (tGPDinterpolate_layer *, tgpil, &tgpi->ilayers) {
+ BKE_gpencil_free_strokes(tgpil->prevFrame);
+ BKE_gpencil_free_strokes(tgpil->nextFrame);
BKE_gpencil_free_strokes(tgpil->interFrame);
- MEM_freeN(tgpil->interFrame);
+ MEM_SAFE_FREE(tgpil->prevFrame);
+ MEM_SAFE_FREE(tgpil->nextFrame);
+ MEM_SAFE_FREE(tgpil->interFrame);
}
BLI_freelistN(&tgpi->ilayers);
- MEM_freeN(tgpi);
+ MEM_SAFE_FREE(tgpi);
}
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
@@ -992,8 +997,8 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
}
/* store extremes */
- prevFrame = gpl->actframe;
- nextFrame = gpl->actframe->next;
+ prevFrame = BKE_gpencil_frame_duplicate(gpl->actframe);
+ nextFrame = BKE_gpencil_frame_duplicate(gpl->actframe->next);
/* Loop over intermediary frames and create the interpolation */
for (cframe = prevFrame->framenum + step; cframe < nextFrame->framenum; cframe += step) {
@@ -1049,28 +1054,17 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
interFrame->key_type = BEZT_KEYTYPE_BREAKDOWN;
}
- /* create new stroke */
- bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
-
/* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */
if (gps_from->totpoints > gps_to->totpoints) {
- /* free weights of removed points */
- if (new_stroke->dvert != NULL) {
- BKE_defvert_array_free_elems(new_stroke->dvert + gps_to->totpoints,
- gps_from->totpoints - gps_to->totpoints);
- }
-
- new_stroke->points = MEM_recallocN(new_stroke->points,
- sizeof(*new_stroke->points) * gps_to->totpoints);
-
- if (new_stroke->dvert != NULL) {
- new_stroke->dvert = MEM_recallocN(new_stroke->dvert,
- sizeof(*new_stroke->dvert) * gps_to->totpoints);
- }
-
- new_stroke->totpoints = gps_to->totpoints;
+ BKE_gpencil_stroke_uniform_subdivide(gpd, gps_to, gps_from->totpoints, true);
+ }
+ if (gps_to->totpoints > gps_from->totpoints) {
+ BKE_gpencil_stroke_uniform_subdivide(gpd, gps_from, gps_to->totpoints, true);
}
+ /* create new stroke */
+ bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
+
/* update points position */
gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, factor);
@@ -1081,6 +1075,11 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
BLI_addtail(&interFrame->strokes, new_stroke);
}
}
+
+ BKE_gpencil_free_strokes(prevFrame);
+ BKE_gpencil_free_strokes(nextFrame);
+ MEM_SAFE_FREE(prevFrame);
+ MEM_SAFE_FREE(nextFrame);
}
/* notifiers */
diff --git a/source/blender/editors/gpencil/gpencil_merge.c b/source/blender/editors/gpencil/gpencil_merge.c
index 9f2bf3818a4..272dff56291 100644
--- a/source/blender/editors/gpencil/gpencil_merge.c
+++ b/source/blender/editors/gpencil/gpencil_merge.c
@@ -31,6 +31,7 @@
#include "BLI_math.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "BKE_brush.h"
#include "BKE_context.h"
diff --git a/source/blender/editors/gpencil/gpencil_ops_versioning.c b/source/blender/editors/gpencil/gpencil_ops_versioning.c
index 4721736489e..815bbbaa254 100644
--- a/source/blender/editors/gpencil/gpencil_ops_versioning.c
+++ b/source/blender/editors/gpencil/gpencil_ops_versioning.c
@@ -33,6 +33,7 @@
#include "BLI_math.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
index ed18c2eed5d..bb9dd8cac5d 100644
--- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c
+++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
@@ -41,6 +41,7 @@
#include "BLT_translation.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c
index 2c0b9534141..281ab8c5adc 100644
--- a/source/blender/editors/gpencil/gpencil_select.c
+++ b/source/blender/editors/gpencil/gpencil_select.c
@@ -36,6 +36,7 @@
#include "BLI_utildefines.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c
index e8e25a55796..c3ac33063af 100644
--- a/source/blender/editors/gpencil/gpencil_utils.c
+++ b/source/blender/editors/gpencil/gpencil_utils.c
@@ -40,7 +40,9 @@
#include "PIL_time.h"
#include "DNA_brush_types.h"
+#include "DNA_collection_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
diff --git a/source/blender/editors/gpencil/gpencil_vertex_ops.c b/source/blender/editors/gpencil/gpencil_vertex_ops.c
index c3fd8d10b64..90b2c1c3895 100644
--- a/source/blender/editors/gpencil/gpencil_vertex_ops.c
+++ b/source/blender/editors/gpencil/gpencil_vertex_ops.c
@@ -32,6 +32,7 @@
#include "DNA_brush_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "BKE_colortools.h"
#include "BKE_context.h"
diff --git a/source/blender/editors/gpencil/gpencil_vertex_paint.c b/source/blender/editors/gpencil/gpencil_vertex_paint.c
index a4dc677f0dc..3afff897734 100644
--- a/source/blender/editors/gpencil/gpencil_vertex_paint.c
+++ b/source/blender/editors/gpencil/gpencil_vertex_paint.c
@@ -31,6 +31,7 @@
#include "DNA_brush_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "BKE_brush.h"
#include "BKE_colortools.h"
diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h
index 3501acd4fdf..0c4576096fb 100644
--- a/source/blender/editors/include/ED_armature.h
+++ b/source/blender/editors/include/ED_armature.h
@@ -31,7 +31,6 @@ struct Base;
struct Bone;
struct Depsgraph;
struct EditBone;
-struct IDProperty;
struct ListBase;
struct Main;
struct Mesh;
diff --git a/source/blender/editors/include/ED_asset.h b/source/blender/editors/include/ED_asset.h
new file mode 100644
index 00000000000..6fe50528cc5
--- /dev/null
+++ b/source/blender/editors/include/ED_asset.h
@@ -0,0 +1,39 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup editors
+ */
+
+#ifndef __ED_ASSET_H__
+#define __ED_ASSET_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+bool ED_asset_mark_id(const struct bContext *C, struct ID *id);
+bool ED_asset_clear_id(struct ID *id);
+
+bool ED_asset_can_make_single_from_context(const struct bContext *C);
+
+void ED_operatortypes_asset(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ED_ASSET_H__ */
diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h
index a5a8df916d6..7b240e0569f 100644
--- a/source/blender/editors/include/ED_fileselect.h
+++ b/source/blender/editors/include/ED_fileselect.h
@@ -28,7 +28,9 @@ extern "C" {
#endif
struct ARegion;
+struct FileAssetSelectParams;
struct FileSelectParams;
+struct FileDirEntry;
struct Scene;
struct ScrArea;
struct SpaceFile;
@@ -103,14 +105,14 @@ struct rcti;
struct FileSelectParams *ED_fileselect_ensure_active_params(struct SpaceFile *sfile);
struct FileSelectParams *ED_fileselect_get_active_params(const struct SpaceFile *sfile);
+struct FileSelectParams *ED_fileselect_get_file_params(const struct SpaceFile *sfile);
+struct FileAssetSelectParams *ED_fileselect_get_asset_params(const struct SpaceFile *sfile);
void ED_fileselect_set_params_from_userdef(struct SpaceFile *sfile);
void ED_fileselect_params_to_userdef(struct SpaceFile *sfile,
const int temp_win_size[],
const bool is_maximized);
-void ED_fileselect_reset_params(struct SpaceFile *sfile);
-
void ED_fileselect_init_layout(struct SpaceFile *sfile, struct ARegion *region);
FileLayout *ED_fileselect_get_layout(struct SpaceFile *sfile, struct ARegion *region);
@@ -142,6 +144,8 @@ void ED_fileselect_exit(struct wmWindowManager *wm,
struct Scene *owner_scene,
struct SpaceFile *sfile);
+bool ED_fileselect_is_asset_browser(const struct SpaceFile *sfile);
+
void ED_fileselect_window_params_get(const struct wmWindow *win,
int win_size[2],
bool *is_maximized);
@@ -151,6 +155,7 @@ struct ScrArea *ED_fileselect_handler_area_find(const struct wmWindow *win,
int ED_path_extension_type(const char *path);
int ED_file_extension_icon(const char *path);
+int ED_file_icon(const struct FileDirEntry *file);
void ED_file_read_bookmarks(void);
diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h
index be2f714dfe1..1b7caf27ecf 100644
--- a/source/blender/editors/include/ED_gpencil.h
+++ b/source/blender/editors/include/ED_gpencil.h
@@ -51,7 +51,6 @@ struct ScrArea;
struct SnapObjectContext;
struct ToolSettings;
struct View3D;
-struct ViewLayer;
struct bContext;
struct Material;
diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h
index c1d3a17b9b6..b139b0765a3 100644
--- a/source/blender/editors/include/ED_image.h
+++ b/source/blender/editors/include/ED_image.h
@@ -34,12 +34,10 @@ struct ARegion;
struct ImBuf;
struct Image;
struct ImageUser;
-struct LinkNodePair;
struct Main;
struct ReportList;
struct Scene;
struct SpaceImage;
-struct ViewLayer;
struct bContext;
struct wmOperator;
struct wmWindowManager;
diff --git a/source/blender/editors/include/ED_info.h b/source/blender/editors/include/ED_info.h
index e3ce494e09a..9ac6b6c1085 100644
--- a/source/blender/editors/include/ED_info.h
+++ b/source/blender/editors/include/ED_info.h
@@ -38,8 +38,12 @@ const char *ED_info_statistics_string(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer);
-void ED_info_draw_stats(
- struct Main *bmain, Scene *scene, ViewLayer *view_layer, int x, int *y, int height);
+void ED_info_draw_stats(struct Main *bmain,
+ struct Scene *scene,
+ struct ViewLayer *view_layer,
+ int x,
+ int *y,
+ int height);
#ifdef __cplusplus
}
diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h
index 2e9b711c99a..f9358f62274 100644
--- a/source/blender/editors/include/ED_object.h
+++ b/source/blender/editors/include/ED_object.h
@@ -53,7 +53,6 @@ struct uiLayout;
struct wmKeyConfig;
struct wmOperator;
struct wmOperatorType;
-struct wmWindowManager;
/* object_edit.c */
/* context.object */
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h
index dc1c43c0337..20417634020 100644
--- a/source/blender/editors/include/ED_screen.h
+++ b/source/blender/editors/include/ED_screen.h
@@ -239,6 +239,7 @@ void ED_screen_restore_temp_type(struct bContext *C, ScrArea *area);
ScrArea *ED_screen_full_newspace(struct bContext *C, ScrArea *area, int type);
void ED_screen_full_prevspace(struct bContext *C, ScrArea *area);
void ED_screen_full_restore(struct bContext *C, ScrArea *area);
+ScrArea *ED_screen_state_maximized_create(struct bContext *C);
struct ScrArea *ED_screen_state_toggle(struct bContext *C,
struct wmWindow *win,
struct ScrArea *area,
diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h
index 0ea86e006e0..ca3e351a052 100644
--- a/source/blender/editors/include/ED_transform.h
+++ b/source/blender/editors/include/ED_transform.h
@@ -32,7 +32,6 @@ extern "C" {
struct Object;
struct bContext;
struct wmKeyConfig;
-struct wmMsgBus;
struct wmOperatorType;
void ED_keymap_transform(struct wmKeyConfig *keyconf);
@@ -108,7 +107,6 @@ bool calculateTransformCenter(struct bContext *C,
struct Object;
struct Scene;
-struct wmGizmoGroup;
struct wmGizmoGroupType;
/* UNUSED */
diff --git a/source/blender/editors/include/ED_transform_snap_object_context.h b/source/blender/editors/include/ED_transform_snap_object_context.h
index ebaa32941f2..b7174964ef6 100644
--- a/source/blender/editors/include/ED_transform_snap_object_context.h
+++ b/source/blender/editors/include/ED_transform_snap_object_context.h
@@ -31,7 +31,6 @@ struct BMVert;
struct ARegion;
struct Depsgraph;
struct ListBase;
-struct Main;
struct Object;
struct Scene;
struct View3D;
diff --git a/source/blender/editors/include/ED_util.h b/source/blender/editors/include/ED_util.h
index 68ae3589064..ca6b4bdc618 100644
--- a/source/blender/editors/include/ED_util.h
+++ b/source/blender/editors/include/ED_util.h
@@ -53,6 +53,8 @@ void ED_spacedata_id_remap(struct ScrArea *area,
struct ID *new_id);
void ED_OT_flush_edits(struct wmOperatorType *ot);
+void ED_OT_lib_id_load_custom_preview(struct wmOperatorType *ot);
+void ED_OT_lib_id_generate_preview(struct wmOperatorType *ot);
/* ************** XXX OLD CRUFT WARNING ************* */
diff --git a/source/blender/editors/include/ED_util_imbuf.h b/source/blender/editors/include/ED_util_imbuf.h
index d142d3d6425..4bbaa68e849 100644
--- a/source/blender/editors/include/ED_util_imbuf.h
+++ b/source/blender/editors/include/ED_util_imbuf.h
@@ -31,7 +31,6 @@ extern "C" {
#endif
struct ARegion;
-struct Main;
struct bContext;
struct wmEvent;
struct wmOperator;
diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h
index 2066d7da511..4de97411059 100644
--- a/source/blender/editors/include/ED_uvedit.h
+++ b/source/blender/editors/include/ED_uvedit.h
@@ -33,7 +33,6 @@ struct BMEditMesh;
struct BMFace;
struct BMLoop;
struct BMesh;
-struct Depsgraph;
struct Image;
struct ImageUser;
struct Main;
@@ -217,8 +216,10 @@ struct BMLoop *ED_uvedit_active_vert_loop_get(struct BMesh *bm);
void ED_uvedit_active_edge_loop_set(struct BMesh *bm, struct BMLoop *l);
struct BMLoop *ED_uvedit_active_edge_loop_get(struct BMesh *bm);
-char ED_uvedit_select_mode_get(const Scene *scene);
-void ED_uvedit_select_sync_flush(const ToolSettings *ts, struct BMEditMesh *em, const bool select);
+char ED_uvedit_select_mode_get(const struct Scene *scene);
+void ED_uvedit_select_sync_flush(const struct ToolSettings *ts,
+ struct BMEditMesh *em,
+ const bool select);
/* uvedit_unwrap_ops.c */
void ED_uvedit_live_unwrap_begin(struct Scene *scene, struct Object *obedit);
@@ -244,7 +245,7 @@ struct UVPackIsland_Params {
uint use_seams : 1;
uint correct_aspect : 1;
};
-void ED_uvedit_pack_islands_multi(const Scene *scene,
+void ED_uvedit_pack_islands_multi(const struct Scene *scene,
Object **objects,
const uint objects_len,
const struct UVPackIsland_Params *params);
diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h
index 596533406c3..a4856845a65 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -41,8 +41,6 @@ struct Camera;
struct CustomData_MeshMasks;
struct Depsgraph;
struct EditBone;
-struct GPUOffScreen;
-struct GPUViewport;
struct ID;
struct MVert;
struct Main;
@@ -55,7 +53,6 @@ struct RenderEngineType;
struct Scene;
struct ScrArea;
struct View3D;
-struct View3DShading;
struct ViewContext;
struct ViewLayer;
struct bContext;
@@ -64,8 +61,6 @@ struct bScreen;
struct rctf;
struct rcti;
struct wmGizmo;
-struct wmOperator;
-struct wmOperatorType;
struct wmWindow;
struct wmWindowManager;
@@ -141,6 +136,11 @@ void ED_view3d_to_object(const struct Depsgraph *depsgraph,
const float quat[4],
const float dist);
+bool ED_view3d_camera_to_view_selected(struct Main *bmain,
+ struct Depsgraph *depsgraph,
+ const struct Scene *scene,
+ struct Object *camera_ob);
+
void ED_view3d_lastview_store(struct RegionView3D *rv3d);
/* Depth buffer */
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 005dbf0e381..7c128cbf1e6 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -50,7 +50,6 @@ struct PointerRNA;
struct PropertyRNA;
struct ReportList;
struct ResultBLF;
-struct ScrArea;
struct bContext;
struct bContextStore;
struct bNode;
@@ -727,6 +726,13 @@ void UI_block_translate(uiBlock *block, int x, int y);
int UI_but_return_value_get(uiBut *but);
void UI_but_drag_set_id(uiBut *but, struct ID *id);
+void UI_but_drag_set_asset(uiBut *but,
+ const char *name,
+ const char *path,
+ int id_type,
+ int icon,
+ struct ImBuf *imb,
+ float scale);
void UI_but_drag_set_rna(uiBut *but, struct PointerRNA *ptr);
void UI_but_drag_set_path(uiBut *but, const char *path, const bool use_free);
void UI_but_drag_set_name(uiBut *but, const char *name);
@@ -1879,6 +1885,7 @@ uiBlock *uiLayoutGetBlock(uiLayout *layout);
void uiLayoutSetFunc(uiLayout *layout, uiMenuHandleFunc handlefunc, void *argv);
void uiLayoutSetContextPointer(uiLayout *layout, const char *name, struct PointerRNA *ptr);
+struct bContextStore *uiLayoutGetContextStore(uiLayout *layout);
void uiLayoutContextCopy(uiLayout *layout, struct bContextStore *context);
struct wmOperatorType *UI_but_operatortype_get_from_enum_menu(struct uiBut *but,
PropertyRNA **r_prop);
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index 4a02c6b6e88..c5c2f0e55c4 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -3352,7 +3352,7 @@ static void ui_but_free(const bContext *C, uiBut *but)
}
if (but->dragpoin && (but->dragflag & UI_BUT_DRAGPOIN_FREE)) {
- MEM_freeN(but->dragpoin);
+ WM_drag_data_free(but->dragtype, but->dragpoin);
}
ui_but_extra_operator_icons_free(but);
@@ -4552,6 +4552,15 @@ static uiBut *ui_def_but_rna(uiBlock *block,
UI_but_disable(but, info);
}
+ if (proptype == PROP_POINTER) {
+ /* If the button shows an ID, automatically set it as focused in context so operators can
+ * access it.*/
+ const PointerRNA pptr = RNA_property_pointer_get(ptr, prop);
+ if (pptr.data && RNA_struct_is_ID(pptr.type)) {
+ but->context = CTX_store_add(&block->contexts, "id", &pptr);
+ }
+ }
+
if (but->flag & UI_BUT_UNDO && (ui_but_is_rna_undo(but) == false)) {
but->flag &= ~UI_BUT_UNDO;
}
@@ -6089,17 +6098,42 @@ void UI_but_drag_set_id(uiBut *but, ID *id)
{
but->dragtype = WM_DRAG_ID;
if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) {
- MEM_SAFE_FREE(but->dragpoin);
+ WM_drag_data_free(but->dragtype, but->dragpoin);
but->dragflag &= ~UI_BUT_DRAGPOIN_FREE;
}
but->dragpoin = (void *)id;
}
+void UI_but_drag_set_asset(uiBut *but,
+ const char *name,
+ const char *path,
+ int id_type,
+ int icon,
+ struct ImBuf *imb,
+ float scale)
+{
+ wmDragAsset *asset_drag = MEM_mallocN(sizeof(*asset_drag), "wmDragAsset");
+
+ BLI_strncpy(asset_drag->name, name, sizeof(asset_drag->name));
+ asset_drag->path = path;
+ asset_drag->id_type = id_type;
+
+ but->dragtype = WM_DRAG_ASSET;
+ ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */
+ if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) {
+ WM_drag_data_free(but->dragtype, but->dragpoin);
+ }
+ but->dragpoin = asset_drag;
+ but->dragflag |= UI_BUT_DRAGPOIN_FREE;
+ but->imb = imb;
+ but->imb_scale = scale;
+}
+
void UI_but_drag_set_rna(uiBut *but, PointerRNA *ptr)
{
but->dragtype = WM_DRAG_RNA;
if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) {
- MEM_SAFE_FREE(but->dragpoin);
+ WM_drag_data_free(but->dragtype, but->dragpoin);
but->dragflag &= ~UI_BUT_DRAGPOIN_FREE;
}
but->dragpoin = (void *)ptr;
@@ -6109,7 +6143,7 @@ void UI_but_drag_set_path(uiBut *but, const char *path, const bool use_free)
{
but->dragtype = WM_DRAG_PATH;
if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) {
- MEM_SAFE_FREE(but->dragpoin);
+ WM_drag_data_free(but->dragtype, but->dragpoin);
but->dragflag &= ~UI_BUT_DRAGPOIN_FREE;
}
but->dragpoin = (void *)path;
@@ -6122,7 +6156,7 @@ void UI_but_drag_set_name(uiBut *but, const char *name)
{
but->dragtype = WM_DRAG_NAME;
if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) {
- MEM_SAFE_FREE(but->dragpoin);
+ WM_drag_data_free(but->dragtype, but->dragpoin);
but->dragflag &= ~UI_BUT_DRAGPOIN_FREE;
}
but->dragpoin = (void *)name;
@@ -6140,7 +6174,7 @@ void UI_but_drag_set_image(
but->dragtype = WM_DRAG_PATH;
ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */
if ((but->dragflag & UI_BUT_DRAGPOIN_FREE)) {
- MEM_SAFE_FREE(but->dragpoin);
+ WM_drag_data_free(but->dragtype, but->dragpoin);
but->dragflag &= ~UI_BUT_DRAGPOIN_FREE;
}
but->dragpoin = (void *)path;
diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c
index 39b405a02b8..870c3a2a13f 100644
--- a/source/blender/editors/interface/interface_context_menu.c
+++ b/source/blender/editors/interface/interface_context_menu.c
@@ -38,6 +38,7 @@
#include "BKE_idprop.h"
#include "BKE_screen.h"
+#include "ED_asset.h"
#include "ED_keyframing.h"
#include "ED_screen.h"
@@ -503,6 +504,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
uiPopupMenu *pup;
uiLayout *layout;
+ bContextStore *previous_ctx = CTX_store_get(C);
{
uiStringInfo label = {BUT_GET_LABEL, NULL};
@@ -514,6 +516,11 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
if (label.strinfo) {
MEM_freeN(label.strinfo);
}
+
+ if (but->context) {
+ uiLayoutContextCopy(layout, but->context);
+ CTX_store_set(C, uiLayoutGetContextStore(layout));
+ }
uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
}
@@ -946,6 +953,22 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
}
}
+ /* If the button reprents an id, it can set the "id" context pointer. */
+ if (ED_asset_can_make_single_from_context(C)) {
+ ID *id = CTX_data_pointer_get_type(C, "id", &RNA_ID).data;
+
+ /* Gray out items depending on if data-block is an asset. Preferably this could be done via
+ * operator poll, but that doesn't work since the operator also works with "selected_ids",
+ * which isn't cheap to check. */
+ uiLayout *sub = uiLayoutColumn(layout, true);
+ uiLayoutSetEnabled(sub, !id->asset_data);
+ uiItemO(sub, NULL, ICON_NONE, "ASSET_OT_mark");
+ sub = uiLayoutColumn(layout, true);
+ uiLayoutSetEnabled(sub, id->asset_data);
+ uiItemO(sub, NULL, ICON_NONE, "ASSET_OT_clear");
+ uiItemS(layout);
+ }
+
/* Pointer properties and string properties with
* prop_search support jumping to target object/bone. */
if (but->rnapoin.data && but->rnaprop) {
@@ -1210,6 +1233,10 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
UI_menutype_draw(C, mt, uiLayoutColumn(layout, false));
}
+ if (but->context) {
+ CTX_store_set(C, previous_ctx);
+ }
+
return UI_popup_menu_end_or_cancel(C, pup);
}
diff --git a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c
index 7f735a0e789..f2899fc0098 100644
--- a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c
+++ b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c
@@ -34,6 +34,7 @@
#include "BLT_translation.h"
#include "DNA_gpencil_types.h"
+#include "DNA_material_types.h"
#include "DNA_space_types.h"
#include "BKE_context.h"
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index f914ccd7497..72ed2cc0933 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -626,7 +626,11 @@ static bool ui_rna_is_userdef(PointerRNA *ptr, PropertyRNA *prop)
if (base == NULL) {
base = ptr->type;
}
- if (ELEM(base, &RNA_AddonPreferences, &RNA_KeyConfigPreferences, &RNA_KeyMapItem)) {
+ if (ELEM(base,
+ &RNA_AddonPreferences,
+ &RNA_KeyConfigPreferences,
+ &RNA_KeyMapItem,
+ &RNA_UserAssetLibrary)) {
tag = true;
}
}
@@ -1349,6 +1353,9 @@ static void ui_multibut_states_apply(bContext *C, uiHandleButtonData *data, uiBl
if (mbut_state == NULL) {
/* Highly unlikely. */
printf("%s: Can't find button\n", __func__);
+ /* While this avoids crashing, multi-button dragging will fail,
+ * which is still a bug from the user perspective. See T83651. */
+ continue;
}
void *active_back;
@@ -1984,6 +1991,8 @@ static bool ui_but_drag_init(bContext *C,
else {
wmDrag *drag = WM_event_start_drag(
C, but->icon, but->dragtype, but->dragpoin, ui_but_value_get(but), WM_DRAG_NOP);
+ /* wmDrag has ownership over dragpoin now, stop messing with it. */
+ but->dragpoin = NULL;
if (but->imb) {
WM_event_drag_image(drag,
@@ -2256,10 +2265,11 @@ static void ui_but_drop(bContext *C, const wmEvent *event, uiBut *but, uiHandleB
ListBase *drags = event->customdata; /* drop event type has listbase customdata by default */
LISTBASE_FOREACH (wmDrag *, wmd, drags) {
+ /* TODO asset dropping. */
if (wmd->type == WM_DRAG_ID) {
/* align these types with UI_but_active_drop_name */
if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) {
- ID *id = WM_drag_ID(wmd, 0);
+ ID *id = WM_drag_get_local_ID(wmd, 0);
button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING);
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index 90f5172f6ec..899f4a6ddb1 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -100,11 +100,12 @@ typedef void (*VectorDrawFunc)(int x, int y, int w, int h, float alpha);
#define ICON_TYPE_COLOR_TEXTURE 1
#define ICON_TYPE_MONO_TEXTURE 2
#define ICON_TYPE_BUFFER 3
-#define ICON_TYPE_VECTOR 4
-#define ICON_TYPE_GEOM 5
-#define ICON_TYPE_EVENT 6 /* draw keymap entries using custom renderer. */
-#define ICON_TYPE_GPLAYER 7
-#define ICON_TYPE_BLANK 8
+#define ICON_TYPE_IMBUF 4
+#define ICON_TYPE_VECTOR 5
+#define ICON_TYPE_GEOM 6
+#define ICON_TYPE_EVENT 7 /* draw keymap entries using custom renderer. */
+#define ICON_TYPE_GPLAYER 8
+#define ICON_TYPE_BLANK 9
typedef struct DrawInfo {
int type;
@@ -1147,6 +1148,9 @@ static DrawInfo *icon_create_drawinfo(Icon *icon)
if (ELEM(icon_data_type, ICON_DATA_ID, ICON_DATA_PREVIEW)) {
di->type = ICON_TYPE_PREVIEW;
}
+ else if (icon_data_type == ICON_DATA_IMBUF) {
+ di->type = ICON_TYPE_IMBUF;
+ }
else if (icon_data_type == ICON_DATA_GEOM) {
di->type = ICON_TYPE_GEOM;
}
@@ -1262,7 +1266,7 @@ static void icon_create_rect(struct PreviewImage *prv_img, enum eIconSizes size)
else if (!prv_img->rect[size]) {
prv_img->w[size] = render_size;
prv_img->h[size] = render_size;
- prv_img->flag[size] |= PRV_CHANGED;
+ prv_img->flag[size] |= (PRV_CHANGED | PRV_UNFINISHED);
prv_img->changed_timestamp[size] = 0;
prv_img->rect[size] = MEM_callocN(render_size * render_size * sizeof(uint), "prv_rect");
}
@@ -1384,8 +1388,12 @@ void ui_icon_ensure_deferred(const bContext *C, const int icon_id, const bool bi
}
}
-/* only called when icon has changed */
-/* only call with valid pointer from UI_icon_draw */
+/**
+ * * Only call with valid pointer from UI_icon_draw.
+ * * Only called when icon has changed.
+ *
+ * Note that if an ID doesn't support jobs for preview creation, \a use_job will be ignored.
+ */
static void icon_set_image(const bContext *C,
Scene *scene,
ID *id,
@@ -1408,7 +1416,7 @@ static void icon_set_image(const bContext *C,
const bool delay = prv_img->rect[size] != NULL;
icon_create_rect(prv_img, size);
- if (use_job) {
+ if (use_job && (!id || BKE_previewimg_id_supports_jobs(id))) {
/* Job (background) version */
ED_preview_icon_job(
C, prv_img, id, prv_img->rect[size], prv_img->w[size], prv_img->h[size], delay);
@@ -1790,7 +1798,14 @@ static void icon_draw_size(float x,
/* We need to flush widget base first to ensure correct ordering. */
UI_widgetbase_draw_cache_flush();
- if (di->type == ICON_TYPE_VECTOR) {
+ if (di->type == ICON_TYPE_IMBUF) {
+ ImBuf *ibuf = icon->obj;
+
+ GPU_blend(GPU_BLEND_ALPHA_PREMULT);
+ icon_draw_rect(x, y, w, h, aspect, ibuf->x, ibuf->y, ibuf->rect, alpha, desaturate);
+ GPU_blend(GPU_BLEND_ALPHA);
+ }
+ else if (di->type == ICON_TYPE_VECTOR) {
/* vector icons use the uiBlock transformation, they are not drawn
* with untransformed coordinates like the other icons */
di->data.vector.func((int)x, (int)y, w, h, 1.0f);
@@ -1937,6 +1952,9 @@ static void ui_id_preview_image_render_size(
}
}
+/**
+ * Note that if an ID doesn't support jobs for preview creation, \a use_job will be ignored.
+ */
void UI_icon_render_id(const bContext *C, Scene *scene, ID *id, const bool big, const bool use_job)
{
PreviewImage *pi = BKE_previewimg_id_ensure(id);
@@ -1964,12 +1982,7 @@ static void ui_id_icon_render(const bContext *C, ID *id, bool use_jobs)
}
for (enum eIconSizes i = 0; i < NUM_ICON_SIZES; i++) {
- /* check if rect needs to be created; changed
- * only set by dynamic icons */
- if (((pi->flag[i] & PRV_CHANGED) || !pi->rect[i])) {
- icon_set_image(C, NULL, id, pi, i, use_jobs);
- pi->flag[i] &= ~PRV_CHANGED;
- }
+ ui_id_preview_image_render_size(C, NULL, id, pi, i, use_jobs);
}
}
@@ -2186,6 +2199,9 @@ int UI_icon_from_library(const ID *id)
if (ID_IS_OVERRIDE_LIBRARY(id)) {
return ICON_LIBRARY_DATA_OVERRIDE;
}
+ if (ID_IS_ASSET(id)) {
+ return ICON_MAT_SPHERE_SKY;
+ }
return ICON_NONE;
}
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
index 0403287125c..4b19b8f97f4 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -651,7 +651,7 @@ static void ui_item_array(uiLayout *layout,
uiDefAutoButR(block, ptr, prop, -1, "", ICON_NONE, 0, 0, w, UI_UNIT_Y);
}
else {
- /* even if 'expand' is fale, expanding anyway */
+ /* Even if 'expand' is false, we expand anyway. */
/* layout for known array subtypes */
char str[3] = {'\0'};
@@ -5663,6 +5663,11 @@ void uiLayoutSetContextPointer(uiLayout *layout, const char *name, PointerRNA *p
layout->context = CTX_store_add(&block->contexts, name, ptr);
}
+bContextStore *uiLayoutGetContextStore(uiLayout *layout)
+{
+ return layout->context;
+}
+
void uiLayoutContextCopy(uiLayout *layout, bContextStore *context)
{
uiBlock *block = layout->root->block;
diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c
index c3d528ad5c5..a37fb0dfde1 100644
--- a/source/blender/editors/interface/interface_style.c
+++ b/source/blender/editors/interface/interface_style.c
@@ -206,8 +206,12 @@ void UI_fontstyle_draw_ex(const uiFontStyle *fs,
BLF_disable(fs->uifont_id, font_flag);
- *r_xofs = xofs;
- *r_yofs = yofs;
+ if (r_xofs) {
+ *r_xofs = xofs;
+ }
+ if (r_yofs) {
+ *r_yofs = yofs;
+ }
}
void UI_fontstyle_draw(const uiFontStyle *fs,
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 43ead511cfe..b895f1702f4 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -895,6 +895,9 @@ static void template_ID(const bContext *C,
idfrom = template_ui->ptr.owner_id;
// lb = template_ui->idlb;
+ /* Allow opertators to take the ID from context. */
+ uiLayoutSetContextPointer(layout, "id", &idptr);
+
block = uiLayoutGetBlock(layout);
UI_block_align_begin(block);
@@ -2011,7 +2014,7 @@ static void set_constraint_expand_flag(const bContext *UNUSED(C), Panel *panel,
/**
* Function with void * argument for #uiListPanelIDFromDataFunc.
*
- * \note: Constraint panel types are assumed to be named with the struct name field
+ * \note Constraint panel types are assumed to be named with the struct name field
* concatenated to the defined prefix.
*/
static void object_constraint_panel_id(void *md_link, char *r_name)
@@ -6170,6 +6173,10 @@ void uiTemplateList(uiLayout *layout,
org_i,
flt_flag);
+ /* Items should be able to set context pointers for the layout. But the list-row button
+ * swallows events, so it needs the context storage too for handlers to see it. */
+ but->context = uiLayoutGetContextStore(sub);
+
/* If we are "drawing" active item, set all labels as active. */
if (i == activei) {
ui_layout_list_set_labels_active(sub);
diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c
index a5999962e09..7de0ec0255f 100644
--- a/source/blender/editors/interface/view2d_ops.c
+++ b/source/blender/editors/interface/view2d_ops.c
@@ -109,14 +109,13 @@ typedef struct v2dViewPanData {
static bool view_pan_poll(bContext *C)
{
ARegion *region = CTX_wm_region(C);
- View2D *v2d;
/* check if there's a region in context to work with */
if (region == NULL) {
return false;
}
- v2d = &region->v2d;
+ View2D *v2d = &region->v2d;
/* check that 2d-view can pan */
if ((v2d->keepofs & V2D_LOCKOFS_X) && (v2d->keepofs & V2D_LOCKOFS_Y)) {
@@ -191,10 +190,7 @@ static void view_pan_apply(bContext *C, wmOperator *op)
/* cleanup temp customdata */
static void view_pan_exit(wmOperator *op)
{
- if (op->customdata) {
- MEM_freeN(op->customdata);
- op->customdata = NULL;
- }
+ MEM_SAFE_FREE(op->customdata);
}
/** \} */
@@ -216,14 +212,12 @@ static int view_pan_exec(bContext *C, wmOperator *op)
static int view_pan_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
wmWindow *window = CTX_wm_window(C);
- v2dViewPanData *vpd;
- View2D *v2d;
/* set up customdata */
view_pan_init(C, op);
- vpd = op->customdata;
- v2d = vpd->v2d;
+ v2dViewPanData *vpd = op->customdata;
+ View2D *v2d = vpd->v2d;
/* set initial settings */
vpd->startx = vpd->lastx = event->x;
@@ -787,7 +781,6 @@ static void view_zoom_axis_lock_defaults(bContext *C, bool r_do_zoom_xy[2])
static bool view_zoom_poll(bContext *C)
{
ARegion *region = CTX_wm_region(C);
- View2D *v2d;
/* check if there's a region in context to work with */
if (region == NULL) {
@@ -799,7 +792,7 @@ static bool view_zoom_poll(bContext *C)
return false;
}
- v2d = &region->v2d;
+ View2D *v2d = &region->v2d;
/* check that 2d-view is zoomable */
if ((v2d->keepzoom & V2D_LOCKZOOM_X) && (v2d->keepzoom & V2D_LOCKZOOM_Y)) {
@@ -836,12 +829,12 @@ static void view_zoomstep_apply_ex(bContext *C,
ARegion *region = CTX_wm_region(C);
View2D *v2d = &region->v2d;
const rctf cur_old = v2d->cur;
- float dx, dy;
const int snap_test = ED_region_snap_size_test(region);
/* calculate amount to move view by, ensuring symmetry so the
* old zoom level is restored after zooming back the same amount
*/
+ float dx, dy;
if (facx >= 0.0f) {
dx = BLI_rctf_size_x(&v2d->cur) * facx;
dy = BLI_rctf_size_y(&v2d->cur) * facy;
@@ -955,10 +948,7 @@ static void view_zoomstep_exit(wmOperator *op)
{
UI_view2d_zoom_cache_reset();
- if (op->customdata) {
- MEM_freeN(op->customdata);
- op->customdata = NULL;
- }
+ MEM_SAFE_FREE(op->customdata);
}
/* this operator only needs this single callback, where it calls the view_zoom_*() methods */
@@ -1107,15 +1097,14 @@ static void view_zoomdrag_apply(bContext *C, wmOperator *op)
{
v2dViewZoomData *vzd = op->customdata;
View2D *v2d = vzd->v2d;
- float dx, dy;
const int snap_test = ED_region_snap_size_test(vzd->region);
const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init");
const bool zoom_to_pos = use_cursor_init && vzd->zoom_to_mouse_pos;
/* get amount to move view by */
- dx = RNA_float_get(op->ptr, "deltax") / U.dpi_fac;
- dy = RNA_float_get(op->ptr, "deltay") / U.dpi_fac;
+ float dx = RNA_float_get(op->ptr, "deltax") / U.dpi_fac;
+ float dy = RNA_float_get(op->ptr, "deltay") / U.dpi_fac;
if (U.uiflag & USER_ZOOM_INVERT) {
dx *= -1;
@@ -1223,14 +1212,12 @@ static int view_zoomdrag_exec(bContext *C, wmOperator *op)
static int view_zoomdrag_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
wmWindow *window = CTX_wm_window(C);
- v2dViewZoomData *vzd;
- View2D *v2d;
/* set up customdata */
view_zoomdrag_init(C, op);
- vzd = op->customdata;
- v2d = vzd->v2d;
+ v2dViewZoomData *vzd = op->customdata;
+ View2D *v2d = vzd->v2d;
if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) {
ARegion *region = CTX_wm_region(C);
@@ -1242,20 +1229,18 @@ static int view_zoomdrag_invoke(bContext *C, wmOperator *op, const wmEvent *even
}
if (ELEM(event->type, MOUSEZOOM, MOUSEPAN)) {
- float dx, dy, fac;
-
vzd->lastx = event->prevx;
vzd->lasty = event->prevy;
/* As we have only 1D information (magnify value), feed both axes
* with magnify information that is stored in x axis
*/
- fac = 0.01f * (event->prevx - event->x);
- dx = fac * BLI_rctf_size_x(&v2d->cur) / 10.0f;
+ float fac = 0.01f * (event->prevx - event->x);
+ float dx = fac * BLI_rctf_size_x(&v2d->cur) / 10.0f;
if (event->type == MOUSEPAN) {
fac = 0.01f * (event->prevy - event->y);
}
- dy = fac * BLI_rctf_size_y(&v2d->cur) / 10.0f;
+ float dy = fac * BLI_rctf_size_y(&v2d->cur) / 10.0f;
/* support trackpad zoom to always zoom entirely - the v2d code uses portrait or
* landscape exceptions */
@@ -1491,11 +1476,11 @@ static int view_borderzoom_exec(bContext *C, wmOperator *op)
{
ARegion *region = CTX_wm_region(C);
View2D *v2d = &region->v2d;
- rctf rect;
rctf cur_new = v2d->cur;
const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
/* convert coordinates of rect to 'tot' rect coordinates */
+ rctf rect;
WM_operator_properties_border_to_rctf(op, &rect);
UI_view2d_region_to_view_rctf(v2d, &rect, &rect);
@@ -1766,13 +1751,13 @@ static int view2d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const w
ARegion *region = CTX_wm_region(C);
View2D *v2d = &region->v2d;
struct SmoothView2DStore *sms = v2d->sms;
- float step;
/* escape if not our timer */
if (v2d->smooth_timer == NULL || v2d->smooth_timer != event->customdata) {
return OPERATOR_PASS_THROUGH;
}
+ float step;
if (sms->time_allowed != 0.0) {
step = (float)((v2d->smooth_timer->duration) / sms->time_allowed);
}
@@ -1978,15 +1963,11 @@ static void scroller_activate_init(bContext *C,
const wmEvent *event,
const char in_scroller)
{
- v2dScrollerMove *vsm;
- View2DScrollers scrollers;
ARegion *region = CTX_wm_region(C);
View2D *v2d = &region->v2d;
- rctf tot_cur_union;
- float mask_size;
/* set custom-data for operator */
- vsm = MEM_callocN(sizeof(v2dScrollerMove), "v2dScrollerMove");
+ v2dScrollerMove *vsm = MEM_callocN(sizeof(v2dScrollerMove), "v2dScrollerMove");
op->customdata = vsm;
/* set general data */
@@ -2000,16 +1981,17 @@ static void scroller_activate_init(bContext *C,
/* 'zone' depends on where mouse is relative to bubble
* - zooming must be allowed on this axis, otherwise, default to pan
*/
+ View2DScrollers scrollers;
UI_view2d_scrollers_calc(v2d, NULL, &scrollers);
/* Use a union of 'cur' & 'tot' in case the current view is far outside 'tot'. In this cases
* moving the scroll bars has far too little effect and the view can get stuck T31476. */
- tot_cur_union = v2d->tot;
+ rctf tot_cur_union = v2d->tot;
BLI_rctf_union(&tot_cur_union, &v2d->cur);
if (in_scroller == 'h') {
/* horizontal scroller - calculate adjustment factor first */
- mask_size = (float)BLI_rcti_size_x(&v2d->hor);
+ float mask_size = (float)BLI_rcti_size_x(&v2d->hor);
vsm->fac = BLI_rctf_size_x(&tot_cur_union) / mask_size;
/* pixel rounding */
@@ -2029,7 +2011,7 @@ static void scroller_activate_init(bContext *C,
}
else {
/* vertical scroller - calculate adjustment factor first */
- mask_size = (float)BLI_rcti_size_y(&v2d->vert);
+ float mask_size = (float)BLI_rcti_size_y(&v2d->vert);
vsm->fac = BLI_rctf_size_y(&tot_cur_union) / mask_size;
/* pixel rounding */
@@ -2076,10 +2058,9 @@ static void scroller_activate_apply(bContext *C, wmOperator *op)
{
v2dScrollerMove *vsm = op->customdata;
View2D *v2d = vsm->v2d;
- float temp;
/* calculate amount to move view by */
- temp = vsm->fac * vsm->delta;
+ float temp = vsm->fac * vsm->delta;
/* round to pixel */
temp = roundf(temp / vsm->fac_round) * vsm->fac_round;
@@ -2219,11 +2200,9 @@ static int scroller_activate_invoke(bContext *C, wmOperator *op, const wmEvent *
/* if in a scroller, init customdata then set modal handler which will
* catch mouse-down to start doing useful stuff */
if (in_scroller) {
- v2dScrollerMove *vsm;
-
/* initialize customdata */
scroller_activate_init(C, op, event, in_scroller);
- vsm = (v2dScrollerMove *)op->customdata;
+ v2dScrollerMove *vsm = (v2dScrollerMove *)op->customdata;
/* support for quick jump to location - gtk and qt do this on linux */
if (event->type == MIDDLEMOUSE) {
@@ -2324,12 +2303,11 @@ static int reset_exec(bContext *C, wmOperator *UNUSED(op))
const uiStyle *style = UI_style_get();
ARegion *region = CTX_wm_region(C);
View2D *v2d = &region->v2d;
- int winx, winy;
const int snap_test = ED_region_snap_size_test(region);
/* zoom 1.0 */
- winx = (float)(BLI_rcti_size_x(&v2d->mask) + 1);
- winy = (float)(BLI_rcti_size_y(&v2d->mask) + 1);
+ const int winx = (float)(BLI_rcti_size_x(&v2d->mask) + 1);
+ const int winy = (float)(BLI_rcti_size_y(&v2d->mask) + 1);
v2d->cur.xmax = v2d->cur.xmin + winx;
v2d->cur.ymax = v2d->cur.ymin + winy;
diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c
index b8badd207fe..e788b28d3b4 100644
--- a/source/blender/editors/mesh/editmesh_bevel.c
+++ b/source/blender/editors/mesh/editmesh_bevel.c
@@ -1129,7 +1129,7 @@ void MESH_OT_bevel(wmOperatorType *ot)
prop_affect_items,
BEVEL_AFFECT_EDGES,
"Affect",
- "Affect Edges or Vertices");
+ "Affect edges or vertices");
RNA_def_boolean(ot->srna,
"clamp_overlap",
diff --git a/source/blender/editors/mesh/editmesh_knife_project.c b/source/blender/editors/mesh/editmesh_knife_project.c
index ef78d31a6bb..aa144dd3f3c 100644
--- a/source/blender/editors/mesh/editmesh_knife_project.c
+++ b/source/blender/editors/mesh/editmesh_knife_project.c
@@ -177,6 +177,6 @@ void MESH_OT_knife_project(wmOperatorType *ot)
RNA_def_boolean(ot->srna,
"cut_through",
false,
- "Cut through",
+ "Cut Through",
"Cut through all faces, not just visible ones");
}
diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c
index a2fe949b6c5..d3eaa9048d3 100644
--- a/source/blender/editors/mesh/editmesh_select.c
+++ b/source/blender/editors/mesh/editmesh_select.c
@@ -4552,7 +4552,8 @@ void MESH_OT_select_non_manifold(wmOperatorType *ot)
/* edges */
RNA_def_boolean(ot->srna, "use_wire", true, "Wire", "Wire edges");
RNA_def_boolean(ot->srna, "use_boundary", true, "Boundaries", "Boundary edges");
- RNA_def_boolean(ot->srna, "use_multi_face", true, "Multiple Faces", "Edges shared by 3+ faces");
+ RNA_def_boolean(
+ ot->srna, "use_multi_face", true, "Multiple Faces", "Edges shared by more than two faces");
RNA_def_boolean(ot->srna,
"use_non_contiguous",
true,
diff --git a/source/blender/editors/mesh/editmesh_select_similar.c b/source/blender/editors/mesh/editmesh_select_similar.c
index 00349983c57..d762eede079 100644
--- a/source/blender/editors/mesh/editmesh_select_similar.c
+++ b/source/blender/editors/mesh/editmesh_select_similar.c
@@ -34,6 +34,7 @@
#include "BKE_material.h"
#include "BKE_report.h"
+#include "DNA_material_types.h"
#include "DNA_meshdata_types.h"
#include "WM_api.h"
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index b961f81e16a..cf01170dd8a 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -180,7 +180,7 @@ void MESH_OT_subdivide(wmOperatorType *ot)
"ngon",
true,
"Create N-Gons",
- "When disabled, newly created faces are limited to 3-4 sided faces");
+ "When disabled, newly created faces are limited to 3 and 4 sided faces");
RNA_def_enum(
ot->srna,
"quadcorner",
@@ -395,7 +395,7 @@ void MESH_OT_unsubdivide(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Un-Subdivide";
- ot->description = "UnSubdivide selected edges & faces";
+ ot->description = "Un-subdivide selected edges and faces";
ot->idname = "MESH_OT_unsubdivide";
/* api callbacks */
@@ -698,7 +698,7 @@ void MESH_OT_edge_collapse(wmOperatorType *ot)
/* identifiers */
ot->name = "Collapse Edges & Faces";
ot->description =
- "Collapse isolated edges & faces regions, merging data such as UV's and vertex colors. "
+ "Collapse isolated edge and face regions, merging data such as UV's and vertex colors. "
"This can collapse edge-rings as well as regions of connected faces into vertices";
ot->idname = "MESH_OT_edge_collapse";
@@ -1904,7 +1904,7 @@ void MESH_OT_edge_split(wmOperatorType *ot)
"VERT",
0,
"Faces & Edges by Vertices",
- "Split faces & edges connected to selected vertices"},
+ "Split faces and edges connected to selected vertices"},
{0, NULL, 0, NULL, NULL},
};
diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c
index 43cad2db185..cff5414da75 100644
--- a/source/blender/editors/mesh/editmesh_undo.c
+++ b/source/blender/editors/mesh/editmesh_undo.c
@@ -27,6 +27,7 @@
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
#include "BLI_array_utils.h"
#include "BLI_listbase.h"
diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c
index 6467df0e87b..94f386e08d5 100644
--- a/source/blender/editors/mesh/editmesh_utils.c
+++ b/source/blender/editors/mesh/editmesh_utils.c
@@ -1139,8 +1139,10 @@ void EDBM_verts_mirror_cache_begin_ex(BMEditMesh *em,
if (use_topology) {
v_mirr = cache_mirr_intptr_as_bmvert(mesh_topo_store.index_lookup, i);
- if (respecthide && BM_elem_flag_test(v_mirr, BM_ELEM_HIDDEN)) {
- v_mirr = NULL;
+ if (v_mirr != NULL) {
+ if (respecthide && BM_elem_flag_test(v_mirr, BM_ELEM_HIDDEN)) {
+ v_mirr = NULL;
+ }
}
}
else {
diff --git a/source/blender/editors/metaball/editmball_undo.c b/source/blender/editors/metaball/editmball_undo.c
index 552e459acb1..b4030ad269b 100644
--- a/source/blender/editors/metaball/editmball_undo.c
+++ b/source/blender/editors/metaball/editmball_undo.c
@@ -30,8 +30,10 @@
#include "BLI_utildefines.h"
#include "DNA_defs.h"
+#include "DNA_layer_types.h"
#include "DNA_meta_types.h"
#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
#include "BKE_context.h"
#include "BKE_layer.h"
diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt
index a1be9b9df61..77b5379ddd4 100644
--- a/source/blender/editors/object/CMakeLists.txt
+++ b/source/blender/editors/object/CMakeLists.txt
@@ -31,8 +31,8 @@ set(INC
../../makesrna
../../modifiers
../../python
- ../../shader_fx
../../render
+ ../../shader_fx
../../windowmanager
../../../../intern/clog
../../../../intern/glew-mx
@@ -88,6 +88,7 @@ endif()
if(WITH_EXPERIMENTAL_FEATURES)
add_definitions(-DWITH_GEOMETRY_NODES)
+ add_definitions(-DWITH_POINT_CLOUD)
add_definitions(-DWITH_HAIR_NODES)
endif()
diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c
index bbfdfb2532d..a64033bc63a 100644
--- a/source/blender/editors/object/object_add.c
+++ b/source/blender/editors/object/object_add.c
@@ -1714,6 +1714,9 @@ void OBJECT_OT_hair_add(wmOperatorType *ot)
static bool object_pointcloud_add_poll(bContext *C)
{
+ if (!U.experimental.use_new_point_cloud_type) {
+ return false;
+ }
return ED_operator_objectmode(C);
}
@@ -2316,17 +2319,23 @@ static const EnumPropertyItem convert_target_items[] = {
"MESH",
ICON_OUTLINER_OB_MESH,
"Mesh",
+#ifdef WITH_POINT_CLOUD
"Mesh from Curve, Surface, Metaball, Text, or Pointcloud objects"},
+#else
+ "Mesh from Curve, Surface, Metaball, or Text objects"},
+#endif
{OB_GPENCIL,
"GPENCIL",
ICON_OUTLINER_OB_GREASEPENCIL,
"Grease Pencil",
"Grease Pencil from Curve or Mesh objects"},
+#ifdef WITH_POINT_CLOUD
{OB_POINTCLOUD,
"POINTCLOUD",
ICON_OUTLINER_OB_POINTCLOUD,
"Pointcloud",
"Pointcloud from Mesh objects"},
+#endif
{0, NULL, 0, NULL, NULL},
};
diff --git a/source/blender/editors/object/object_bake.c b/source/blender/editors/object/object_bake.c
index 008498a1735..9618774eea8 100644
--- a/source/blender/editors/object/object_bake.c
+++ b/source/blender/editors/object/object_bake.c
@@ -25,6 +25,7 @@
#include "MEM_guardedalloc.h"
+#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
diff --git a/source/blender/editors/object/object_gpencil_modifier.c b/source/blender/editors/object/object_gpencil_modifier.c
index af95f5581bd..e5feb74df26 100644
--- a/source/blender/editors/object/object_gpencil_modifier.c
+++ b/source/blender/editors/object/object_gpencil_modifier.c
@@ -461,7 +461,7 @@ static bool gpencil_edit_modifier_poll(bContext *C)
* (not only from added 'local' ones). */
static bool gpencil_edit_modifier_liboverride_allowed_poll(bContext *C)
{
- return gpencil_edit_modifier_poll_generic(C, &RNA_Modifier, 0, true);
+ return gpencil_edit_modifier_poll_generic(C, &RNA_GpencilModifier, 0, true);
}
static void gpencil_edit_modifier_properties(wmOperatorType *ot)
@@ -789,7 +789,7 @@ void OBJECT_OT_gpencil_modifier_apply(wmOperatorType *ot)
"apply_as",
gpencil_modifier_apply_as_items,
MODIFIER_APPLY_DATA,
- "Apply as",
+ "Apply As",
"How to apply the modifier to the geometry");
gpencil_edit_modifier_properties(ot);
gpencil_edit_modifier_report_property(ot);
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index e6ef53a3d65..89ade5cc49d 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -206,6 +206,7 @@ void OBJECT_OT_gpencil_modifier_copy(struct wmOperatorType *ot);
/* object_shader_fx.c */
void OBJECT_OT_shaderfx_add(struct wmOperatorType *ot);
+void OBJECT_OT_shaderfx_copy(struct wmOperatorType *ot);
void OBJECT_OT_shaderfx_remove(struct wmOperatorType *ot);
void OBJECT_OT_shaderfx_move_up(struct wmOperatorType *ot);
void OBJECT_OT_shaderfx_move_down(struct wmOperatorType *ot);
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index 8ba0ce5fd08..2e5a75ffa7d 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -166,6 +166,7 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_shaderfx_move_up);
WM_operatortype_append(OBJECT_OT_shaderfx_move_down);
WM_operatortype_append(OBJECT_OT_shaderfx_move_to_index);
+ WM_operatortype_append(OBJECT_OT_shaderfx_copy);
WM_operatortype_append(OBJECT_OT_correctivesmooth_bind);
WM_operatortype_append(OBJECT_OT_meshdeform_bind);
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index 8841b1955bf..5caa7c71e83 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -1554,6 +1554,7 @@ enum {
MAKE_LINKS_DUPLICOLLECTION = 5,
MAKE_LINKS_MODIFIERS = 6,
MAKE_LINKS_FONTS = 7,
+ MAKE_LINKS_SHADERFX = 8,
};
/* Return true if make link data is allowed, false otherwise */
@@ -1589,6 +1590,11 @@ static bool allow_make_links_data(const int type, Object *ob_src, Object *ob_dst
return true;
}
break;
+ case MAKE_LINKS_SHADERFX:
+ if ((ob_src->type == OB_GPENCIL) && (ob_dst->type == OB_GPENCIL)) {
+ return true;
+ }
+ break;
}
return false;
}
@@ -1720,6 +1726,11 @@ static int make_links_data_exec(bContext *C, wmOperator *op)
ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
break;
}
+ case MAKE_LINKS_SHADERFX:
+ ED_object_shaderfx_link(ob_dst, ob_src);
+ DEG_id_tag_update(&ob_dst->id,
+ ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
+ break;
}
}
}
@@ -1782,6 +1793,7 @@ void OBJECT_OT_make_links_data(wmOperatorType *ot)
{MAKE_LINKS_DUPLICOLLECTION, "DUPLICOLLECTION", 0, "Instance Collection", ""},
{MAKE_LINKS_MODIFIERS, "MODIFIERS", 0, "Modifiers", ""},
{MAKE_LINKS_FONTS, "FONTS", 0, "Fonts", ""},
+ {MAKE_LINKS_SHADERFX, "EFFECTS", 0, "Effects", ""},
{0, NULL, 0, NULL, NULL},
};
@@ -2515,7 +2527,7 @@ static bool convert_proxy_to_override_poll(bContext *C)
return obact != NULL && obact->proxy != NULL;
}
-static int convert_proxy_to_override_exec(bContext *C, wmOperator *UNUSED(op))
+static int convert_proxy_to_override_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
@@ -2529,6 +2541,15 @@ static int convert_proxy_to_override_exec(bContext *C, wmOperator *UNUSED(op))
const bool success = BKE_lib_override_library_proxy_convert(bmain, scene, view_layer, ob_proxy);
+ if (!success) {
+ BKE_reportf(
+ op->reports,
+ RPT_ERROR_INVALID_INPUT,
+ "Could not create a library override from proxy '%s' (might use already local data?)",
+ ob_proxy->id.name + 2);
+ return OPERATOR_CANCELLED;
+ }
+
/* Remove the instance empty from this scene, the items now have an overridden collection
* instead. */
if (success && is_override_instancing_object) {
@@ -2544,7 +2565,7 @@ static int convert_proxy_to_override_exec(bContext *C, wmOperator *UNUSED(op))
void OBJECT_OT_convert_proxy_to_override(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Convert Proxy To Override";
+ ot->name = "Convert Proxy to Override";
ot->description = "Convert a proxy to a local library override";
ot->idname = "OBJECT_OT_convert_proxy_to_override";
diff --git a/source/blender/editors/object/object_shader_fx.c b/source/blender/editors/object/object_shader_fx.c
index c5caee5ba08..2b1ac08ec2e 100644
--- a/source/blender/editors/object/object_shader_fx.c
+++ b/source/blender/editors/object/object_shader_fx.c
@@ -640,3 +640,57 @@ void OBJECT_OT_shaderfx_move_to_index(wmOperatorType *ot)
RNA_def_int(
ot->srna, "index", 0, 0, INT_MAX, "Index", "The index to move the effect to", 0, INT_MAX);
}
+
+/************************ copy shader operator *********************/
+
+static int shaderfx_copy_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = ED_object_active_context(C);
+ ShaderFxData *fx = edit_shaderfx_property_get(op, ob, 0);
+
+ ShaderFxData *nfx = BKE_shaderfx_new(fx->type);
+ if (!nfx) {
+ return OPERATOR_CANCELLED;
+ }
+
+ BLI_strncpy(nfx->name, fx->name, sizeof(nfx->name));
+ /* Make sure effect data has unique name. */
+ BKE_shaderfx_unique_name(&ob->shader_fx, nfx);
+
+ BKE_shaderfx_copydata(fx, nfx);
+ BLI_insertlinkafter(&ob->shader_fx, fx, nfx);
+
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ WM_main_add_notifier(NC_OBJECT | ND_SHADERFX, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+static int shaderfx_copy_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ int retval;
+ if (edit_shaderfx_invoke_properties(C, op, event, &retval)) {
+ return shaderfx_copy_exec(C, op);
+ }
+ return retval;
+}
+
+static bool shaderfx_copy_poll(bContext *C)
+{
+ return edit_shaderfx_poll_generic(C, &RNA_ShaderFx, 0);
+}
+
+void OBJECT_OT_shaderfx_copy(wmOperatorType *ot)
+{
+ ot->name = "Copy Effect";
+ ot->description = "Duplicate effect at the same position in the stack";
+ ot->idname = "OBJECT_OT_shaderfx_copy";
+
+ ot->invoke = shaderfx_copy_invoke;
+ ot->exec = shaderfx_copy_exec;
+ ot->poll = shaderfx_copy_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
+ edit_shaderfx_properties(ot);
+}
diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c
index 3d6a6abfe0d..23f1718cb2e 100644
--- a/source/blender/editors/object/object_vgroup.c
+++ b/source/blender/editors/object/object_vgroup.c
@@ -741,6 +741,9 @@ const EnumPropertyItem *ED_object_vgroup_selection_itemf_helper(const bContext *
RNA_enum_items_add_value(
&item, &totitem, WT_vertex_group_select_item, WT_VGROUP_BONE_SELECT);
}
+ }
+
+ if (BKE_modifiers_is_deformed_by_armature(ob)) {
if (selection_mask & (1 << WT_VGROUP_BONE_DEFORM)) {
RNA_enum_items_add_value(
&item, &totitem, WT_vertex_group_select_item, WT_VGROUP_BONE_DEFORM);
diff --git a/source/blender/editors/physics/particle_object.c b/source/blender/editors/physics/particle_object.c
index 017cd63d9d5..f5c3fc17552 100644
--- a/source/blender/editors/physics/particle_object.c
+++ b/source/blender/editors/physics/particle_object.c
@@ -1203,6 +1203,9 @@ static bool copy_particle_systems_to_object(const bContext *C,
#undef PSYS_FROM_FIRST
#undef PSYS_FROM_NEXT
+ if (duplicate_settings) {
+ DEG_relations_tag_update(bmain);
+ }
DEG_id_tag_update(&ob_to->id, ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_OBJECT | ND_PARTICLE | NA_EDITED, ob_to);
return true;
diff --git a/source/blender/editors/physics/rigidbody_constraint.c b/source/blender/editors/physics/rigidbody_constraint.c
index 4939bf0086b..cb7ca5bd5d1 100644
--- a/source/blender/editors/physics/rigidbody_constraint.c
+++ b/source/blender/editors/physics/rigidbody_constraint.c
@@ -25,6 +25,7 @@
#include <stdlib.h>
#include <string.h>
+#include "DNA_collection_types.h"
#include "DNA_object_types.h"
#include "DNA_rigidbody_types.h"
#include "DNA_scene_types.h"
diff --git a/source/blender/editors/physics/rigidbody_object.c b/source/blender/editors/physics/rigidbody_object.c
index cb25363d2b2..4fd304ea71d 100644
--- a/source/blender/editors/physics/rigidbody_object.c
+++ b/source/blender/editors/physics/rigidbody_object.c
@@ -25,6 +25,7 @@
#include <stdlib.h>
#include <string.h>
+#include "DNA_collection_types.h"
#include "DNA_object_types.h"
#include "DNA_rigidbody_types.h"
#include "DNA_scene_types.h"
diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c
index 3dbf70aa4bc..a035ee3e342 100644
--- a/source/blender/editors/render/render_internal.c
+++ b/source/blender/editors/render/render_internal.c
@@ -150,31 +150,31 @@ static void image_buffer_rect_update(RenderJob *rj,
}
/* xmin here is first subrect x coord, xmax defines subrect width */
- xmin = renrect->xmin + rr->crop;
- xmax = renrect->xmax - xmin + rr->crop;
+ xmin = renrect->xmin;
+ xmax = renrect->xmax - xmin;
if (xmax < 2) {
return;
}
- ymin = renrect->ymin + rr->crop;
- ymax = renrect->ymax - ymin + rr->crop;
+ ymin = renrect->ymin;
+ ymax = renrect->ymax - ymin;
if (ymax < 2) {
return;
}
renrect->ymin = renrect->ymax;
}
else {
- xmin = ymin = rr->crop;
- xmax = rr->rectx - 2 * rr->crop;
- ymax = rr->recty - 2 * rr->crop;
+ xmin = ymin = 0;
+ xmax = rr->rectx;
+ ymax = rr->recty;
}
/* xmin ymin is in tile coords. transform to ibuf */
- rxmin = rr->tilerect.xmin + xmin;
+ rxmin = rr->tilerect.xmin;
if (rxmin >= ibuf->x) {
return;
}
- rymin = rr->tilerect.ymin + ymin;
+ rymin = rr->tilerect.ymin;
if (rymin >= ibuf->y) {
return;
}
diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c
index 095deccada0..579fd86077e 100644
--- a/source/blender/editors/render/render_preview.c
+++ b/source/blender/editors/render/render_preview.c
@@ -68,6 +68,7 @@
#include "BKE_main.h"
#include "BKE_material.h"
#include "BKE_node.h"
+#include "BKE_object.h"
#include "BKE_scene.h"
#include "BKE_texture.h"
#include "BKE_world.h"
@@ -94,12 +95,16 @@
#include "ED_datafiles.h"
#include "ED_render.h"
#include "ED_screen.h"
+#include "ED_view3d.h"
+#include "ED_view3d_offscreen.h"
#ifndef NDEBUG
/* Used for database init assert(). */
# include "BLI_threads.h"
#endif
+static void icon_copy_rect(ImBuf *ibuf, uint w, uint h, uint *rect);
+
ImBuf *get_brush_icon(Brush *brush)
{
static const int flags = IB_rect | IB_multilayer | IB_metadata;
@@ -184,7 +189,7 @@ typedef struct IconPreview {
Main *bmain;
Scene *scene;
void *owner;
- ID *id, *id_copy;
+ ID *id, *id_copy; /* May be NULL! (see ICON_TYPE_PREVIEW case in #ui_icon_ensure_deferred()) */
ListBase sizes;
} IconPreview;
@@ -336,7 +341,7 @@ static World *preview_get_localized_world(ShaderPreview *sp, World *world)
return sp->worldcopy;
}
-static ID *duplicate_ids(ID *id)
+static ID *duplicate_ids(ID *id, const bool allow_failure)
{
if (id == NULL) {
/* Non-ID preview render. */
@@ -344,20 +349,25 @@ static ID *duplicate_ids(ID *id)
}
switch (GS(id->name)) {
+ case ID_OB:
case ID_MA:
case ID_TE:
case ID_LA:
case ID_WO: {
+ BLI_assert(BKE_previewimg_id_supports_jobs(id));
ID *id_copy = BKE_id_copy_ex(
NULL, id, NULL, LIB_ID_CREATE_LOCAL | LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_NO_ANIMDATA);
return id_copy;
}
+ /* These support threading, but don't need duplicating. */
case ID_IM:
case ID_BR:
- case ID_SCR:
+ BLI_assert(BKE_previewimg_id_supports_jobs(id));
return NULL;
default:
- BLI_assert(!"ID type preview not supported.");
+ if (!allow_failure) {
+ BLI_assert(!"ID type preview not supported.");
+ }
return NULL;
}
}
@@ -698,6 +708,132 @@ void ED_preview_draw(const bContext *C, void *idp, void *parentp, void *slotp, r
}
}
+/* **************************** Object preview ****************** */
+
+struct ObjectPreviewData {
+ /* The main for the preview, not of the current file. */
+ Main *pr_main;
+ /* Copy of the object to create the preview for. The copy is for thread safety (and to insert it
+ * into an own main). */
+ Object *object;
+ int sizex;
+ int sizey;
+};
+
+static Object *object_preview_camera_create(
+ Main *preview_main, ViewLayer *view_layer, Object *preview_object, int sizex, int sizey)
+{
+ Object *camera = BKE_object_add(preview_main, view_layer, OB_CAMERA, "Preview Camera");
+
+ float rotmat[3][3];
+ float dummyscale[3];
+ mat4_to_loc_rot_size(camera->loc, rotmat, dummyscale, preview_object->obmat);
+
+ /* Camera is Y up, so needs additional 90deg rotation around X to match object's Z up. */
+ float drotmat[3][3];
+ axis_angle_to_mat3_single(drotmat, 'X', M_PI_2);
+ mul_m3_m3_post(rotmat, drotmat);
+
+ camera->rotmode = ROT_MODE_QUAT;
+ mat3_to_quat(camera->quat, rotmat);
+
+ /* shader_preview_render() does this too. */
+ if (sizex > sizey) {
+ ((Camera *)camera->data)->lens *= (float)sizey / (float)sizex;
+ }
+
+ return camera;
+}
+
+static Scene *object_preview_scene_create(const struct ObjectPreviewData *preview_data,
+ Depsgraph **r_depsgraph)
+{
+ Scene *scene = BKE_scene_add(preview_data->pr_main, "Object preview scene");
+ ViewLayer *view_layer = scene->view_layers.first;
+ Depsgraph *depsgraph = DEG_graph_new(
+ preview_data->pr_main, scene, view_layer, DAG_EVAL_VIEWPORT);
+
+ BLI_assert(preview_data->object != NULL);
+ BLI_addtail(&preview_data->pr_main->objects, preview_data->object);
+
+ BKE_collection_object_add(preview_data->pr_main, scene->master_collection, preview_data->object);
+
+ Object *camera_object = object_preview_camera_create(preview_data->pr_main,
+ view_layer,
+ preview_data->object,
+ preview_data->sizex,
+ preview_data->sizey);
+
+ scene->camera = camera_object;
+ scene->r.xsch = preview_data->sizex;
+ scene->r.ysch = preview_data->sizey;
+ scene->r.size = 100;
+
+ Base *preview_base = BKE_view_layer_base_find(view_layer, preview_data->object);
+ /* For 'view selected' below. */
+ preview_base->flag |= BASE_SELECTED;
+
+ DEG_graph_build_from_view_layer(depsgraph);
+ DEG_evaluate_on_refresh(depsgraph);
+
+ ED_view3d_camera_to_view_selected(preview_data->pr_main, depsgraph, scene, camera_object);
+
+ BKE_scene_graph_update_tagged(depsgraph, preview_data->pr_main);
+
+ *r_depsgraph = depsgraph;
+ return scene;
+}
+
+static void object_preview_render(IconPreview *preview, IconPreviewSize *preview_sized)
+{
+ Main *preview_main = BKE_main_new();
+ const float pixelsize_old = U.pixelsize;
+ char err_out[256] = "unknown";
+
+ BLI_assert(preview->id_copy && (preview->id_copy != preview->id));
+
+ struct ObjectPreviewData preview_data = {
+ .pr_main = preview_main,
+ /* Act on a copy. */
+ .object = (Object *)preview->id_copy,
+ .sizex = preview_sized->sizex,
+ .sizey = preview_sized->sizey,
+ };
+ Depsgraph *depsgraph;
+ Scene *scene = object_preview_scene_create(&preview_data, &depsgraph);
+
+ /* Ownership is now ours. */
+ preview->id_copy = NULL;
+
+ U.pixelsize = 2.0f;
+
+ ImBuf *ibuf = ED_view3d_draw_offscreen_imbuf_simple(
+ depsgraph,
+ DEG_get_evaluated_scene(depsgraph),
+ NULL,
+ OB_SOLID,
+ DEG_get_evaluated_object(depsgraph, scene->camera),
+ preview_sized->sizex,
+ preview_sized->sizey,
+ IB_rect,
+ V3D_OFSDRAW_NONE,
+ R_ALPHAPREMUL,
+ NULL,
+ NULL,
+ err_out);
+ /* TODO color-management? */
+
+ U.pixelsize = pixelsize_old;
+
+ if (ibuf) {
+ icon_copy_rect(ibuf, preview_sized->sizex, preview_sized->sizey, preview_sized->rect);
+ IMB_freeImBuf(ibuf);
+ }
+
+ DEG_graph_free(depsgraph);
+ BKE_main_free(preview_main);
+}
+
/* **************************** new shader preview system ****************** */
/* inside thread, called by renderer, sets job update value */
@@ -1101,6 +1237,8 @@ static void icon_preview_startjob(void *customdata, short *stop, short *do_updat
ID *id = sp->id;
short idtype = GS(id->name);
+ BLI_assert(id != NULL);
+
if (idtype == ID_IM) {
Image *ima = (Image *)id;
ImBuf *ibuf = NULL;
@@ -1188,27 +1326,72 @@ static void common_preview_startjob(void *customdata,
}
}
-/* exported functions */
-
-static void icon_preview_add_size(IconPreview *ip, uint *rect, int sizex, int sizey)
+/**
+ * Some ID types already have their own, more focused rendering (only objects right now). This is
+ * for the other ones, which all share #ShaderPreview and some functions.
+ */
+static void other_id_types_preview_render(IconPreview *ip,
+ IconPreviewSize *cur_size,
+ const bool is_deferred,
+ short *stop,
+ short *do_update,
+ float *progress)
{
- IconPreviewSize *cur_size = ip->sizes.first, *new_size;
+ ShaderPreview *sp = MEM_callocN(sizeof(ShaderPreview), "Icon ShaderPreview");
+ const bool is_render = !is_deferred;
+
+ /* These types don't use the ShaderPreview mess, they have their own types and functions. */
+ BLI_assert(!ip->id || !ELEM(GS(ip->id->name), ID_OB));
+
+ /* construct shader preview from image size and previewcustomdata */
+ sp->scene = ip->scene;
+ sp->owner = ip->owner;
+ sp->sizex = cur_size->sizex;
+ sp->sizey = cur_size->sizey;
+ sp->pr_method = is_render ? PR_ICON_RENDER : PR_ICON_DEFERRED;
+ sp->pr_rect = cur_size->rect;
+ sp->id = ip->id;
+ sp->id_copy = ip->id_copy;
+ sp->bmain = ip->bmain;
+ sp->own_id_copy = false;
+ Material *ma = NULL;
- while (cur_size) {
- if (cur_size->sizex == sizex && cur_size->sizey == sizey) {
- /* requested size is already in list, no need to add it again */
- return;
+ if (is_render) {
+ BLI_assert(ip->id);
+
+ /* grease pencil use its own preview file */
+ if (GS(ip->id->name) == ID_MA) {
+ ma = (Material *)ip->id;
}
- cur_size = cur_size->next;
+ if ((ma == NULL) || (ma->gp_style == NULL)) {
+ sp->pr_main = G_pr_main;
+ }
+ else {
+ sp->pr_main = G_pr_main_grease_pencil;
+ }
}
- new_size = MEM_callocN(sizeof(IconPreviewSize), "IconPreviewSize");
- new_size->sizex = sizex;
- new_size->sizey = sizey;
- new_size->rect = rect;
+ common_preview_startjob(sp, stop, do_update, progress);
+ shader_preview_free(sp);
+}
- BLI_addtail(&ip->sizes, new_size);
+/* exported functions */
+
+/**
+ * Find the index to map \a icon_size to data in \a preview_image.
+ */
+static int icon_previewimg_size_index_get(const IconPreviewSize *icon_size,
+ const PreviewImage *preview_image)
+{
+ for (int i = 0; i < NUM_ICON_SIZES; i++) {
+ if ((preview_image->w[i] == icon_size->sizex) && (preview_image->h[i] == icon_size->sizey)) {
+ return i;
+ }
+ }
+
+ BLI_assert(!"The searched icon size does not match any in the preview image");
+ return -1;
}
static void icon_preview_startjob_all_sizes(void *customdata,
@@ -1235,41 +1418,43 @@ static void icon_preview_startjob_all_sizes(void *customdata,
continue;
}
- ShaderPreview *sp = MEM_callocN(sizeof(ShaderPreview), "Icon ShaderPreview");
- const bool is_render = !(prv->tag & PRV_TAG_DEFFERED);
-
- /* construct shader preview from image size and previewcustomdata */
- sp->scene = ip->scene;
- sp->owner = ip->owner;
- sp->sizex = cur_size->sizex;
- sp->sizey = cur_size->sizey;
- sp->pr_method = is_render ? PR_ICON_RENDER : PR_ICON_DEFERRED;
- sp->pr_rect = cur_size->rect;
- sp->id = ip->id;
- sp->id_copy = ip->id_copy;
- sp->bmain = ip->bmain;
- sp->own_id_copy = false;
- Material *ma = NULL;
-
- if (is_render) {
- BLI_assert(ip->id);
-
- /* grease pencil use its own preview file */
- if (GS(ip->id->name) == ID_MA) {
- ma = (Material *)ip->id;
- }
+#ifndef NDEBUG
+ {
+ int size_index = icon_previewimg_size_index_get(cur_size, prv);
+ BLI_assert(!BKE_previewimg_is_finished(prv, size_index));
+ }
+#endif
- if ((ma == NULL) || (ma->gp_style == NULL)) {
- sp->pr_main = G_pr_main;
- }
- else {
- sp->pr_main = G_pr_main_grease_pencil;
- }
+ if (ip->id && ELEM(GS(ip->id->name), ID_OB)) {
+ /* Much simpler than the ShaderPreview mess used for other ID types. */
+ object_preview_render(ip, cur_size);
+ }
+ else {
+ other_id_types_preview_render(
+ ip, cur_size, (prv->tag & PRV_TAG_DEFFERED), stop, do_update, progress);
+ }
+ }
+}
+
+static void icon_preview_add_size(IconPreview *ip, uint *rect, int sizex, int sizey)
+{
+ IconPreviewSize *cur_size = ip->sizes.first, *new_size;
+
+ while (cur_size) {
+ if (cur_size->sizex == sizex && cur_size->sizey == sizey) {
+ /* requested size is already in list, no need to add it again */
+ return;
}
- common_preview_startjob(sp, stop, do_update, progress);
- shader_preview_free(sp);
+ cur_size = cur_size->next;
}
+
+ new_size = MEM_callocN(sizeof(IconPreviewSize), "IconPreviewSize");
+ new_size->sizex = sizex;
+ new_size->sizey = sizey;
+ new_size->rect = rect;
+
+ BLI_addtail(&ip->sizes, new_size);
}
static void icon_preview_endjob(void *customdata)
@@ -1302,9 +1487,15 @@ static void icon_preview_endjob(void *customdata)
if (ip->owner) {
PreviewImage *prv_img = ip->owner;
prv_img->tag &= ~PRV_TAG_DEFFERED_RENDERING;
+
+ LISTBASE_FOREACH (IconPreviewSize *, icon_size, &ip->sizes) {
+ int size_index = icon_previewimg_size_index_get(icon_size, prv_img);
+ BKE_previewimg_finish(prv_img, size_index);
+ }
+
if (prv_img->tag & PRV_TAG_DEFFERED_DELETE) {
BLI_assert(prv_img->tag & PRV_TAG_DEFFERED);
- BKE_previewimg_cached_release_pointer(prv_img);
+ BKE_previewimg_deferred_release(prv_img);
}
}
}
@@ -1333,7 +1524,9 @@ void ED_preview_icon_render(Main *bmain, Scene *scene, ID *id, uint *rect, int s
ip.scene = scene;
ip.owner = BKE_previewimg_id_ensure(id);
ip.id = id;
- ip.id_copy = duplicate_ids(id);
+ /* Control isn't given back to the caller until the preview is done. So we don't need to copy
+ * the ID to avoid thread races. */
+ ip.id_copy = duplicate_ids(id, true);
icon_preview_add_size(&ip, rect, sizex, sizey);
@@ -1376,7 +1569,7 @@ void ED_preview_icon_job(
ip->scene = CTX_data_scene(C);
ip->owner = owner;
ip->id = id;
- ip->id_copy = duplicate_ids(id);
+ ip->id_copy = duplicate_ids(id, false);
icon_preview_add_size(ip, rect, sizex, sizey);
@@ -1416,6 +1609,8 @@ void ED_preview_shader_job(const bContext *C,
Scene *scene = CTX_data_scene(C);
short id_type = GS(id->name);
+ BLI_assert(BKE_previewimg_id_supports_jobs(id));
+
/* Use workspace render only for buttons Window,
* since the other previews are related to the datablock. */
@@ -1445,7 +1640,7 @@ void ED_preview_shader_job(const bContext *C,
sp->sizey = sizey;
sp->pr_method = method;
sp->id = id;
- sp->id_copy = duplicate_ids(id);
+ sp->id_copy = duplicate_ids(id, false);
sp->own_id_copy = true;
sp->parent = parent;
sp->slot = slot;
diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c
index 244ebea5bbe..fb7606d1fe5 100644
--- a/source/blender/editors/screen/screen_context.c
+++ b/source/blender/editors/screen/screen_context.c
@@ -649,8 +649,6 @@ static eContextResult screen_ctx_selected_editable_sequences(const bContext *C,
}
static eContextResult screen_ctx_selected_nla_strips(const bContext *C, bContextDataResult *result)
{
- wmWindow *win = CTX_wm_window(C);
- Scene *scene = WM_window_get_active_scene(win);
bAnimContext ac;
if (ANIM_animdata_get_context(C, &ac) != 0) {
ListBase anim_data = {NULL, NULL};
@@ -663,7 +661,7 @@ static eContextResult screen_ctx_selected_nla_strips(const bContext *C, bContext
NlaTrack *nlt = (NlaTrack *)ale->data;
LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) {
if (strip->flag & NLASTRIP_FLAG_SELECT) {
- CTX_data_list_add(result, &scene->id, &RNA_NlaStrip, strip);
+ CTX_data_list_add(result, ale->id, &RNA_NlaStrip, strip);
}
}
}
diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c
index 6be2fb8004b..be52874ed0b 100644
--- a/source/blender/editors/screen/screen_edit.c
+++ b/source/blender/editors/screen/screen_edit.c
@@ -1133,12 +1133,11 @@ void ED_screen_scene_change(bContext *C, wmWindow *win, Scene *scene)
ScrArea *ED_screen_full_newspace(bContext *C, ScrArea *area, int type)
{
- wmWindow *win = CTX_wm_window(C);
ScrArea *newsa = NULL;
SpaceLink *newsl;
if (!area || area->full == NULL) {
- newsa = ED_screen_state_toggle(C, win, area, SCREENMAXIMIZED);
+ newsa = ED_screen_state_maximized_create(C);
}
if (!newsa) {
@@ -1149,11 +1148,11 @@ ScrArea *ED_screen_full_newspace(bContext *C, ScrArea *area, int type)
newsl = newsa->spacedata.first;
/* Tag the active space before changing, so we can identify it when user wants to go back. */
- if ((newsl->link_flag & SPACE_FLAG_TYPE_TEMPORARY) == 0) {
+ if (newsl && (newsl->link_flag & SPACE_FLAG_TYPE_TEMPORARY) == 0) {
newsl->link_flag |= SPACE_FLAG_TYPE_WAS_ACTIVE;
}
- ED_area_newspace(C, newsa, type, newsl->link_flag & SPACE_FLAG_TYPE_TEMPORARY);
+ ED_area_newspace(C, newsa, type, (newsl && newsl->link_flag & SPACE_FLAG_TYPE_TEMPORARY));
return newsa;
}
@@ -1217,13 +1216,108 @@ void ED_screen_full_restore(bContext *C, ScrArea *area)
}
/**
- * this function toggles: if area is maximized/full then the parent will be restored
+ * \param toggle_area: If this is set, its space data will be swapped with the one of the new emtpy
+ * area, when toggling back it can be swapped back again.
+ * \return The newly created screen with the non-normal area.
+ */
+static bScreen *screen_state_to_nonnormal(bContext *C,
+ wmWindow *win,
+ ScrArea *toggle_area,
+ int state)
+{
+ Main *bmain = CTX_data_main(C);
+ WorkSpace *workspace = WM_window_get_active_workspace(win);
+
+ /* change from SCREENNORMAL to new state */
+ WorkSpaceLayout *layout_new;
+ ScrArea *newa;
+ char newname[MAX_ID_NAME - 2];
+
+ BLI_assert(ELEM(state, SCREENMAXIMIZED, SCREENFULL));
+
+ bScreen *oldscreen = WM_window_get_active_screen(win);
+
+ oldscreen->state = state;
+ BLI_snprintf(newname, sizeof(newname), "%s-%s", oldscreen->id.name + 2, "nonnormal");
+
+ layout_new = ED_workspace_layout_add(bmain, workspace, win, newname);
+
+ bScreen *screen = BKE_workspace_layout_screen_get(layout_new);
+ screen->state = state;
+ screen->redraws_flag = oldscreen->redraws_flag;
+ screen->temp = oldscreen->temp;
+ screen->flag = oldscreen->flag;
+
+ /* timer */
+ screen->animtimer = oldscreen->animtimer;
+ oldscreen->animtimer = NULL;
+
+ newa = (ScrArea *)screen->areabase.first;
+
+ /* swap area */
+ if (toggle_area) {
+ ED_area_data_swap(newa, toggle_area);
+ newa->flag = toggle_area->flag; /* mostly for AREA_FLAG_WASFULLSCREEN */
+ }
+
+ if (state == SCREENFULL) {
+ /* temporarily hide global areas */
+ LISTBASE_FOREACH (ScrArea *, glob_area, &win->global_areas.areabase) {
+ glob_area->global->flag |= GLOBAL_AREA_IS_HIDDEN;
+ }
+ /* temporarily hide the side panels/header */
+ LISTBASE_FOREACH (ARegion *, region, &newa->regionbase) {
+ region->flagfullscreen = region->flag;
+
+ if (ELEM(region->regiontype,
+ RGN_TYPE_UI,
+ RGN_TYPE_HEADER,
+ RGN_TYPE_TOOL_HEADER,
+ RGN_TYPE_FOOTER,
+ RGN_TYPE_TOOLS,
+ RGN_TYPE_NAV_BAR,
+ RGN_TYPE_EXECUTE)) {
+ region->flag |= RGN_FLAG_HIDDEN;
+ }
+ }
+ }
+
+ if (toggle_area) {
+ toggle_area->full = oldscreen;
+ }
+ newa->full = oldscreen;
+
+ ED_screen_change(C, screen);
+ ED_area_tag_refresh(newa);
+
+ return screen;
+}
+
+/**
+ * Create a new temporary screen with a maximized, empty area.
+ * This can be closed with #ED_screen_state_toggle().
+ *
+ * Use this to just create a new maximized screen/area, rather than maximizing an existing one.
+ * Otherwise, maximize with #ED_screen_state_toggle().
+ */
+ScrArea *ED_screen_state_maximized_create(bContext *C)
+{
+ bScreen *screen = screen_state_to_nonnormal(C, CTX_wm_window(C), NULL, SCREENMAXIMIZED);
+ return screen->areabase.first;
+}
+
+/**
+ * This function toggles: if area is maximized/full then the parent will be restored.
+ *
+ * Use #ED_screen_state_maximized_create() if you do not want the toggle behavior when changing to
+ * a maximized area. I.e. if you just want to open a new maximized screen/area, not maximize a
+ * specific area. In the former case, space data of the maximized and non-maximized area should be
+ * independent, in the latter it should be the same.
*
* \warning \a area may be freed.
*/
ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const short state)
{
- Main *bmain = CTX_data_main(C);
wmWindowManager *wm = CTX_wm_manager(C);
WorkSpace *workspace = WM_window_get_active_workspace(win);
@@ -1257,7 +1351,7 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const
screen->state = SCREENNORMAL;
screen->flag = oldscreen->flag;
- /* find old area to restore from */
+ /* Find old area we may have swapped dummy space data to. It's swapped back here. */
ScrArea *fullsa = NULL;
LISTBASE_FOREACH (ScrArea *, old, &screen->areabase) {
/* area to restore from is always first */
@@ -1271,13 +1365,6 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const
area->full = NULL;
- if (fullsa == NULL) {
- if (G.debug & G_DEBUG) {
- printf("%s: something wrong in areafullscreen\n", __func__);
- }
- return NULL;
- }
-
if (state == SCREENFULL) {
/* unhide global areas */
LISTBASE_FOREACH (ScrArea *, glob_area, &win->global_areas.areabase) {
@@ -1289,14 +1376,16 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const
}
}
- ED_area_data_swap(fullsa, area);
+ if (fullsa) {
+ ED_area_data_swap(fullsa, area);
+ ED_area_tag_refresh(fullsa);
+ }
/* animtimer back */
screen->animtimer = oldscreen->animtimer;
oldscreen->animtimer = NULL;
ED_screen_change(C, screen);
- ED_area_tag_refresh(fullsa);
BKE_workspace_layout_remove(CTX_data_main(C), workspace, layout_old);
@@ -1307,68 +1396,16 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const
screen->skip_handling = true;
}
else {
- /* change from SCREENNORMAL to new state */
- WorkSpaceLayout *layout_new;
- ScrArea *newa;
- char newname[MAX_ID_NAME - 2];
-
- BLI_assert(ELEM(state, SCREENMAXIMIZED, SCREENFULL));
-
- bScreen *oldscreen = WM_window_get_active_screen(win);
-
- oldscreen->state = state;
- BLI_snprintf(newname, sizeof(newname), "%s-%s", oldscreen->id.name + 2, "nonnormal");
-
- layout_new = ED_workspace_layout_add(bmain, workspace, win, newname);
-
- screen = BKE_workspace_layout_screen_get(layout_new);
- screen->state = state;
- screen->redraws_flag = oldscreen->redraws_flag;
- screen->temp = oldscreen->temp;
- screen->flag = oldscreen->flag;
-
- /* timer */
- screen->animtimer = oldscreen->animtimer;
- oldscreen->animtimer = NULL;
+ ScrArea *toggle_area = area;
/* use random area when we have no active one, e.g. when the
* mouse is outside of the window and we open a file browser */
- if (!area || area->global) {
- area = oldscreen->areabase.first;
+ if (!toggle_area || toggle_area->global) {
+ bScreen *oldscreen = WM_window_get_active_screen(win);
+ toggle_area = oldscreen->areabase.first;
}
- newa = (ScrArea *)screen->areabase.first;
-
- /* copy area */
- ED_area_data_swap(newa, area);
- newa->flag = area->flag; /* mostly for AREA_FLAG_WASFULLSCREEN */
-
- if (state == SCREENFULL) {
- /* temporarily hide global areas */
- LISTBASE_FOREACH (ScrArea *, glob_area, &win->global_areas.areabase) {
- glob_area->global->flag |= GLOBAL_AREA_IS_HIDDEN;
- }
- /* temporarily hide the side panels/header */
- LISTBASE_FOREACH (ARegion *, region, &newa->regionbase) {
- region->flagfullscreen = region->flag;
-
- if (ELEM(region->regiontype,
- RGN_TYPE_UI,
- RGN_TYPE_HEADER,
- RGN_TYPE_TOOL_HEADER,
- RGN_TYPE_FOOTER,
- RGN_TYPE_TOOLS,
- RGN_TYPE_NAV_BAR,
- RGN_TYPE_EXECUTE)) {
- region->flag |= RGN_FLAG_HIDDEN;
- }
- }
- }
-
- area->full = oldscreen;
- newa->full = oldscreen;
-
- ED_screen_change(C, screen);
+ screen = screen_state_to_nonnormal(C, win, toggle_area, state);
}
/* XXX bad code: setscreen() ends with first area active. fullscreen render assumes this too */
@@ -1412,9 +1449,6 @@ ScrArea *ED_screen_temp_space_open(bContext *C,
area->flag |= AREA_FLAG_STACKED_FULLSCREEN;
((SpaceLink *)area->spacedata.first)->link_flag |= SPACE_FLAG_TYPE_TEMPORARY;
}
- else if (ctx_area != NULL && ctx_area->spacetype == space_type) {
- area = ED_screen_state_toggle(C, CTX_wm_window(C), ctx_area, SCREENMAXIMIZED);
- }
else {
area = ED_screen_full_newspace(C, ctx_area, (int)space_type);
((SpaceLink *)area->spacedata.first)->link_flag |= SPACE_FLAG_TYPE_TEMPORARY;
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index 72b3b344813..51687d5de1d 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -3918,7 +3918,7 @@ static void SCREEN_OT_region_quadview(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Toggle Quad View";
- ot->description = "Split selected area into camera, front, right & top views";
+ ot->description = "Split selected area into camera, front, right, and top views";
ot->idname = "SCREEN_OT_region_quadview";
/* api callbacks */
@@ -5510,6 +5510,8 @@ void ED_operatortypes_screen(void)
WM_operatortype_append(ED_OT_undo_history);
WM_operatortype_append(ED_OT_flush_edits);
+ WM_operatortype_append(ED_OT_lib_id_load_custom_preview);
+ WM_operatortype_append(ED_OT_lib_id_generate_preview);
}
/** \} */
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt
index 9bf3d2610d8..fff8d27ef5b 100644
--- a/source/blender/editors/sculpt_paint/CMakeLists.txt
+++ b/source/blender/editors/sculpt_paint/CMakeLists.txt
@@ -32,6 +32,7 @@ set(INC
../../windowmanager
../../../../intern/atomic
../../../../intern/clog
+ ../../../../intern/eigen
../../../../intern/glew-mx
../../../../intern/guardedalloc
)
diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c
index 3a6b91443a0..324fd5d3075 100644
--- a/source/blender/editors/sculpt_paint/paint_image.c
+++ b/source/blender/editors/sculpt_paint/paint_image.c
@@ -40,6 +40,7 @@
#include "IMB_imbuf_types.h"
#include "DNA_brush_types.h"
+#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
@@ -934,7 +935,7 @@ void PAINT_OT_grab_clone(wmOperatorType *ot)
-FLT_MAX,
FLT_MAX,
"Delta",
- "Delta offset of clone image in 0.0..1.0 coordinates",
+ "Delta offset of clone image in 0.0 to 1.0 coordinates",
-1.0f,
1.0f);
}
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index 98f4b4013cb..cca4ffd4d78 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -6781,7 +6781,7 @@ static bool add_simple_uvs_poll(bContext *C)
void PAINT_OT_add_simple_uvs(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Add simple UVs";
+ ot->name = "Add Simple UVs";
ot->description = "Add cube map uvs on mesh";
ot->idname = "PAINT_OT_add_simple_uvs";
diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h
index 175d98ba9aa..3ca0d853d6a 100644
--- a/source/blender/editors/sculpt_paint/paint_intern.h
+++ b/source/blender/editors/sculpt_paint/paint_intern.h
@@ -43,7 +43,6 @@ struct wmEvent;
struct wmKeyConfig;
struct wmOperator;
struct wmOperatorType;
-struct wmWindowManager;
enum ePaintMode;
enum ePaintSymmetryFlags;
diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c
index 17690757fa5..92c78a674f0 100644
--- a/source/blender/editors/sculpt_paint/paint_mask.c
+++ b/source/blender/editors/sculpt_paint/paint_mask.c
@@ -1552,6 +1552,11 @@ static int sculpt_trim_gesture_box_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+ if (ss->totvert == 0) {
+ /* No geometry to trim or to detect a valid position for the trimming shape. */
+ return OPERATOR_CANCELLED;
+ }
+
SculptGestureContext *sgcontext = sculpt_gesture_init_from_box(C, op);
if (!sgcontext) {
return OPERATOR_CANCELLED;
@@ -1589,6 +1594,11 @@ static int sculpt_trim_gesture_lasso_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+ if (ss->totvert == 0) {
+ /* No geometry to trim or to detect a valid position for the trimming shape. */
+ return OPERATOR_CANCELLED;
+ }
+
SculptGestureContext *sgcontext = sculpt_gesture_init_from_lasso(C, op);
if (!sgcontext) {
return OPERATOR_CANCELLED;
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index fd7ec1da497..38d2bed7d97 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -1227,6 +1227,7 @@ static bool sculpt_tool_is_proxy_used(const char sculpt_tool)
SCULPT_TOOL_SMOOTH,
SCULPT_TOOL_LAYER,
SCULPT_TOOL_POSE,
+ SCULPT_TOOL_DISPLACEMENT_SMEAR,
SCULPT_TOOL_BOUNDARY,
SCULPT_TOOL_CLOTH,
SCULPT_TOOL_PAINT,
@@ -2362,6 +2363,7 @@ static float brush_strength(const Sculpt *sd,
final_pressure = pressure * pressure;
return final_pressure * overlap * feather;
case SCULPT_TOOL_SMEAR:
+ case SCULPT_TOOL_DISPLACEMENT_SMEAR:
return alpha * pressure * overlap * feather;
case SCULPT_TOOL_CLAY_STRIPS:
/* Clay Strips needs less strength to compensate the curve. */
@@ -3103,6 +3105,147 @@ static void do_displacement_eraser_brush(Sculpt *sd, Object *ob, PBVHNode **node
/** \} */
+/** \name Sculpt Multires Displacement Smear Brush
+ * \{ */
+
+static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ const Brush *brush = data->brush;
+ const float bstrength = clamp_f(ss->cache->bstrength, 0.0f, 1.0f);
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, data->brush->falloff_shape);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ PBVHVertexIter vd;
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+ {
+ if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
+ continue;
+ }
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ thread_id);
+
+ float current_disp[3];
+ float current_disp_norm[3];
+ float interp_limit_surface_disp[3];
+
+ copy_v3_v3(interp_limit_surface_disp, ss->cache->prev_displacement[vd.index]);
+
+ switch (brush->smear_deform_type) {
+ case BRUSH_SMEAR_DEFORM_DRAG:
+ sub_v3_v3v3(current_disp, ss->cache->location, ss->cache->last_location);
+ break;
+ case BRUSH_SMEAR_DEFORM_PINCH:
+ sub_v3_v3v3(current_disp, ss->cache->location, vd.co);
+ break;
+ case BRUSH_SMEAR_DEFORM_EXPAND:
+ sub_v3_v3v3(current_disp, vd.co, ss->cache->location);
+ break;
+ }
+
+ normalize_v3_v3(current_disp_norm, current_disp);
+ mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength);
+
+ float weights_accum = 1.0f;
+
+ SculptVertexNeighborIter ni;
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ float vertex_disp[3];
+ float vertex_disp_norm[3];
+ float neighbor_limit_co[3];
+ SCULPT_vertex_limit_surface_get(ss, ni.index, neighbor_limit_co);
+ sub_v3_v3v3(vertex_disp,
+ ss->cache->limit_surface_co[ni.index],
+ ss->cache->limit_surface_co[vd.index]);
+ const float *neighbor_limit_surface_disp = ss->cache->prev_displacement[ni.index];
+ normalize_v3_v3(vertex_disp_norm, vertex_disp);
+ if (dot_v3v3(current_disp_norm, vertex_disp_norm) < 0.0f) {
+ const float disp_interp = clamp_f(
+ -dot_v3v3(current_disp_norm, vertex_disp_norm), 0.0f, 1.0f);
+ madd_v3_v3fl(interp_limit_surface_disp, neighbor_limit_surface_disp, disp_interp);
+ weights_accum += disp_interp;
+ }
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
+ mul_v3_fl(interp_limit_surface_disp, 1.0f / weights_accum);
+
+ float new_co[3];
+ add_v3_v3v3(new_co, ss->cache->limit_surface_co[vd.index], interp_limit_surface_disp);
+ interp_v3_v3v3(vd.co, vd.co, new_co, fade);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+static void do_displacement_smear_store_prev_disp_task_cb_ex(
+ void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+
+ PBVHVertexIter vd;
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+ {
+ sub_v3_v3v3(ss->cache->prev_displacement[vd.index],
+ SCULPT_vertex_co_get(ss, vd.index),
+ ss->cache->limit_surface_co[vd.index]);
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+static void do_displacement_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+{
+ Brush *brush = BKE_paint_brush(&sd->paint);
+ SculptSession *ss = ob->sculpt;
+
+ BKE_curvemapping_init(brush->curve);
+
+ const int totvert = SCULPT_vertex_count_get(ss);
+ if (!ss->cache->prev_displacement) {
+ ss->cache->prev_displacement = MEM_malloc_arrayN(
+ totvert, sizeof(float[3]), "prev displacement");
+ ss->cache->limit_surface_co = MEM_malloc_arrayN(totvert, sizeof(float[3]), "limit surface co");
+ for (int i = 0; i < totvert; i++) {
+ SCULPT_vertex_limit_surface_get(ss, i, ss->cache->limit_surface_co[i]);
+ sub_v3_v3v3(ss->cache->prev_displacement[i],
+ SCULPT_vertex_co_get(ss, i),
+ ss->cache->limit_surface_co[i]);
+ }
+ }
+ /* Threaded loop over nodes. */
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(
+ 0, totnode, &data, do_displacement_smear_store_prev_disp_task_cb_ex, &settings);
+ BLI_task_parallel_range(0, totnode, &data, do_displacement_smear_brush_task_cb_ex, &settings);
+}
+
+/** \} */
+
static void do_draw_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict tls)
@@ -5742,32 +5885,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
}
else if (brush->sculpt_tool == SCULPT_TOOL_CLOTH) {
- if (brush->cloth_simulation_area_type == BRUSH_CLOTH_SIMULATION_AREA_LOCAL) {
- SculptSearchSphereData data = {
- .ss = ss,
- .sd = sd,
- .radius_squared = square_f(ss->cache->initial_radius * (1.0 + brush->cloth_sim_limit)),
- .original = false,
- .ignore_fully_ineffective = false,
- .center = ss->cache->initial_location,
- };
- BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode);
- }
- if (brush->cloth_simulation_area_type == BRUSH_CLOTH_SIMULATION_AREA_DYNAMIC) {
- SculptSearchSphereData data = {
- .ss = ss,
- .sd = sd,
- .radius_squared = square_f(ss->cache->radius * (1.0 + brush->cloth_sim_limit)),
- .original = false,
- .ignore_fully_ineffective = false,
- .center = ss->cache->location,
- };
- BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode);
- }
- else {
- /* Gobal simulation, get all nodes. */
- BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
- }
+ nodes = SCULPT_cloth_brush_affected_nodes_gather(ss, brush, &totnode);
}
else {
const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true :
@@ -5805,6 +5923,17 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
}
}
+ /* Initialize automasking cache. For anchored brushes with spherical falloff, we start off with
+ * zero radius, thus we have no pbvh nodes on the first brush step. */
+ if (totnode ||
+ ((brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) && (brush->flag & BRUSH_ANCHORED))) {
+ if (SCULPT_stroke_is_first_brush_step(ss->cache)) {
+ if (SCULPT_is_automasking_enabled(sd, ss, brush)) {
+ ss->cache->automasking = SCULPT_automasking_cache_init(sd, brush, ob);
+ }
+ }
+ }
+
/* Only act if some verts are inside the brush area. */
if (totnode) {
float location[3];
@@ -5828,12 +5957,6 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
update_brush_local_mat(sd, ob);
}
- if (SCULPT_stroke_is_first_brush_step(ss->cache)) {
- if (SCULPT_is_automasking_enabled(sd, ss, brush)) {
- ss->cache->automasking = SCULPT_automasking_cache_init(sd, brush, ob);
- }
- }
-
if (brush->sculpt_tool == SCULPT_TOOL_POSE && SCULPT_stroke_is_first_brush_step(ss->cache)) {
SCULPT_pose_brush_init(sd, ob, ss, brush);
}
@@ -5952,6 +6075,9 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
case SCULPT_TOOL_DISPLACEMENT_ERASER:
do_displacement_eraser_brush(sd, ob, nodes, totnode);
break;
+ case SCULPT_TOOL_DISPLACEMENT_SMEAR:
+ do_displacement_smear_brush(sd, ob, nodes, totnode);
+ break;
case SCULPT_TOOL_PAINT:
SCULPT_do_paint_brush(sd, ob, nodes, totnode);
break;
@@ -6515,6 +6641,8 @@ static const char *sculpt_tool_name(Sculpt *sd)
return "Draw Face Sets";
case SCULPT_TOOL_DISPLACEMENT_ERASER:
return "Multires Displacement Eraser";
+ case SCULPT_TOOL_DISPLACEMENT_SMEAR:
+ return "Multires Displacement Smear";
case SCULPT_TOOL_PAINT:
return "Paint Brush";
case SCULPT_TOOL_SMEAR:
@@ -6535,6 +6663,8 @@ void SCULPT_cache_free(StrokeCache *cache)
MEM_SAFE_FREE(cache->layer_displacement_factor);
MEM_SAFE_FREE(cache->prev_colors);
MEM_SAFE_FREE(cache->detail_directions);
+ MEM_SAFE_FREE(cache->prev_displacement);
+ MEM_SAFE_FREE(cache->limit_surface_co);
if (cache->pose_ik_chain) {
SCULPT_pose_ik_chain_free(cache->pose_ik_chain);
@@ -6706,6 +6836,7 @@ static void sculpt_update_cache_invariants(
SCULPT_TOOL_MASK,
SCULPT_TOOL_SMOOTH,
SCULPT_TOOL_SIMPLIFY,
+ SCULPT_TOOL_DISPLACEMENT_SMEAR,
SCULPT_TOOL_DISPLACEMENT_ERASER) &&
(sd->gravity_factor > 0.0f));
/* Get gravity vector in world space. */
@@ -8685,7 +8816,7 @@ static int sculpt_sample_color_invoke(bContext *C,
static void SCULPT_OT_sample_color(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Sample color";
+ ot->name = "Sample Color";
ot->idname = "SCULPT_OT_sample_color";
ot->description = "Sample the vertex color of the active vertex";
diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c
index f8165890cc4..0ac0d796ca4 100644
--- a/source/blender/editors/sculpt_paint/sculpt_cloth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c
@@ -119,6 +119,43 @@ static void cloth_brush_simulation_location_get(SculptSession *ss,
copy_v3_v3(r_location, ss->cache->location);
}
+PBVHNode **SCULPT_cloth_brush_affected_nodes_gather(SculptSession *ss,
+ Brush *brush,
+ int *r_totnode)
+{
+ BLI_assert(ss->cache);
+ BLI_assert(brush->sculpt_tool == SCULPT_TOOL_CLOTH);
+ PBVHNode **nodes = NULL;
+
+ switch (brush->cloth_simulation_area_type) {
+ case BRUSH_CLOTH_SIMULATION_AREA_LOCAL: {
+ SculptSearchSphereData data = {
+ .ss = ss,
+ .radius_squared = square_f(ss->cache->initial_radius * (1.0 + brush->cloth_sim_limit)),
+ .original = false,
+ .ignore_fully_ineffective = false,
+ .center = ss->cache->initial_location,
+ };
+ BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, r_totnode);
+ } break;
+ case BRUSH_CLOTH_SIMULATION_AREA_GLOBAL:
+ BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, r_totnode);
+ break;
+ case BRUSH_CLOTH_SIMULATION_AREA_DYNAMIC: {
+ SculptSearchSphereData data = {
+ .ss = ss,
+ .radius_squared = square_f(ss->cache->radius * (1.0 + brush->cloth_sim_limit)),
+ .original = false,
+ .ignore_fully_ineffective = false,
+ .center = ss->cache->location,
+ };
+ BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, r_totnode);
+ } break;
+ }
+
+ return nodes;
+}
+
static float cloth_brush_simulation_falloff_get(const Brush *brush,
const float radius,
const float location[3],
diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.c b/source/blender/editors/sculpt_paint/sculpt_detail.c
index ddf5b39f080..aa1d407dc24 100644
--- a/source/blender/editors/sculpt_paint/sculpt_detail.c
+++ b/source/blender/editors/sculpt_paint/sculpt_detail.c
@@ -363,7 +363,7 @@ void SCULPT_OT_sample_detail_size(wmOperatorType *ot)
0,
SHRT_MAX,
"Location",
- "Screen Coordinates of sampling",
+ "Screen coordinates of sampling",
0,
SHRT_MAX);
RNA_def_enum(ot->srna,
diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c
index ad42750bb92..1fba958d695 100644
--- a/source/blender/editors/sculpt_paint/sculpt_face_set.c
+++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c
@@ -41,6 +41,7 @@
#include "BKE_context.h"
#include "BKE_customdata.h"
#include "BKE_mesh.h"
+#include "BKE_mesh_fair.h"
#include "BKE_mesh_mapping.h"
#include "BKE_multires.h"
#include "BKE_node.h"
@@ -1024,6 +1025,8 @@ typedef enum eSculptFaceSetEditMode {
SCULPT_FACE_SET_EDIT_GROW = 0,
SCULPT_FACE_SET_EDIT_SHRINK = 1,
SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY = 2,
+ SCULPT_FACE_SET_EDIT_FAIR_POSITIONS = 3,
+ SCULPT_FACE_SET_EDIT_FAIR_TANGENCY = 4,
} eSculptFaceSetEditMode;
static EnumPropertyItem prop_sculpt_face_sets_edit_types[] = {
@@ -1048,6 +1051,22 @@ static EnumPropertyItem prop_sculpt_face_sets_edit_types[] = {
"Delete Geometry",
"Deletes the faces that are assigned to the Face Set",
},
+ {
+ SCULPT_FACE_SET_EDIT_FAIR_POSITIONS,
+ "FAIR_POSITIONS",
+ 0,
+ "Fair Positions",
+ "Creates a smooth as possible geometry patch from the Face Set minimizing changes in "
+ "vertex positions",
+ },
+ {
+ SCULPT_FACE_SET_EDIT_FAIR_TANGENCY,
+ "FAIR_TANGENCY",
+ 0,
+ "Fair Tangency",
+ "Creates a smooth as possible geometry patch from the Face Set minimizing changes in "
+ "vertex tangents",
+ },
{0, NULL, 0, NULL, NULL},
};
@@ -1181,6 +1200,29 @@ static void sculpt_face_set_delete_geometry(Object *ob,
BM_mesh_free(bm);
}
+static void sculpt_face_set_edit_fair_face_set(Object *ob,
+ const int active_face_set_id,
+ const int fair_order)
+{
+ SculptSession *ss = ob->sculpt;
+ const int totvert = SCULPT_vertex_count_get(ss);
+
+ Mesh *mesh = ob->data;
+ bool *fair_vertices = MEM_malloc_arrayN(sizeof(bool), totvert, "fair vertices");
+
+ SCULPT_boundary_info_ensure(ob);
+
+ for (int i = 0; i < totvert; i++) {
+ fair_vertices[i] = !SCULPT_vertex_is_boundary(ss, i) &&
+ SCULPT_vertex_has_face_set(ss, i, active_face_set_id) &&
+ SCULPT_vertex_has_unique_face_set(ss, i);
+ }
+
+ MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss);
+ BKE_mesh_prefair_and_fair_vertices(mesh, mvert, fair_vertices, fair_order);
+ MEM_freeN(fair_vertices);
+}
+
static void sculpt_face_set_apply_edit(Object *ob,
const int active_face_set_id,
const int mode,
@@ -1204,6 +1246,12 @@ static void sculpt_face_set_apply_edit(Object *ob,
case SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY:
sculpt_face_set_delete_geometry(ob, ss, active_face_set_id, modify_hidden);
break;
+ case SCULPT_FACE_SET_EDIT_FAIR_POSITIONS:
+ sculpt_face_set_edit_fair_face_set(ob, active_face_set_id, MESH_FAIRING_DEPTH_POSITION);
+ break;
+ case SCULPT_FACE_SET_EDIT_FAIR_TANGENCY:
+ sculpt_face_set_edit_fair_face_set(ob, active_face_set_id, MESH_FAIRING_DEPTH_TANGENCY);
+ break;
}
}
@@ -1230,6 +1278,16 @@ static bool sculpt_face_set_edit_is_operation_valid(SculptSession *ss,
return false;
}
}
+
+ if (ELEM(mode, SCULPT_FACE_SET_EDIT_FAIR_POSITIONS, SCULPT_FACE_SET_EDIT_FAIR_TANGENCY)) {
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
+ /* TODO: Multires topology representation using grids and duplicates can't be used directly
+ * by the fair algorithm. Multires topology needs to be exposed in a different way or
+ * converted to a mesh for this operation. */
+ return false;
+ }
+ }
+
return true;
}
@@ -1287,11 +1345,38 @@ static void sculpt_face_set_edit_modify_face_sets(Object *ob,
MEM_freeN(nodes);
}
+static void sculpt_face_set_edit_modify_coordinates(bContext *C,
+ Object *ob,
+ const int active_face_set,
+ const eSculptFaceSetEditMode mode)
+{
+ Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+ SculptSession *ss = ob->sculpt;
+ PBVH *pbvh = ss->pbvh;
+ PBVHNode **nodes;
+ int totnode;
+ BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
+ SCULPT_undo_push_begin(ob, "face set edit");
+ for (int i = 0; i < totnode; i++) {
+ BKE_pbvh_node_mark_update(nodes[i]);
+ SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_COORDS);
+ }
+ sculpt_face_set_apply_edit(ob, abs(active_face_set), mode, false);
+
+ if (ss->deform_modifiers_active || ss->shapekey_active) {
+ SCULPT_flush_stroke_deform(sd, ob, true);
+ }
+ SCULPT_flush_update_step(C, SCULPT_UPDATE_COORDS);
+ SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COORDS);
+ SCULPT_undo_push_end();
+ MEM_freeN(nodes);
+}
+
static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
Object *ob = CTX_data_active_object(C);
SculptSession *ss = ob->sculpt;
- Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
const int mode = RNA_enum_get(op->ptr, "mode");
const bool modify_hidden = RNA_boolean_get(op->ptr, "modify_hidden");
@@ -1320,6 +1405,10 @@ static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEven
case SCULPT_FACE_SET_EDIT_SHRINK:
sculpt_face_set_edit_modify_face_sets(ob, active_face_set, mode, modify_hidden);
break;
+ case SCULPT_FACE_SET_EDIT_FAIR_POSITIONS:
+ case SCULPT_FACE_SET_EDIT_FAIR_TANGENCY:
+ sculpt_face_set_edit_modify_coordinates(C, ob, active_face_set, mode);
+ break;
}
SCULPT_tag_update_overlays(C);
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
index f3c07a86201..76a6b05cdff 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
@@ -328,9 +328,9 @@ void SCULPT_OT_color_filter(struct wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* rna */
- RNA_def_enum(ot->srna, "type", prop_color_filter_types, COLOR_FILTER_HUE, "Filter type", "");
+ RNA_def_enum(ot->srna, "type", prop_color_filter_types, COLOR_FILTER_HUE, "Filter Type", "");
RNA_def_float(
- ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter Strength", -10.0f, 10.0f);
+ ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter strength", -10.0f, 10.0f);
PropertyRNA *prop = RNA_def_float_color(
ot->srna, "fill_color", 3, NULL, 0.0f, FLT_MAX, "Fill Color", "", 0.0f, 1.0f);
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c
index ddad6bef7fd..0297ed73dd4 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c
@@ -80,12 +80,12 @@ static EnumPropertyItem prop_mask_filter_types[] = {
{MASK_FILTER_CONTRAST_INCREASE,
"CONTRAST_INCREASE",
0,
- "Increase contrast",
+ "Increase Contrast",
"Increase the contrast of the paint mask"},
{MASK_FILTER_CONTRAST_DECREASE,
"CONTRAST_DECREASE",
0,
- "Decrease contrast",
+ "Decrease Contrast",
"Decrease the contrast of the paint mask"},
{0, NULL, 0, NULL, NULL},
};
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
index 02d4be20e1b..e11894a8c01 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
@@ -771,15 +771,15 @@ void SCULPT_OT_mesh_filter(struct wmOperatorType *ot)
"type",
prop_mesh_filter_types,
MESH_FILTER_INFLATE,
- "Filter type",
+ "Filter Type",
"Operation that is going to be applied to the mesh");
RNA_def_float(
- ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter Strength", -10.0f, 10.0f);
+ ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter strength", -10.0f, 10.0f);
RNA_def_enum_flag(ot->srna,
"deform_axis",
prop_mesh_filter_deform_axis_items,
MESH_FILTER_DEFORM_X | MESH_FILTER_DEFORM_Y | MESH_FILTER_DEFORM_Z,
- "Deform axis",
+ "Deform Axis",
"Apply the deformation in the selected axis");
RNA_def_enum(ot->srna,
"orientation",
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index 3b48207f461..d1e17c7e59b 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -39,7 +39,6 @@
struct AutomaskingCache;
struct KeyBlock;
struct Object;
-struct SculptPoseIKChainSegment;
struct SculptUndoNode;
struct bContext;
@@ -423,6 +422,10 @@ void SCULPT_cloth_plane_falloff_preview_draw(const uint gpuattr,
const float outline_col[3],
float outline_alpha);
+PBVHNode **SCULPT_cloth_brush_affected_nodes_gather(SculptSession *ss,
+ Brush *brush,
+ int *r_totnode);
+
BLI_INLINE bool SCULPT_is_cloth_deform_brush(const Brush *brush)
{
return (brush->sculpt_tool == SCULPT_TOOL_CLOTH && ELEM(brush->cloth_deform_type,
@@ -452,7 +455,7 @@ BLI_INLINE bool SCULPT_tool_needs_all_pbvh_nodes(const Brush *brush)
if (brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) {
/* Boundary needs all nodes because it is not possible to know where the boundary
* deformation is going to be propagated before calculating it. */
- /* TODO: after calculating the boudnary info in the first iteration, it should be
+ /* TODO: after calculating the boundary info in the first iteration, it should be
* possible to get the nodes that have vertices included in any boundary deformation
* and cache them. */
return true;
@@ -923,6 +926,10 @@ typedef struct StrokeCache {
float (*prev_colors)[4];
+ /* Multires Displacement Smear. */
+ float (*prev_displacement)[3];
+ float (*limit_surface_co)[3];
+
/* The rest is temporary storage that isn't saved as a property */
bool first_time; /* Beginning of stroke may do some things special */
diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c
index 167215b3813..d186cafb857 100644
--- a/source/blender/editors/space_action/action_edit.c
+++ b/source/blender/editors/space_action/action_edit.c
@@ -1827,7 +1827,7 @@ static const EnumPropertyItem prop_actkeys_mirror_types[] = {
{ACTKEYS_MIRROR_XAXIS,
"XAXIS",
0,
- "By Values Over Value=0",
+ "By Values Over Zero Value",
"Flip values of selected keyframes (i.e. negative values become positive, and vice versa)"},
{ACTKEYS_MIRROR_MARKER,
"MARKER",
diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c
index ab5f1e0ab22..98e39520e99 100644
--- a/source/blender/editors/space_action/action_select.c
+++ b/source/blender/editors/space_action/action_select.c
@@ -1325,8 +1325,8 @@ void ACTION_OT_select_less(wmOperatorType *ot)
/* defines for left-right select tool */
static const EnumPropertyItem prop_actkeys_leftright_select_types[] = {
{ACTKEYS_LRSEL_TEST, "CHECK", 0, "Check if Select Left or Right", ""},
- {ACTKEYS_LRSEL_LEFT, "LEFT", 0, "Before current frame", ""},
- {ACTKEYS_LRSEL_RIGHT, "RIGHT", 0, "After current frame", ""},
+ {ACTKEYS_LRSEL_LEFT, "LEFT", 0, "Before Current Frame", ""},
+ {ACTKEYS_LRSEL_RIGHT, "RIGHT", 0, "After Current Frame", ""},
{0, NULL, 0, NULL, NULL},
};
diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c
index 10ce7b81954..0fe94ec382c 100644
--- a/source/blender/editors/space_api/spacetypes.c
+++ b/source/blender/editors/space_api/spacetypes.c
@@ -40,6 +40,7 @@
#include "ED_anim_api.h"
#include "ED_armature.h"
+#include "ED_asset.h"
#include "ED_clip.h"
#include "ED_curve.h"
#include "ED_fileselect.h"
@@ -105,6 +106,7 @@ void ED_spacetypes_init(void)
ED_operatortypes_screen();
ED_operatortypes_anim();
ED_operatortypes_animchannels();
+ ED_operatortypes_asset();
ED_operatortypes_gpencil();
ED_operatortypes_object();
ED_operatortypes_lattice();
diff --git a/source/blender/editors/space_buttons/CMakeLists.txt b/source/blender/editors/space_buttons/CMakeLists.txt
index fa3e6a51036..c71e5e49d8d 100644
--- a/source/blender/editors/space_buttons/CMakeLists.txt
+++ b/source/blender/editors/space_buttons/CMakeLists.txt
@@ -51,6 +51,7 @@ endif()
if(WITH_EXPERIMENTAL_FEATURES)
add_definitions(-DWITH_GEOMETRY_NODES)
+ add_definitions(-DWITH_POINT_CLOUD)
add_definitions(-DWITH_HAIR_NODES)
endif()
diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c
index 8b39995a5c9..c1f29231f96 100644
--- a/source/blender/editors/space_buttons/buttons_context.c
+++ b/source/blender/editors/space_buttons/buttons_context.c
@@ -246,9 +246,11 @@ static bool buttons_context_path_data(ButsContextPath *path, int type)
return true;
}
#endif
+#ifdef WITH_POINT_CLOUD
if (RNA_struct_is_a(ptr->type, &RNA_PointCloud) && (type == -1 || type == OB_POINTCLOUD)) {
return true;
}
+#endif
if (RNA_struct_is_a(ptr->type, &RNA_Volume) && (type == -1 || type == OB_VOLUME)) {
return true;
}
@@ -812,7 +814,9 @@ const char *buttons_context_dir[] = {
#ifdef WITH_HAIR_NODES
"hair",
#endif
+#ifdef WITH_POINT_CLOUD
"pointcloud",
+#endif
"volume",
NULL,
};
@@ -822,6 +826,11 @@ int /*eContextResult*/ buttons_context(const bContext *C,
bContextDataResult *result)
{
SpaceProperties *sbuts = CTX_wm_space_properties(C);
+ if (sbuts && sbuts->path == NULL) {
+ /* path is cleared for SCREEN_OT_redo_last, when global undo does a file-read which clears the
+ * path (see lib_link_workspace_layout_restore). */
+ buttons_context_compute(C, sbuts);
+ }
ButsContextPath *path = sbuts ? sbuts->path : NULL;
if (!path) {
@@ -899,10 +908,12 @@ int /*eContextResult*/ buttons_context(const bContext *C,
return CTX_RESULT_OK;
}
#endif
+#ifdef WITH_POINT_CLOUD
if (CTX_data_equals(member, "pointcloud")) {
set_pointer_type(path, result, &RNA_PointCloud);
return CTX_RESULT_OK;
}
+#endif
if (CTX_data_equals(member, "volume")) {
set_pointer_type(path, result, &RNA_Volume);
return CTX_RESULT_OK;
diff --git a/source/blender/editors/space_buttons/buttons_intern.h b/source/blender/editors/space_buttons/buttons_intern.h
index 0a0846cf216..74e7bc11c26 100644
--- a/source/blender/editors/space_buttons/buttons_intern.h
+++ b/source/blender/editors/space_buttons/buttons_intern.h
@@ -35,7 +35,6 @@ struct bContext;
struct bContextDataResult;
struct bNode;
struct bNodeTree;
-struct uiLayout;
struct wmOperatorType;
struct SpaceProperties_Runtime {
diff --git a/source/blender/editors/space_buttons/buttons_ops.c b/source/blender/editors/space_buttons/buttons_ops.c
index 8bdc2ed993f..8f57abf83ae 100644
--- a/source/blender/editors/space_buttons/buttons_ops.c
+++ b/source/blender/editors/space_buttons/buttons_ops.c
@@ -338,7 +338,7 @@ static int file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event)
is_relative = BLI_path_is_rel(str);
}
- if (UNLIKELY(ptr.data == &U)) {
+ if (UNLIKELY(ptr.data == &U || is_userdef)) {
is_relative = false;
}
diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c
index 3f00e3114a5..8f36ccb019a 100644
--- a/source/blender/editors/space_clip/clip_ops.c
+++ b/source/blender/editors/space_clip/clip_ops.c
@@ -859,7 +859,7 @@ void CLIP_OT_view_zoom_out(wmOperatorType *ot)
-FLT_MAX,
FLT_MAX,
"Location",
- "Cursor location in normalized (0.0-1.0) coordinates",
+ "Cursor location in normalized (0.0 to 1.0) coordinates",
-10.0f,
10.0f);
RNA_def_property_flag(prop, PROP_HIDDEN);
diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c
index a54faa41122..9b8e9e0e871 100644
--- a/source/blender/editors/space_console/space_console.c
+++ b/source/blender/editors/space_console/space_console.c
@@ -164,12 +164,12 @@ static bool id_drop_poll(bContext *UNUSED(C),
const wmEvent *UNUSED(event),
const char **UNUSED(tooltip))
{
- return WM_drag_ID(drag, 0) != NULL;
+ return WM_drag_get_local_ID(drag, 0) != NULL;
}
static void id_drop_copy(wmDrag *drag, wmDropBox *drop)
{
- ID *id = WM_drag_ID(drag, 0);
+ ID *id = WM_drag_get_local_ID(drag, 0);
/* copy drag path to properties */
char *text = RNA_path_full_ID_py(G_MAIN, id);
diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c
index e3bdda7c480..f2f7f9d82f9 100644
--- a/source/blender/editors/space_file/file_draw.c
+++ b/source/blender/editors/space_file/file_draw.c
@@ -25,6 +25,7 @@
#include <math.h>
#include <string.h>
+#include "BLI_alloca.h"
#include "BLI_blenlib.h"
#include "BLI_fileops_types.h"
#include "BLI_math.h"
@@ -134,6 +135,7 @@ static void draw_tile(int sx, int sy, int width, int height, int colorid, int sh
}
static void file_draw_icon(uiBlock *block,
+ const FileDirEntry *file,
const char *path,
int sx,
int sy,
@@ -157,8 +159,29 @@ static void file_draw_icon(uiBlock *block,
UI_but_func_tooltip_set(but, file_draw_tooltip_func, BLI_strdup(path));
if (drag) {
- /* path is no more static, cannot give it directly to but... */
- UI_but_drag_set_path(but, BLI_strdup(path), true);
+ /* TODO duplicated from file_draw_preview(). */
+ ID *id;
+
+ if ((id = filelist_file_get_id(file))) {
+ UI_but_drag_set_id(but, id);
+ }
+ else if (file->typeflag & FILE_TYPE_ASSET) {
+ ImBuf *preview_image = filelist_file_getimage(file);
+ char blend_path[FILE_MAX_LIBEXTRA];
+ if (BLO_library_path_explode(path, blend_path, NULL, NULL)) {
+ UI_but_drag_set_asset(but,
+ file->name,
+ BLI_strdup(blend_path),
+ file->blentype,
+ icon,
+ preview_image,
+ UI_DPI_FAC);
+ }
+ }
+ else {
+ /* path is no more static, cannot give it directly to but... */
+ UI_but_drag_set_path(but, BLI_strdup(path), true);
+ }
}
}
@@ -200,6 +223,65 @@ static void file_draw_string(int sx,
});
}
+/**
+ * \param r_sx, r_sy: The lower right corner of the last line drawn. AKA the cursor position on
+ * completion.
+ */
+static void file_draw_string_multiline(int sx,
+ int sy,
+ const char *string,
+ int wrap_width,
+ int line_height,
+ const uchar text_col[4],
+ int *r_sx,
+ int *r_sy)
+{
+ rcti rect;
+
+ if (string[0] == '\0' || wrap_width < 1) {
+ return;
+ }
+
+ const uiStyle *style = UI_style_get();
+ int font_id = style->widgetlabel.uifont_id;
+ int len = strlen(string);
+
+ rctf textbox;
+ BLF_wordwrap(font_id, wrap_width);
+ BLF_enable(font_id, BLF_WORD_WRAP);
+ BLF_boundbox(font_id, string, len, &textbox);
+ BLF_disable(font_id, BLF_WORD_WRAP);
+
+ /* no text clipping needed, UI_fontstyle_draw does it but is a bit too strict
+ * (for buttons it works) */
+ rect.xmin = sx;
+ rect.xmax = sx + wrap_width;
+ /* Need to increase the clipping rect by one more line, since the #UI_fontstyle_draw_ex() will
+ * actually start drawing at (ymax - line-height). */
+ rect.ymin = sy - round_fl_to_int(BLI_rctf_size_y(&textbox)) - line_height;
+ rect.ymax = sy;
+
+ struct ResultBLF result;
+ UI_fontstyle_draw_ex(&style->widget,
+ &rect,
+ string,
+ text_col,
+ &(struct uiFontStyleDraw_Params){
+ .align = UI_STYLE_TEXT_LEFT,
+ .word_wrap = true,
+ },
+ len,
+ NULL,
+ NULL,
+ &result);
+ if (r_sx) {
+ *r_sx = result.width;
+ }
+ if (r_sy) {
+ *r_sy = rect.ymin + line_height;
+ }
+}
+
void file_calc_previews(const bContext *C, ARegion *region)
{
SpaceFile *sfile = CTX_wm_space_file(C);
@@ -210,6 +292,7 @@ void file_calc_previews(const bContext *C, ARegion *region)
}
static void file_draw_preview(uiBlock *block,
+ const FileDirEntry *file,
const char *path,
int sx,
int sy,
@@ -218,7 +301,6 @@ static void file_draw_preview(uiBlock *block,
const int icon,
FileLayout *layout,
const bool is_icon,
- const int typeflags,
const bool drag,
const bool dimmed,
const bool is_link)
@@ -232,7 +314,7 @@ static void file_draw_preview(uiBlock *block,
float scale;
int ex, ey;
bool show_outline = !is_icon &&
- (typeflags & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_BLENDER));
+ (file->typeflag & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_BLENDER));
BLI_assert(imb != NULL);
@@ -273,14 +355,14 @@ static void file_draw_preview(uiBlock *block,
float col[4] = {1.0f, 1.0f, 1.0f, 1.0f};
if (is_icon) {
- if (typeflags & FILE_TYPE_DIR) {
+ if (file->typeflag & FILE_TYPE_DIR) {
UI_GetThemeColor4fv(TH_ICON_FOLDER, col);
}
else {
UI_GetThemeColor4fv(TH_TEXT, col);
}
}
- else if (typeflags & FILE_TYPE_FTFONT) {
+ else if (file->typeflag & FILE_TYPE_FTFONT) {
UI_GetThemeColor4fv(TH_TEXT, col);
}
@@ -288,7 +370,7 @@ static void file_draw_preview(uiBlock *block,
col[3] *= 0.3f;
}
- if (!is_icon && typeflags & FILE_TYPE_BLENDERLIB) {
+ if (!is_icon && file->typeflag & FILE_TYPE_BLENDERLIB) {
/* Datablock preview images use premultiplied alpha. */
GPU_blend(GPU_BLEND_ALPHA_PREMULT);
}
@@ -324,7 +406,7 @@ static void file_draw_preview(uiBlock *block,
icon_color[2] = 255;
}
icon_x = xco + (ex / 2.0f) - (icon_size / 2.0f);
- icon_y = yco + (ey / 2.0f) - (icon_size * ((typeflags & FILE_TYPE_DIR) ? 0.78f : 0.75f));
+ icon_y = yco + (ey / 2.0f) - (icon_size * ((file->typeflag & FILE_TYPE_DIR) ? 0.78f : 0.75f));
UI_icon_draw_ex(
icon_x, icon_y, icon, icon_aspect / U.dpi_fac, icon_opacity, 0.0f, icon_color, false);
}
@@ -346,13 +428,13 @@ static void file_draw_preview(uiBlock *block,
/* Link to folder or non-previewed file. */
uchar icon_color[4];
UI_GetThemeColor4ubv(TH_BACK, icon_color);
- icon_x = xco + ((typeflags & FILE_TYPE_DIR) ? 0.14f : 0.23f) * scaledx;
- icon_y = yco + ((typeflags & FILE_TYPE_DIR) ? 0.24f : 0.14f) * scaledy;
+ icon_x = xco + ((file->typeflag & FILE_TYPE_DIR) ? 0.14f : 0.23f) * scaledx;
+ icon_y = yco + ((file->typeflag & FILE_TYPE_DIR) ? 0.24f : 0.14f) * scaledy;
UI_icon_draw_ex(
icon_x, icon_y, arrow, icon_aspect / U.dpi_fac * 1.8, 0.3f, 0.0f, icon_color, false);
}
}
- else if (icon && !is_icon && !(typeflags & FILE_TYPE_FTFONT)) {
+ else if (icon && !is_icon && !(file->typeflag & FILE_TYPE_FTFONT)) {
/* Smaller, fainter icon at bottom-left for preview image thumbnail, but not for fonts. */
float icon_x, icon_y;
const uchar dark[4] = {0, 0, 0, 255};
@@ -385,8 +467,22 @@ static void file_draw_preview(uiBlock *block,
/* dragregion */
if (drag) {
+ ID *id;
+
+ if ((id = filelist_file_get_id(file))) {
+ UI_but_drag_set_id(but, id);
+ }
/* path is no more static, cannot give it directly to but... */
- UI_but_drag_set_image(but, BLI_strdup(path), icon, imb, scale, true);
+ else if (file->typeflag & FILE_TYPE_ASSET) {
+ char blend_path[FILE_MAX_LIBEXTRA];
+ if (BLO_library_path_explode(path, blend_path, NULL, NULL)) {
+ UI_but_drag_set_asset(
+ but, file->name, BLI_strdup(blend_path), file->blentype, icon, imb, scale);
+ }
+ }
+ else {
+ UI_but_drag_set_image(but, BLI_strdup(path), icon, imb, scale, true);
+ }
}
GPU_blend(GPU_BLEND_NONE);
@@ -821,6 +917,7 @@ void file_draw_list(const bContext *C, ARegion *region)
}
file_draw_preview(block,
+ file,
path,
sx,
sy,
@@ -829,13 +926,13 @@ void file_draw_list(const bContext *C, ARegion *region)
icon,
layout,
is_icon,
- file->typeflag,
do_drag,
is_hidden,
is_link);
}
else {
file_draw_icon(block,
+ file,
path,
sx,
sy - layout->tile_border_y,
@@ -906,3 +1003,66 @@ void file_draw_list(const bContext *C, ARegion *region)
layout->curr_size = params->thumbnail_size;
}
+
+static void file_draw_invalid_library_hint(const SpaceFile *sfile, const ARegion *region)
+{
+ const FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
+
+ char library_ui_path[PATH_MAX];
+ file_path_to_ui_path(asset_params->base_params.dir, library_ui_path, sizeof(library_ui_path));
+
+ uchar text_col[4];
+ uchar text_alert_col[4];
+ UI_GetThemeColor4ubv(TH_TEXT, text_col);
+ UI_GetThemeColor4ubv(TH_REDALERT, text_alert_col);
+
+ const View2D *v2d = &region->v2d;
+ const int pad = sfile->layout->tile_border_x;
+ const int width = BLI_rctf_size_x(&v2d->tot) - (2 * pad);
+ const int line_height = sfile->layout->textheight;
+ int sx = v2d->tot.xmin + pad;
+ /* For some reason no padding needed. */
+ int sy = v2d->tot.ymax;
+
+ {
+ const char *message = TIP_("Library not found");
+ const int draw_string_str_len = strlen(message) + 2 + sizeof(library_ui_path);
+ char *draw_string = alloca(draw_string_str_len);
+ BLI_snprintf(draw_string, draw_string_str_len, "%s: %s", message, library_ui_path);
+ file_draw_string_multiline(sx, sy, draw_string, width, line_height, text_alert_col, NULL, &sy);
+ }
+
+ /* Next line, but separate it a bit further. */
+ sy -= line_height;
+
+ {
+ UI_icon_draw(sx, sy - UI_UNIT_Y, ICON_INFO);
+
+ const char *suggestion = TIP_(
+ "Set up the library or edit libraries in the Preferences, File Paths section.");
+ file_draw_string_multiline(
+ sx + UI_UNIT_X, sy, suggestion, width - UI_UNIT_X, line_height, text_col, NULL, NULL);
+ }
+}
+
+/**
+ * Draw a string hint if the file list is invalid.
+ * \return true if the list is invalid and a hint was drawn.
+ */
+bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region)
+{
+ FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
+ /* Only for asset browser. */
+ if (!ED_fileselect_is_asset_browser(sfile)) {
+ return false;
+ }
+ /* Check if the library exists. */
+ if ((asset_params->asset_library.type == FILE_ASSET_LIBRARY_LOCAL) ||
+ filelist_is_dir(sfile->files, asset_params->base_params.dir)) {
+ return false;
+ }
+
+ file_draw_invalid_library_hint(sfile, region);
+
+ return true;
+}
diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h
index b459c02d9e5..56fb588776e 100644
--- a/source/blender/editors/space_file/file_intern.h
+++ b/source/blender/editors/space_file/file_intern.h
@@ -38,6 +38,7 @@ struct View2D;
void file_calc_previews(const bContext *C, ARegion *region);
void file_draw_list(const bContext *C, ARegion *region);
+bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region);
void file_draw_check_ex(bContext *C, struct ScrArea *area);
void file_draw_check(bContext *C);
@@ -90,6 +91,7 @@ void file_sfile_to_operator(struct Main *bmain, struct wmOperator *op, struct Sp
void file_operator_to_sfile(struct Main *bmain, struct SpaceFile *sfile, struct wmOperator *op);
/* filesel.c */
+void fileselect_refresh_params(struct SpaceFile *sfile);
void fileselect_file_set(SpaceFile *sfile, const int index);
bool file_attribute_column_type_enabled(const FileSelectParams *params,
FileAttributeColumnType column);
@@ -116,3 +118,5 @@ void file_execute_region_panels_register(struct ARegionType *art);
/* file_utils.c */
void file_tile_boundbox(const ARegion *region, FileLayout *layout, const int file, rcti *r_bounds);
+
+void file_path_to_ui_path(const char *path, char *r_pathi, int max_size);
diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c
index b98348307f3..3730174a6c7 100644
--- a/source/blender/editors/space_file/file_ops.c
+++ b/source/blender/editors/space_file/file_ops.c
@@ -40,6 +40,7 @@
# include "BLI_winstuff.h"
#endif
+#include "ED_asset.h"
#include "ED_fileselect.h"
#include "ED_screen.h"
#include "ED_select_utils.h"
@@ -930,6 +931,7 @@ void FILE_OT_select_all(wmOperatorType *ot)
/* Note we could get rid of this one, but it's used by some addon so...
* Does not hurt keeping it around for now. */
+/* TODO disallow bookmark editing in assets mode? */
static int bookmark_select_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
@@ -1836,10 +1838,6 @@ static int file_previous_exec(bContext *C, wmOperator *UNUSED(op))
FileSelectParams *params = ED_fileselect_get_active_params(sfile);
if (params) {
- if (!sfile->folders_next) {
- sfile->folders_next = folderlist_new();
- }
-
folderlist_pushdir(sfile->folders_next, params->dir);
folderlist_popdir(sfile->folders_prev, params->dir);
folderlist_pushdir(sfile->folders_next, params->dir);
@@ -1874,10 +1872,6 @@ static int file_next_exec(bContext *C, wmOperator *UNUSED(unused))
SpaceFile *sfile = CTX_wm_space_file(C);
FileSelectParams *params = ED_fileselect_get_active_params(sfile);
if (params) {
- if (!sfile->folders_next) {
- sfile->folders_next = folderlist_new();
- }
-
folderlist_pushdir(sfile->folders_prev, params->dir);
folderlist_popdir(sfile->folders_next, params->dir);
@@ -2702,6 +2696,29 @@ static bool file_delete_poll(bContext *C)
return poll;
}
+static bool file_delete_single(const FileSelectParams *params,
+ FileDirEntry *file,
+ const char **r_error_message)
+{
+ if (file->typeflag & FILE_TYPE_ASSET) {
+ ID *id = filelist_file_get_id(file);
+ if (!id) {
+ *r_error_message = "File is not a local data-block asset.";
+ return false;
+ }
+ ED_asset_clear_id(id);
+ }
+ else {
+ char str[FILE_MAX];
+ BLI_join_dirfile(str, sizeof(str), params->dir, file->relpath);
+ if (BLI_delete_soft(str, r_error_message) != 0 || BLI_exists(str)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
static int file_delete_exec(bContext *C, wmOperator *op)
{
wmWindowManager *wm = CTX_wm_manager(C);
@@ -2715,9 +2732,7 @@ static int file_delete_exec(bContext *C, wmOperator *op)
for (int i = 0; i < numfiles; i++) {
if (filelist_entry_select_index_get(sfile->files, i, CHECK_ALL)) {
FileDirEntry *file = filelist_file(sfile->files, i);
- char str[FILE_MAX];
- BLI_join_dirfile(str, sizeof(str), params->dir, file->relpath);
- if (BLI_delete_soft(str, &error_message) != 0 || BLI_exists(str)) {
+ if (!file_delete_single(params, file, &error_message)) {
report_error = true;
}
}
@@ -2763,13 +2778,20 @@ void FILE_OT_delete(struct wmOperatorType *ot)
static int file_start_filter_exec(bContext *C, wmOperator *UNUSED(op))
{
ScrArea *area = CTX_wm_area(C);
- ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_UI);
SpaceFile *sfile = CTX_wm_space_file(C);
FileSelectParams *params = ED_fileselect_get_active_params(sfile);
ARegion *region_ctx = CTX_wm_region(C);
- CTX_wm_region_set(C, region);
- UI_textbutton_activate_rna(C, region, params, "filter_search");
+
+ if (area) {
+ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) {
+ CTX_wm_region_set(C, region);
+ if (UI_textbutton_activate_rna(C, region, params, "filter_search")) {
+ break;
+ }
+ }
+ }
+
CTX_wm_region_set(C, region_ctx);
return OPERATOR_FINISHED;
diff --git a/source/blender/editors/space_file/file_utils.c b/source/blender/editors/space_file/file_utils.c
index 452f2f704cf..9d85996c559 100644
--- a/source/blender/editors/space_file/file_utils.c
+++ b/source/blender/editors/space_file/file_utils.c
@@ -18,12 +18,14 @@
* \ingroup spfile
*/
+#include "BLI_fileops.h"
#include "BLI_listbase.h"
+#include "BLI_path_util.h"
#include "BLI_rect.h"
-
-#include "BLO_readfile.h"
+#include "BLI_string.h"
#include "BKE_context.h"
+#include "BLO_readfile.h"
#include "ED_fileselect.h"
#include "ED_screen.h"
@@ -44,3 +46,14 @@ void file_tile_boundbox(const ARegion *region, FileLayout *layout, const int fil
ymax - layout->tile_h - layout->tile_border_y,
ymax);
}
+
+/**
+ * If \a path leads to a .blend, remove the trailing slash (if needed).
+ */
+void file_path_to_ui_path(const char *path, char *r_path, int max_size)
+{
+ char tmp_path[PATH_MAX];
+ BLI_strncpy(tmp_path, path, sizeof(tmp_path));
+ BLI_path_slash_rstrip(tmp_path);
+ BLI_strncpy(r_path, BLO_has_bfile_extension(tmp_path) ? tmp_path : path, max_size);
+}
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index e87142a7096..d66219c7549 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -53,13 +53,18 @@
# include "BLI_winstuff.h"
#endif
+#include "BKE_asset.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_icons.h"
#include "BKE_idtype.h"
+#include "BKE_lib_id.h"
#include "BKE_main.h"
+#include "BKE_main_idmap.h"
+#include "BKE_preferences.h"
#include "BLO_readfile.h"
+#include "DNA_asset_types.h"
#include "DNA_space_types.h"
#include "ED_datafiles.h"
@@ -82,6 +87,8 @@
#include "filelist.h"
+#define FILEDIR_NBR_ENTRIES_UNSET -1
+
/* ----------------- FOLDERLIST (previous/next) -------------- */
typedef struct FolderList {
@@ -89,12 +96,6 @@ typedef struct FolderList {
char *foldername;
} FolderList;
-ListBase *folderlist_new(void)
-{
- ListBase *p = MEM_callocN(sizeof(*p), __func__);
- return p;
-}
-
void folderlist_popdir(struct ListBase *folderlist, char *dir)
{
const char *prev_dir;
@@ -117,6 +118,10 @@ void folderlist_popdir(struct ListBase *folderlist, char *dir)
void folderlist_pushdir(ListBase *folderlist, const char *dir)
{
+ if (!dir[0]) {
+ return;
+ }
+
struct FolderList *folder, *previous_folder;
previous_folder = folderlist->last;
@@ -153,7 +158,7 @@ int folderlist_clear_next(struct SpaceFile *sfile)
struct FolderList *folder;
/* if there is no folder_next there is nothing we can clear */
- if (!sfile->folders_next) {
+ if (BLI_listbase_is_empty(sfile->folders_next)) {
return 0;
}
@@ -180,23 +185,79 @@ void folderlist_free(ListBase *folderlist)
}
}
-ListBase *folderlist_duplicate(ListBase *folderlist)
+static ListBase folderlist_duplicate(ListBase *folderlist)
{
+ ListBase folderlistn = {NULL};
- if (folderlist) {
- ListBase *folderlistn = MEM_callocN(sizeof(*folderlistn), __func__);
- FolderList *folder;
+ BLI_duplicatelist(&folderlistn, folderlist);
+
+ for (FolderList *folder = folderlistn.first; folder; folder = folder->next) {
+ folder->foldername = MEM_dupallocN(folder->foldername);
+ }
+ return folderlistn;
+}
- BLI_duplicatelist(folderlistn, folderlist);
+/* ----------------- Folder-History (wraps/owns file list above) -------------- */
- for (folder = folderlistn->first; folder; folder = folder->next) {
- folder->foldername = MEM_dupallocN(folder->foldername);
+static FileFolderHistory *folder_history_find(const SpaceFile *sfile, eFileBrowse_Mode browse_mode)
+{
+ LISTBASE_FOREACH (FileFolderHistory *, history, &sfile->folder_histories) {
+ if (history->browse_mode == browse_mode) {
+ return history;
}
- return folderlistn;
}
+
return NULL;
}
+void folder_history_list_ensure_for_active_browse_mode(SpaceFile *sfile)
+{
+ FileFolderHistory *history = folder_history_find(sfile, sfile->browse_mode);
+
+ if (!history) {
+ history = MEM_callocN(sizeof(*history), __func__);
+ history->browse_mode = sfile->browse_mode;
+ BLI_addtail(&sfile->folder_histories, history);
+ }
+
+ sfile->folders_next = &history->folders_next;
+ sfile->folders_prev = &history->folders_prev;
+}
+
+static void folder_history_entry_free(SpaceFile *sfile, FileFolderHistory *history)
+{
+ if (sfile->folders_prev == &history->folders_prev) {
+ sfile->folders_prev = NULL;
+ }
+ if (sfile->folders_next == &history->folders_next) {
+ sfile->folders_next = NULL;
+ }
+ folderlist_free(&history->folders_prev);
+ folderlist_free(&history->folders_next);
+ BLI_freelinkN(&sfile->folder_histories, history);
+}
+
+void folder_history_list_free(SpaceFile *sfile)
+{
+ LISTBASE_FOREACH_MUTABLE (FileFolderHistory *, history, &sfile->folder_histories) {
+ folder_history_entry_free(sfile, history);
+ }
+}
+
+ListBase folder_history_list_duplicate(ListBase *listbase)
+{
+ ListBase histories = {NULL};
+
+ LISTBASE_FOREACH (FileFolderHistory *, history, listbase) {
+ FileFolderHistory *history_new = MEM_dupallocN(history);
+ history_new->folders_prev = folderlist_duplicate(&history->folders_prev);
+ history_new->folders_next = folderlist_duplicate(&history->folders_next);
+ BLI_addtail(&histories, history_new);
+ }
+
+ return histories;
+}
+
/* ------------------FILELIST------------------------ */
typedef struct FileListInternEntry {
@@ -216,6 +277,24 @@ typedef struct FileListInternEntry {
/** not strictly needed, but used during sorting, avoids to have to recompute it there... */
char *name;
+ /**
+ * This is data from the current main, represented by this file. It's crucial that this is
+ * updated correctly on undo, redo and file reading (without UI). The space is responsible to
+ * take care of that.
+ */
+ struct {
+ /** When showing local IDs (FILE_MAIN, FILE_MAIN_ASSET), the ID this file entry represents. */
+ ID *id;
+
+ /* For the few file types that have the preview already in memory. For others, there's delayed
+ * preview reading from disk. Non-owning pointer. */
+ PreviewImage *preview_image;
+ } local_data;
+
+ /** When the file represents an asset read from another file, it is stored here.
+ * Owning pointer. */
+ AssetMetaData *imported_asset_data;
+
/** Defined in BLI_fileops.h */
eFileAttributes attributes;
BLI_stat_t st;
@@ -267,7 +346,11 @@ typedef struct FileListEntryPreview {
char path[FILE_MAX];
uint flags;
int index;
- ImBuf *img;
+ /* Some file types load the memory from runtime data, not from disk. We just wait until it's done
+ * generating (BKE_previewimg_is_finished()). */
+ PreviewImage *in_memory_preview;
+
+ int icon_id;
} FileListEntryPreview;
/* Dummy wrapper around FileListEntryPreview to ensure we do not access freed memory when freeing
@@ -290,11 +373,16 @@ enum {
FLF_HIDE_DOT = 1 << 1,
FLF_HIDE_PARENT = 1 << 2,
FLF_HIDE_LIB_DIR = 1 << 3,
+ FLF_ASSETS_ONLY = 1 << 4,
};
typedef struct FileList {
FileDirEntryArr filelist;
+ eFileSelectType type;
+ /* The library this list was created for. Stored here so we know when to re-read. */
+ FileSelectAssetLibraryUID *asset_library;
+
short flags;
short sort;
@@ -324,10 +412,13 @@ typedef struct FileList {
bool (*checkdirf)(struct FileList *, char *, const bool);
/* Fill filelist (to be called by read job). */
- void (*read_jobf)(struct FileList *, const char *, short *, short *, float *, ThreadMutex *);
+ void (*read_jobf)(
+ Main *, struct FileList *, const char *, short *, short *, float *, ThreadMutex *);
/* Filter an entry of current filelist. */
bool (*filterf)(struct FileListInternEntry *, const char *, FileListFilter *);
+
+ short tags; /* FileListTags */
} FileList;
/* FileList.flags */
@@ -340,6 +431,14 @@ enum {
FL_SORT_INVERT = 1 << 5,
};
+/* FileList.tags */
+enum FileListTags {
+ /** The file list has references to main data (IDs) and needs special care. */
+ FILELIST_TAGS_USES_MAIN_DATA = (1 << 0),
+ /** The file list type is not thread-safe. */
+ FILELIST_TAGS_NO_THREADS = (1 << 2),
+};
+
#define SPECIAL_IMG_SIZE 256
#define SPECIAL_IMG_ROWS 1
#define SPECIAL_IMG_COLS 7
@@ -357,24 +456,34 @@ enum {
static ImBuf *gSpecialFileImages[SPECIAL_IMG_MAX];
-static void filelist_readjob_main(FileList *filelist,
+static void filelist_readjob_main(Main *current_main,
+ FileList *filelist,
const char *main_name,
short *stop,
short *do_update,
float *progress,
ThreadMutex *lock);
-static void filelist_readjob_lib(FileList *filelist,
+static void filelist_readjob_lib(Main *current_main,
+ FileList *filelist,
const char *main_name,
short *stop,
short *do_update,
float *progress,
ThreadMutex *lock);
-static void filelist_readjob_dir(FileList *filelist,
+static void filelist_readjob_dir(Main *current_main,
+ FileList *filelist,
const char *main_name,
short *stop,
short *do_update,
float *progress,
ThreadMutex *lock);
+static void filelist_readjob_main_assets(Main *current_main,
+ FileList *filelist,
+ const char *main_name,
+ short *stop,
+ short *do_update,
+ float *progress,
+ ThreadMutex *lock);
/* helper, could probably go in BKE actually? */
static int groupname_to_code(const char *group);
@@ -694,6 +803,11 @@ static bool is_filtered_hidden(const char *filename,
return true;
}
#endif
+ /* For data-blocks (but not the group directories), check the asset-only filter. */
+ if (!(file->typeflag & FILE_TYPE_DIR) && (file->typeflag & FILE_TYPE_BLENDERLIB) &&
+ (filter->flags & FLF_ASSETS_ONLY) && !(file->typeflag & FILE_TYPE_ASSET)) {
+ return true;
+ }
return false;
}
@@ -737,51 +851,61 @@ static bool is_filtered_file(FileListInternEntry *file,
return is_filtered;
}
-static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileListFilter *filter)
+static bool is_filtered_id_file(const FileListInternEntry *file,
+ const char *id_group,
+ const char *name,
+ const FileListFilter *filter)
{
- bool is_filtered;
- char path[FILE_MAX_LIBEXTRA], dir[FILE_MAX_LIBEXTRA], *group, *name;
-
- BLI_join_dirfile(path, sizeof(path), root, file->relpath);
-
- if (BLO_library_path_explode(path, dir, &group, &name)) {
- is_filtered = !is_filtered_hidden(file->relpath, filter, file);
- if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) {
- /* We only check for types if some type are enabled in filtering. */
- if ((filter->filter || filter->filter_id) && (filter->flags & FLF_DO_FILTER)) {
- if (file->typeflag & FILE_TYPE_DIR) {
- if (file->typeflag &
- (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) {
- if (!(filter->filter & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))) {
- is_filtered = false;
- }
- }
- else {
- if (!(filter->filter & FILE_TYPE_FOLDER)) {
- is_filtered = false;
- }
+ bool is_filtered = !is_filtered_hidden(file->relpath, filter, file);
+ if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) {
+ /* We only check for types if some type are enabled in filtering. */
+ if ((filter->filter || filter->filter_id) && (filter->flags & FLF_DO_FILTER)) {
+ if (file->typeflag & FILE_TYPE_DIR) {
+ if (file->typeflag &
+ (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) {
+ if (!(filter->filter & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))) {
+ is_filtered = false;
}
}
- if (is_filtered && group) {
- if (!name && (filter->flags & FLF_HIDE_LIB_DIR)) {
+ else {
+ if (!(filter->filter & FILE_TYPE_FOLDER)) {
is_filtered = false;
}
- else {
- uint64_t filter_id = groupname_to_filter_id(group);
- if (!(filter_id & filter->filter_id)) {
- is_filtered = false;
- }
- }
}
}
- /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */
- if (is_filtered && (filter->filter_search[0] != '\0')) {
- if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) {
+ if (is_filtered && id_group) {
+ if (!name && (filter->flags & FLF_HIDE_LIB_DIR)) {
is_filtered = false;
}
+ else {
+ uint64_t filter_id = groupname_to_filter_id(id_group);
+ if (!(filter_id & filter->filter_id)) {
+ is_filtered = false;
+ }
+ }
+ }
+ }
+ /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */
+ if (is_filtered && (filter->filter_search[0] != '\0')) {
+ if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) {
+ is_filtered = false;
}
}
}
+
+ return is_filtered;
+}
+
+static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileListFilter *filter)
+{
+ bool is_filtered;
+ char path[FILE_MAX_LIBEXTRA], dir[FILE_MAX_LIBEXTRA], *group, *name;
+
+ BLI_join_dirfile(path, sizeof(path), root, file->relpath);
+
+ if (BLO_library_path_explode(path, dir, &group, &name)) {
+ is_filtered = is_filtered_id_file(file, group, name, filter);
+ }
else {
is_filtered = is_filtered_file(file, root, filter);
}
@@ -796,6 +920,14 @@ static bool is_filtered_main(FileListInternEntry *file,
return !is_filtered_hidden(file->relpath, filter, file);
}
+static bool is_filtered_main_assets(FileListInternEntry *file,
+ const char *UNUSED(dir),
+ FileListFilter *filter)
+{
+ /* "Filtered" means *not* being filtered out... So return true if the file should be visible. */
+ return is_filtered_id_file(file, file->relpath, file->name, filter);
+}
+
static void filelist_filter_clear(FileList *filelist)
{
filelist->flags |= FL_NEED_FILTERING;
@@ -807,7 +939,7 @@ void filelist_filter(FileList *filelist)
const int num_files = filelist->filelist.nbr_entries;
FileListInternEntry **filtered_tmp, *file;
- if (filelist->filelist.nbr_entries == 0) {
+ if (ELEM(filelist->filelist.nbr_entries, FILEDIR_NBR_ENTRIES_UNSET, 0)) {
return;
}
@@ -858,6 +990,7 @@ void filelist_setfilter_options(FileList *filelist,
const bool hide_parent,
const uint64_t filter,
const uint64_t filter_id,
+ const bool filter_assets_only,
const char *filter_glob,
const char *filter_search)
{
@@ -875,6 +1008,10 @@ void filelist_setfilter_options(FileList *filelist,
filelist->filter_data.flags ^= FLF_HIDE_PARENT;
update = true;
}
+ if (((filelist->filter_data.flags & FLF_ASSETS_ONLY) != 0) != (filter_assets_only != 0)) {
+ filelist->filter_data.flags ^= FLF_ASSETS_ONLY;
+ update = true;
+ }
if (filelist->filter_data.filter != filter) {
filelist->filter_data.filter = filter;
update = true;
@@ -903,6 +1040,54 @@ void filelist_setfilter_options(FileList *filelist,
}
}
+/**
+ * Checks two libraries for equality.
+ * \return True if the libraries match.
+ */
+static bool filelist_compare_asset_libraries(const FileSelectAssetLibraryUID *library_a,
+ const FileSelectAssetLibraryUID *library_b)
+{
+ if (library_a->type != library_b->type) {
+ return false;
+ }
+ if (library_a->type == FILE_ASSET_LIBRARY_CUSTOM) {
+ /* Don't only check the index, also check that it's valid. */
+ bUserAssetLibrary *library_ptr_a = BKE_preferences_asset_library_find_from_index(
+ &U, library_a->custom_library_index);
+ return (library_ptr_a != NULL) &&
+ (library_a->custom_library_index == library_b->custom_library_index);
+ }
+
+ return true;
+}
+
+/**
+ * \param asset_library: May be NULL to unset the library.
+ */
+void filelist_setlibrary(FileList *filelist, const FileSelectAssetLibraryUID *asset_library)
+{
+ /* Unset if needed. */
+ if (!asset_library) {
+ if (filelist->asset_library) {
+ MEM_SAFE_FREE(filelist->asset_library);
+ filelist->flags |= FL_FORCE_RESET;
+ }
+ return;
+ }
+
+ if (!filelist->asset_library) {
+ filelist->asset_library = MEM_mallocN(sizeof(*filelist->asset_library),
+ "filelist asset library");
+ *filelist->asset_library = *asset_library;
+
+ filelist->flags |= FL_FORCE_RESET;
+ }
+ else if (!filelist_compare_asset_libraries(filelist->asset_library, asset_library)) {
+ *filelist->asset_library = *asset_library;
+ filelist->flags |= FL_FORCE_RESET;
+ }
+}
+
/* ********** Icon/image helpers ********** */
void filelist_init_icons(void)
@@ -960,7 +1145,12 @@ ImBuf *filelist_getimage(struct FileList *filelist, const int index)
{
FileDirEntry *file = filelist_geticon_get_file(filelist, index);
- return file->image;
+ return file->preview_icon_id ? BKE_icon_imbuf_get_buffer(file->preview_icon_id) : NULL;
+}
+
+ImBuf *filelist_file_getimage(const FileDirEntry *file)
+{
+ return file->preview_icon_id ? BKE_icon_imbuf_get_buffer(file->preview_icon_id) : NULL;
}
static ImBuf *filelist_geticon_image_ex(FileDirEntry *file)
@@ -988,12 +1178,12 @@ ImBuf *filelist_geticon_image(struct FileList *filelist, const int index)
return filelist_geticon_image_ex(file);
}
-static int filelist_geticon_ex(FileDirEntry *file,
+static int filelist_geticon_ex(const FileDirEntry *file,
const char *root,
const bool is_main,
const bool ignore_libdir)
{
- const int typeflag = file->typeflag;
+ const eFileSel_File_Types typeflag = file->typeflag;
if ((typeflag & FILE_TYPE_DIR) &&
!(ignore_libdir && (typeflag & (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER)))) {
@@ -1027,7 +1217,7 @@ static int filelist_geticon_ex(FileDirEntry *file,
if (file->redirection_path) {
target = file->redirection_path;
}
- else {
+ else if (root) {
BLI_join_dirfile(fullpath, sizeof(fullpath), root, file->relpath);
BLI_path_slash_ensure(fullpath);
}
@@ -1111,6 +1301,12 @@ int filelist_geticon(struct FileList *filelist, const int index, const bool is_m
return filelist_geticon_ex(file, filelist->filelist.root, is_main, false);
}
+int ED_file_icon(const FileDirEntry *file)
+{
+ return file->preview_icon_id ? file->preview_icon_id :
+ filelist_geticon_ex(file, NULL, false, false);
+}
+
/* ********** Main ********** */
static void parent_dir_until_exists_or_default_root(char *dir)
@@ -1160,6 +1356,14 @@ static bool filelist_checkdir_main(struct FileList *filelist, char *r_dir, const
return filelist_checkdir_lib(filelist, r_dir, do_change);
}
+static bool filelist_checkdir_main_assets(struct FileList *UNUSED(filelist),
+ char *UNUSED(r_dir),
+ const bool UNUSED(do_change))
+{
+ /* Main is always valid. */
+ return true;
+}
+
static void filelist_entry_clear(FileDirEntry *entry)
{
if (entry->name) {
@@ -1174,8 +1378,9 @@ static void filelist_entry_clear(FileDirEntry *entry)
if (entry->redirection_path) {
MEM_freeN(entry->redirection_path);
}
- if (entry->image) {
- IMB_freeImBuf(entry->image);
+ if (entry->preview_icon_id) {
+ BKE_icon_delete(entry->preview_icon_id);
+ entry->preview_icon_id = 0;
}
/* For now, consider FileDirEntryRevision::poin as not owned here,
* so no need to do anything about it */
@@ -1232,8 +1437,8 @@ static void filelist_direntryarr_free(FileDirEntryArr *array)
#else
BLI_assert(BLI_listbase_is_empty(&array->entries));
#endif
- array->nbr_entries = 0;
- array->nbr_entries_filtered = -1;
+ array->nbr_entries = FILEDIR_NBR_ENTRIES_UNSET;
+ array->nbr_entries_filtered = FILEDIR_NBR_ENTRIES_UNSET;
array->entry_idx_start = -1;
array->entry_idx_end = -1;
}
@@ -1249,6 +1454,10 @@ static void filelist_intern_entry_free(FileListInternEntry *entry)
if (entry->name) {
MEM_freeN(entry->name);
}
+ /* If we own the asset-data (it was generated from external file data), free it. */
+ if (entry->imported_asset_data) {
+ BKE_asset_metadata_free(&entry->imported_asset_data);
+ }
MEM_freeN(entry);
}
@@ -1272,37 +1481,54 @@ static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdat
FileListEntryPreview *preview = preview_taskdata->preview;
ThumbSource source = 0;
+ bool done = false;
// printf("%s: Start (%d)...\n", __func__, threadid);
- // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img);
- BLI_assert(preview->flags &
- (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT | FILE_TYPE_BLENDER |
- FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB));
-
- if (preview->flags & FILE_TYPE_IMAGE) {
- source = THB_SOURCE_IMAGE;
- }
- else if (preview->flags &
- (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB)) {
- source = THB_SOURCE_BLEND;
- }
- else if (preview->flags & FILE_TYPE_MOVIE) {
- source = THB_SOURCE_MOVIE;
- }
- else if (preview->flags & FILE_TYPE_FTFONT) {
- source = THB_SOURCE_FONT;
+ if (preview->in_memory_preview) {
+ if (BKE_previewimg_is_finished(preview->in_memory_preview, ICON_SIZE_PREVIEW)) {
+ ImBuf *imbuf = BKE_previewimg_to_imbuf(preview->in_memory_preview, ICON_SIZE_PREVIEW);
+ preview->icon_id = BKE_icon_imbuf_create(imbuf);
+ done = true;
+ }
}
+ else {
+ // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img);
+ BLI_assert(preview->flags &
+ (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT | FILE_TYPE_BLENDER |
+ FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB));
- IMB_thumb_path_lock(preview->path);
- /* Always generate biggest preview size for now, it's simpler and avoids having to re-generate in
- * case user switch to a bigger preview size. */
- preview->img = IMB_thumb_manage(preview->path, THB_LARGE, source);
- IMB_thumb_path_unlock(preview->path);
+ if (preview->flags & FILE_TYPE_IMAGE) {
+ source = THB_SOURCE_IMAGE;
+ }
+ else if (preview->flags &
+ (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB)) {
+ source = THB_SOURCE_BLEND;
+ }
+ else if (preview->flags & FILE_TYPE_MOVIE) {
+ source = THB_SOURCE_MOVIE;
+ }
+ else if (preview->flags & FILE_TYPE_FTFONT) {
+ source = THB_SOURCE_FONT;
+ }
+
+ IMB_thumb_path_lock(preview->path);
+ /* Always generate biggest preview size for now, it's simpler and avoids having to re-generate
+ * in case user switch to a bigger preview size. */
+ ImBuf *imbuf = IMB_thumb_manage(preview->path, THB_LARGE, source);
+ IMB_thumb_path_unlock(preview->path);
+ if (imbuf) {
+ preview->icon_id = BKE_icon_imbuf_create(imbuf);
+ }
+
+ done = true;
+ }
- /* That way task freeing function won't free th preview, since it does not own it anymore. */
- atomic_cas_ptr((void **)&preview_taskdata->preview, preview, NULL);
- BLI_thread_queue_push(cache->previews_done, preview);
+ if (done) {
+ /* That way task freeing function won't free th preview, since it does not own it anymore. */
+ atomic_cas_ptr((void **)&preview_taskdata->preview, preview, NULL);
+ BLI_thread_queue_push(cache->previews_done, preview);
+ }
// printf("%s: End (%d)...\n", __func__, threadid);
}
@@ -1315,8 +1541,8 @@ static void filelist_cache_preview_freef(TaskPool *__restrict UNUSED(pool), void
/* preview_taskdata->preview is atomically set to NULL once preview has been processed and sent
* to previews_done queue. */
if (preview != NULL) {
- if (preview->img) {
- IMB_freeImBuf(preview->img);
+ if (preview->icon_id) {
+ BKE_icon_delete(preview->icon_id);
}
MEM_freeN(preview);
}
@@ -1342,8 +1568,8 @@ static void filelist_cache_previews_clear(FileListEntryCache *cache)
while ((preview = BLI_thread_queue_pop_timeout(cache->previews_done, 0))) {
// printf("%s: DONE %d - %s - %p\n", __func__, preview->index, preview->path,
// preview->img);
- if (preview->img) {
- IMB_freeImBuf(preview->img);
+ if (preview->icon_id) {
+ BKE_icon_delete(preview->icon_id);
}
MEM_freeN(preview);
}
@@ -1374,10 +1600,11 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry
BLI_assert(cache->flags & FLC_PREVIEWS_ACTIVE);
- if (!entry->image && !(entry->flags & FILE_ENTRY_INVALID_PREVIEW) &&
+ if (!entry->preview_icon_id && !(entry->flags & FILE_ENTRY_INVALID_PREVIEW) &&
(entry->typeflag & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT |
FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB))) {
FileListEntryPreview *preview = MEM_mallocN(sizeof(*preview), __func__);
+ FileListInternEntry *intern_entry = filelist->filelist_intern.filtered[index];
if (entry->redirection_path) {
BLI_strncpy(preview->path, entry->redirection_path, FILE_MAXDIR);
@@ -1389,7 +1616,8 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry
preview->index = index;
preview->flags = entry->typeflag;
- preview->img = NULL;
+ preview->in_memory_preview = intern_entry->local_data.preview_image;
+ preview->icon_id = 0;
// printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img);
filelist_cache_preview_ensure_running(cache);
@@ -1497,25 +1725,45 @@ FileList *filelist_new(short type)
p->selection_state = BLI_ghash_new(
BLI_ghashutil_uinthash_v4_p, BLI_ghashutil_uinthash_v4_cmp, __func__);
+ p->filelist.nbr_entries = FILEDIR_NBR_ENTRIES_UNSET;
+ filelist_settype(p, type);
- switch (type) {
+ return p;
+}
+
+void filelist_settype(FileList *filelist, short type)
+{
+ if (filelist->type == type) {
+ return;
+ }
+
+ filelist->type = type;
+ filelist->tags = 0;
+ switch (filelist->type) {
case FILE_MAIN:
- p->checkdirf = filelist_checkdir_main;
- p->read_jobf = filelist_readjob_main;
- p->filterf = is_filtered_main;
+ filelist->checkdirf = filelist_checkdir_main;
+ filelist->read_jobf = filelist_readjob_main;
+ filelist->filterf = is_filtered_main;
break;
case FILE_LOADLIB:
- p->checkdirf = filelist_checkdir_lib;
- p->read_jobf = filelist_readjob_lib;
- p->filterf = is_filtered_lib;
+ filelist->checkdirf = filelist_checkdir_lib;
+ filelist->read_jobf = filelist_readjob_lib;
+ filelist->filterf = is_filtered_lib;
+ break;
+ case FILE_MAIN_ASSET:
+ filelist->checkdirf = filelist_checkdir_main_assets;
+ filelist->read_jobf = filelist_readjob_main_assets;
+ filelist->filterf = is_filtered_main_assets;
+ filelist->tags |= FILELIST_TAGS_USES_MAIN_DATA | FILELIST_TAGS_NO_THREADS;
break;
default:
- p->checkdirf = filelist_checkdir_dir;
- p->read_jobf = filelist_readjob_dir;
- p->filterf = is_filtered_file;
+ filelist->checkdirf = filelist_checkdir_dir;
+ filelist->read_jobf = filelist_readjob_dir;
+ filelist->filterf = is_filtered_file;
break;
}
- return p;
+
+ filelist->flags |= FL_FORCE_RESET;
}
void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const bool do_selection)
@@ -1560,6 +1808,8 @@ void filelist_free(struct FileList *filelist)
filelist->selection_state = NULL;
}
+ MEM_SAFE_FREE(filelist->asset_library);
+
memset(&filelist->filter_data, 0, sizeof(filelist->filter_data));
filelist->flags &= ~(FL_NEED_SORTING | FL_NEED_FILTERING);
@@ -1580,7 +1830,7 @@ BlendHandle *filelist_lib(struct FileList *filelist)
static const char *fileentry_uiname(const char *root,
const char *relpath,
- const int typeflag,
+ const eFileSel_File_Types typeflag,
char *buff)
{
char *name = NULL;
@@ -1624,11 +1874,12 @@ bool filelist_is_dir(struct FileList *filelist, const char *path)
*/
void filelist_setdir(struct FileList *filelist, char *r_dir)
{
+ const bool allow_invalid = filelist->asset_library != NULL;
BLI_assert(strlen(r_dir) < FILE_MAX_LIBEXTRA);
BLI_path_normalize_dir(BKE_main_blendfile_path_from_global(), r_dir);
- const bool is_valid_path = filelist->checkdirf(filelist, r_dir, true);
- BLI_assert(is_valid_path);
+ const bool is_valid_path = filelist->checkdirf(filelist, r_dir, !allow_invalid);
+ BLI_assert(is_valid_path || allow_invalid);
UNUSED_VARS_NDEBUG(is_valid_path);
if (!STREQ(filelist->filelist.root, r_dir)) {
@@ -1645,11 +1896,16 @@ void filelist_setrecursion(struct FileList *filelist, const int recursion_level)
}
}
-bool filelist_force_reset(struct FileList *filelist)
+bool filelist_needs_force_reset(FileList *filelist)
{
return (filelist->flags & FL_FORCE_RESET) != 0;
}
+void filelist_tag_force_reset(FileList *filelist)
+{
+ filelist->flags |= FL_FORCE_RESET;
+}
+
bool filelist_is_ready(struct FileList *filelist)
{
return (filelist->flags & FL_IS_READY) != 0;
@@ -1660,6 +1916,11 @@ bool filelist_pending(struct FileList *filelist)
return (filelist->flags & FL_IS_PENDING) != 0;
}
+bool filelist_needs_reset_on_main_changes(const FileList *filelist)
+{
+ return (filelist->tags & FILELIST_TAGS_USES_MAIN_DATA) != 0;
+}
+
/**
* Limited version of full update done by space_file's file_refresh(),
* to be used by operators and such.
@@ -1668,7 +1929,7 @@ bool filelist_pending(struct FileList *filelist)
*/
int filelist_files_ensure(FileList *filelist)
{
- if (!filelist_force_reset(filelist) || !filelist_empty(filelist)) {
+ if (!filelist_needs_force_reset(filelist) || !filelist_needs_reading(filelist)) {
filelist_sort(filelist);
filelist_filter(filelist);
}
@@ -1701,6 +1962,17 @@ static FileDirEntry *filelist_file_create_entry(FileList *filelist, const int in
if (entry->redirection_path) {
ret->redirection_path = BLI_strdup(entry->redirection_path);
}
+ ret->id = entry->local_data.id;
+ ret->asset_data = entry->imported_asset_data ? entry->imported_asset_data : NULL;
+ if (ret->id && (ret->asset_data == NULL)) {
+ ret->asset_data = ret->id->asset_data;
+ }
+ /* For some file types the preview is already available. */
+ if (entry->local_data.preview_image &&
+ BKE_previewimg_is_finished(entry->local_data.preview_image, ICON_SIZE_PREVIEW)) {
+ ImBuf *ibuf = BKE_previewimg_to_imbuf(entry->local_data.preview_image, ICON_SIZE_PREVIEW);
+ ret->preview_icon_id = BKE_icon_imbuf_create(ibuf);
+ }
BLI_addtail(&cache->cached_entries, ret);
return ret;
}
@@ -1770,7 +2042,7 @@ int filelist_file_findpath(struct FileList *filelist, const char *filename)
{
int fidx = -1;
- if (filelist->filelist.nbr_entries_filtered < 0) {
+ if (filelist->filelist.nbr_entries_filtered == FILEDIR_NBR_ENTRIES_UNSET) {
return fidx;
}
@@ -1788,9 +2060,17 @@ int filelist_file_findpath(struct FileList *filelist, const char *filename)
return -1;
}
+/**
+ * Get the ID a file represents (if any). For #FILE_MAIN, #FILE_MAIN_ASSET.
+ */
+ID *filelist_file_get_id(const FileDirEntry *file)
+{
+ return file->id;
+}
+
FileDirEntry *filelist_entry_find_uuid(struct FileList *filelist, const int uuid[4])
{
- if (filelist->filelist.nbr_entries_filtered < 0) {
+ if (filelist->filelist.nbr_entries_filtered == FILEDIR_NBR_ENTRIES_UNSET) {
return NULL;
}
@@ -2147,15 +2427,17 @@ bool filelist_cache_previews_update(FileList *filelist)
// printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img);
- if (preview->img) {
+ if (preview->icon_id) {
/* Due to asynchronous process, a preview for a given image may be generated several times,
* i.e. entry->image may already be set at this point. */
- if (entry && !entry->image) {
- entry->image = preview->img;
+ if (entry && !entry->preview_icon_id) {
+ /* Move ownership over icon. */
+ entry->preview_icon_id = preview->icon_id;
+ preview->icon_id = 0;
changed = true;
}
else {
- IMB_freeImBuf(preview->img);
+ BKE_icon_delete(preview->icon_id);
}
}
else if (entry) {
@@ -2313,9 +2595,9 @@ int ED_file_extension_icon(const char *path)
}
}
-int filelist_empty(struct FileList *filelist)
+int filelist_needs_reading(struct FileList *filelist)
{
- return (filelist->filelist.nbr_entries == 0);
+ return (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET);
}
uint filelist_entry_select_set(const FileList *filelist,
@@ -2564,8 +2846,8 @@ static int filelist_readjob_list_dir(const char *root,
static int filelist_readjob_list_lib(const char *root, ListBase *entries, const bool skip_currpar)
{
FileListInternEntry *entry;
- LinkNode *ln, *names;
- int i, nnames, idcode = 0, nbr_entries = 0;
+ LinkNode *ln, *names = NULL, *datablock_infos = NULL;
+ int i, nitems, idcode = 0, nbr_entries = 0;
char dir[FILE_MAX_LIBEXTRA], *group;
bool ok;
@@ -2587,11 +2869,11 @@ static int filelist_readjob_list_lib(const char *root, ListBase *entries, const
* and freed in filelist_entry_free. */
if (group) {
idcode = groupname_to_code(group);
- names = BLO_blendhandle_get_datablock_names(libfiledata, idcode, &nnames);
+ datablock_infos = BLO_blendhandle_get_datablock_info(libfiledata, idcode, &nitems);
}
else {
names = BLO_blendhandle_get_linkable_groups(libfiledata);
- nnames = BLI_linklist_count(names);
+ nitems = BLI_linklist_count(names);
}
BLO_blendhandle_close(libfiledata);
@@ -2604,12 +2886,18 @@ static int filelist_readjob_list_lib(const char *root, ListBase *entries, const
nbr_entries++;
}
- for (i = 0, ln = names; i < nnames; i++, ln = ln->next) {
- const char *blockname = ln->link;
+ for (i = 0, ln = (datablock_infos ? datablock_infos : names); i < nitems; i++, ln = ln->next) {
+ struct BLODataBlockInfo *info = datablock_infos ? ln->link : NULL;
+ const char *blockname = info ? info->name : ln->link;
entry = MEM_callocN(sizeof(*entry), __func__);
entry->relpath = BLI_strdup(blockname);
entry->typeflag |= FILE_TYPE_BLENDERLIB;
+ if (info && info->asset_data) {
+ entry->typeflag |= FILE_TYPE_ASSET;
+ /* Moves ownership! */
+ entry->imported_asset_data = info->asset_data;
+ }
if (!(group && idcode)) {
entry->typeflag |= FILE_TYPE_DIR;
entry->blentype = groupname_to_code(blockname);
@@ -2621,7 +2909,7 @@ static int filelist_readjob_list_lib(const char *root, ListBase *entries, const
nbr_entries++;
}
- BLI_linklist_freeN(names);
+ BLI_linklist_freeN(datablock_infos ? datablock_infos : names);
return nbr_entries;
}
@@ -2820,7 +3108,10 @@ static void filelist_readjob_do(const bool do_lib,
// BLI_assert(filelist->filtered == NULL);
BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) &&
- (filelist->filelist.nbr_entries == 0));
+ (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET));
+
+ /* A valid, but empty directory from now. */
+ filelist->filelist.nbr_entries = 0;
todo_dirs = BLI_stack_new(sizeof(*td_dir), __func__);
td_dir = BLI_stack_push_r(todo_dirs);
@@ -2932,7 +3223,8 @@ static void filelist_readjob_do(const bool do_lib,
BLI_stack_free(todo_dirs);
}
-static void filelist_readjob_dir(FileList *filelist,
+static void filelist_readjob_dir(Main *UNUSED(current_main),
+ FileList *filelist,
const char *main_name,
short *stop,
short *do_update,
@@ -2942,7 +3234,8 @@ static void filelist_readjob_dir(FileList *filelist,
filelist_readjob_do(false, filelist, main_name, stop, do_update, progress, lock);
}
-static void filelist_readjob_lib(FileList *filelist,
+static void filelist_readjob_lib(Main *UNUSED(current_main),
+ FileList *filelist,
const char *main_name,
short *stop,
short *do_update,
@@ -2952,7 +3245,8 @@ static void filelist_readjob_lib(FileList *filelist,
filelist_readjob_do(true, filelist, main_name, stop, do_update, progress, lock);
}
-static void filelist_readjob_main(FileList *filelist,
+static void filelist_readjob_main(Main *current_main,
+ FileList *filelist,
const char *main_name,
short *stop,
short *do_update,
@@ -2960,12 +3254,67 @@ static void filelist_readjob_main(FileList *filelist,
ThreadMutex *lock)
{
/* TODO! */
- filelist_readjob_dir(filelist, main_name, stop, do_update, progress, lock);
+ filelist_readjob_dir(current_main, filelist, main_name, stop, do_update, progress, lock);
+}
+
+/**
+ * \warning Acts on main, so NOT thread-safe!
+ */
+static void filelist_readjob_main_assets(Main *current_main,
+ FileList *filelist,
+ const char *UNUSED(main_name),
+ short *UNUSED(stop),
+ short *do_update,
+ float *UNUSED(progress),
+ ThreadMutex *UNUSED(lock))
+{
+ BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) &&
+ (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET));
+
+ /* A valid, but empty directory from now. */
+ filelist->filelist.nbr_entries = 0;
+
+ FileListInternEntry *entry;
+ ListBase tmp_entries = {0};
+ ID *id_iter;
+ int nbr_entries = 0;
+
+ FOREACH_MAIN_ID_BEGIN (current_main, id_iter) {
+ if (!id_iter->asset_data) {
+ continue;
+ }
+
+ const char *id_code_name = BKE_idtype_idcode_to_name(GS(id_iter->name));
+
+ entry = MEM_callocN(sizeof(*entry), __func__);
+ entry->relpath = BLI_strdup(id_code_name);
+ entry->name = BLI_strdup(id_iter->name + 2);
+ entry->typeflag |= FILE_TYPE_BLENDERLIB | FILE_TYPE_ASSET;
+ entry->blentype = GS(id_iter->name);
+ *((uint32_t *)entry->uuid) = atomic_add_and_fetch_uint32(
+ (uint32_t *)filelist->filelist_intern.curr_uuid, 1);
+ entry->local_data.preview_image = BKE_asset_metadata_preview_get_from_id(id_iter->asset_data,
+ id_iter);
+ entry->local_data.id = id_iter;
+ nbr_entries++;
+ BLI_addtail(&tmp_entries, entry);
+ }
+ FOREACH_MAIN_ID_END;
+
+ if (nbr_entries) {
+ *do_update = true;
+
+ BLI_movelisttolist(&filelist->filelist.entries, &tmp_entries);
+ filelist->filelist.nbr_entries += nbr_entries;
+ filelist->filelist.nbr_entries_filtered = filelist->filelist.entry_idx_start =
+ filelist->filelist.entry_idx_end = -1;
+ }
}
typedef struct FileListReadJob {
ThreadMutex lock;
char main_name[FILE_MAX];
+ Main *current_main;
struct FileList *filelist;
/** XXX We may use a simpler struct here... just a linked list and root path? */
struct FileList *tmp_filelist;
@@ -2985,7 +3334,7 @@ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update
flrj->tmp_filelist = MEM_dupallocN(flrj->filelist);
BLI_listbase_clear(&flrj->tmp_filelist->filelist.entries);
- flrj->tmp_filelist->filelist.nbr_entries = 0;
+ flrj->tmp_filelist->filelist.nbr_entries = FILEDIR_NBR_ENTRIES_UNSET;
flrj->tmp_filelist->filelist_intern.filtered = NULL;
BLI_listbase_clear(&flrj->tmp_filelist->filelist_intern.entries);
@@ -2996,11 +3345,17 @@ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update
flrj->tmp_filelist->libfiledata = NULL;
memset(&flrj->tmp_filelist->filelist_cache, 0, sizeof(flrj->tmp_filelist->filelist_cache));
flrj->tmp_filelist->selection_state = NULL;
+ flrj->tmp_filelist->asset_library = NULL;
BLI_mutex_unlock(&flrj->lock);
- flrj->tmp_filelist->read_jobf(
- flrj->tmp_filelist, flrj->main_name, stop, do_update, progress, &flrj->lock);
+ flrj->tmp_filelist->read_jobf(flrj->current_main,
+ flrj->tmp_filelist,
+ flrj->main_name,
+ stop,
+ do_update,
+ progress,
+ &flrj->lock);
}
static void filelist_readjob_update(void *flrjv)
@@ -3015,7 +3370,7 @@ static void filelist_readjob_update(void *flrjv)
BLI_mutex_lock(&flrj->lock);
- if (flrj->tmp_filelist->filelist.nbr_entries) {
+ if (flrj->tmp_filelist->filelist.nbr_entries > 0) {
/* We just move everything out of 'thread context' into final list. */
new_nbr_entries = flrj->tmp_filelist->filelist.nbr_entries;
BLI_movelisttolist(&new_entries, &flrj->tmp_filelist->filelist.entries);
@@ -3033,7 +3388,7 @@ static void filelist_readjob_update(void *flrjv)
/* if no new_nbr_entries, this is NOP */
BLI_movelisttolist(&fl_intern->entries, &new_entries);
- flrj->filelist->filelist.nbr_entries = nbr_entries + new_nbr_entries;
+ flrj->filelist->filelist.nbr_entries = MAX2(nbr_entries, 0) + new_nbr_entries;
}
static void filelist_readjob_endjob(void *flrjv)
@@ -3074,16 +3429,36 @@ void filelist_readjob_start(FileList *filelist, const bContext *C)
wmJob *wm_job;
FileListReadJob *flrj;
+ if (!filelist_is_dir(filelist, filelist->filelist.root)) {
+ return;
+ }
+
/* prepare job data */
flrj = MEM_callocN(sizeof(*flrj), __func__);
flrj->filelist = filelist;
+ flrj->current_main = bmain;
BLI_strncpy(flrj->main_name, BKE_main_blendfile_path(bmain), sizeof(flrj->main_name));
filelist->flags &= ~(FL_FORCE_RESET | FL_IS_READY);
filelist->flags |= FL_IS_PENDING;
+ /* Init even for single threaded execution. Called functions use it. */
BLI_mutex_init(&flrj->lock);
+ if (filelist->tags & FILELIST_TAGS_NO_THREADS) {
+ short dummy_stop = false;
+ short dummy_do_update = false;
+ float dummy_progress = 0.0f;
+
+ /* Single threaded execution. Just directly call the callbacks. */
+ filelist_readjob_startjob(flrj, &dummy_stop, &dummy_do_update, &dummy_progress);
+ filelist_readjob_endjob(flrj);
+ filelist_readjob_free(flrj);
+
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL);
+ return;
+ }
+
/* setup job */
wm_job = WM_jobs_get(CTX_wm_manager(C),
CTX_wm_window(C),
diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h
index 4b8cc052382..16984bb6e43 100644
--- a/source/blender/editors/space_file/filelist.h
+++ b/source/blender/editors/space_file/filelist.h
@@ -29,6 +29,7 @@ extern "C" {
struct BlendHandle;
struct FileList;
+struct FileSelectAssetLibraryUID;
struct FileSelection;
struct wmWindowManager;
@@ -46,14 +47,16 @@ typedef enum FileCheckType {
CHECK_ALL = 3,
} FileCheckType;
-struct ListBase *folderlist_new(void);
void folderlist_free(struct ListBase *folderlist);
-struct ListBase *folderlist_duplicate(ListBase *folderlist);
void folderlist_popdir(struct ListBase *folderlist, char *dir);
void folderlist_pushdir(struct ListBase *folderlist, const char *dir);
const char *folderlist_peeklastdir(struct ListBase *folderlist);
int folderlist_clear_next(struct SpaceFile *sfile);
+void folder_history_list_ensure_for_active_browse_mode(struct SpaceFile *sfile);
+void folder_history_list_free(struct SpaceFile *sfile);
+struct ListBase folder_history_list_duplicate(struct ListBase *listbase);
+
void filelist_setsorting(struct FileList *filelist, const short sort, bool invert_sort);
void filelist_sort(struct FileList *filelist);
@@ -63,17 +66,22 @@ void filelist_setfilter_options(struct FileList *filelist,
const bool hide_parent,
const uint64_t filter,
const uint64_t filter_id,
+ const bool filter_assets_only,
const char *filter_glob,
const char *filter_search);
void filelist_filter(struct FileList *filelist);
+void filelist_setlibrary(struct FileList *filelist,
+ const struct FileSelectAssetLibraryUID *asset_library);
void filelist_init_icons(void);
void filelist_free_icons(void);
struct ImBuf *filelist_getimage(struct FileList *filelist, const int index);
+struct ImBuf *filelist_file_getimage(const FileDirEntry *file);
struct ImBuf *filelist_geticon_image(struct FileList *filelist, const int index);
int filelist_geticon(struct FileList *filelist, const int index, const bool is_main);
struct FileList *filelist_new(short type);
+void filelist_settype(struct FileList *filelist, short type);
void filelist_clear(struct FileList *filelist);
void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const bool do_selection);
void filelist_free(struct FileList *filelist);
@@ -83,15 +91,18 @@ bool filelist_is_dir(struct FileList *filelist, const char *path);
void filelist_setdir(struct FileList *filelist, char *r_dir);
int filelist_files_ensure(struct FileList *filelist);
-int filelist_empty(struct FileList *filelist);
+int filelist_needs_reading(struct FileList *filelist);
FileDirEntry *filelist_file(struct FileList *filelist, int index);
int filelist_file_findpath(struct FileList *filelist, const char *file);
+struct ID *filelist_file_get_id(const struct FileDirEntry *file);
FileDirEntry *filelist_entry_find_uuid(struct FileList *filelist, const int uuid[4]);
void filelist_file_cache_slidingwindow_set(struct FileList *filelist, size_t window_size);
bool filelist_file_cache_block(struct FileList *filelist, const int index);
-bool filelist_force_reset(struct FileList *filelist);
+bool filelist_needs_force_reset(struct FileList *filelist);
+void filelist_tag_force_reset(struct FileList *filelist);
bool filelist_pending(struct FileList *filelist);
+bool filelist_needs_reset_on_main_changes(const struct FileList *filelist);
bool filelist_is_ready(struct FileList *filelist);
unsigned int filelist_entry_select_set(const struct FileList *filelist,
diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c
index 6e933e53a8f..b919a30e6cd 100644
--- a/source/blender/editors/space_file/filesel.c
+++ b/source/blender/editors/space_file/filesel.c
@@ -56,7 +56,9 @@
#include "BKE_appdir.h"
#include "BKE_context.h"
+#include "BKE_idtype.h"
#include "BKE_main.h"
+#include "BKE_preferences.h"
#include "BLF_api.h"
@@ -77,14 +79,67 @@
#define VERTLIST_MAJORCOLUMN_WIDTH (25 * UI_UNIT_X)
-FileSelectParams *ED_fileselect_get_active_params(const SpaceFile *sfile)
+static void fileselect_initialize_params_common(SpaceFile *sfile, FileSelectParams *params)
{
- if (!sfile) {
- /* Sometimes called in poll before space type was checked. */
- return NULL;
+ const char *blendfile_path = BKE_main_blendfile_path_from_global();
+
+ /* operator has no setting for this */
+ params->active_file = -1;
+
+ if (!params->dir[0]) {
+ if (blendfile_path[0] != '\0') {
+ BLI_split_dir_part(blendfile_path, params->dir, sizeof(params->dir));
+ }
+ else {
+ const char *doc_path = BKE_appdir_folder_default();
+ if (doc_path) {
+ BLI_strncpy(params->dir, doc_path, sizeof(params->dir));
+ }
+ }
}
- return sfile->params;
+ folder_history_list_ensure_for_active_browse_mode(sfile);
+ folderlist_pushdir(sfile->folders_prev, params->dir);
+
+ /* Switching thumbnails needs to recalc layout T28809. */
+ if (sfile->layout) {
+ sfile->layout->dirty = true;
+ }
+}
+
+static void fileselect_ensure_updated_asset_params(SpaceFile *sfile)
+{
+ BLI_assert(sfile->browse_mode == FILE_BROWSE_MODE_ASSETS);
+ BLI_assert(sfile->op == NULL);
+
+ FileAssetSelectParams *asset_params = sfile->asset_params;
+
+ if (!asset_params) {
+ asset_params = sfile->asset_params = MEM_callocN(sizeof(*asset_params),
+ "FileAssetSelectParams");
+ asset_params->base_params.details_flags = U_default.file_space_data.details_flags;
+ asset_params->asset_library.type = FILE_ASSET_LIBRARY_LOCAL;
+ asset_params->asset_library.custom_library_index = -1;
+ }
+
+ FileSelectParams *base_params = &asset_params->base_params;
+ base_params->file[0] = '\0';
+ base_params->filter_glob[0] = '\0';
+ /* TODO this way of using filters to form categories is notably slower than specifying a
+ * "group" to read. That's because all types are read and filtering is applied afterwards. Would
+ * be nice if we could lazy-read individual groups. */
+ base_params->flag |= U_default.file_space_data.flag | FILE_ASSETS_ONLY | FILE_FILTER;
+ base_params->flag &= ~FILE_DIRSEL_ONLY;
+ base_params->filter |= FILE_TYPE_BLENDERLIB;
+ base_params->filter_id = FILTER_ID_OB | FILTER_ID_GR;
+ base_params->display = FILE_IMGDISPLAY;
+ base_params->sort = FILE_SORT_ALPHA;
+ base_params->recursion_level = 1;
+ /* 'SMALL' size by default. More reasonable since this is typically used as regular editor,
+ * space is more of an issue here. */
+ base_params->thumbnail_size = 96;
+
+ fileselect_initialize_params_common(sfile, base_params);
}
/**
@@ -92,6 +147,8 @@ FileSelectParams *ED_fileselect_get_active_params(const SpaceFile *sfile)
* the previously used settings to be used here rather than overriding them */
static FileSelectParams *fileselect_ensure_updated_file_params(SpaceFile *sfile)
{
+ BLI_assert(sfile->browse_mode == FILE_BROWSE_MODE_FILES);
+
FileSelectParams *params;
wmOperator *op = sfile->op;
@@ -297,42 +354,104 @@ static FileSelectParams *fileselect_ensure_updated_file_params(SpaceFile *sfile)
params->filter_glob[0] = '\0';
}
- /* operator has no setting for this */
- params->active_file = -1;
+ fileselect_initialize_params_common(sfile, params);
- /* initialize the list with previous folders */
- if (!sfile->folders_prev) {
- sfile->folders_prev = folderlist_new();
- }
+ return params;
+}
- if (!params->dir[0]) {
- if (blendfile_path[0] != '\0') {
- BLI_split_dir_part(blendfile_path, params->dir, sizeof(params->dir));
- }
- else {
- const char *doc_path = BKE_appdir_folder_default();
- if (doc_path) {
- BLI_strncpy(params->dir, doc_path, sizeof(params->dir));
+/**
+ * If needed, create and return the file select parameters for the active browse mode.
+ */
+FileSelectParams *ED_fileselect_ensure_active_params(SpaceFile *sfile)
+{
+ switch ((eFileBrowse_Mode)sfile->browse_mode) {
+ case FILE_BROWSE_MODE_FILES:
+ if (!sfile->params) {
+ fileselect_ensure_updated_file_params(sfile);
}
- }
+ return sfile->params;
+ case FILE_BROWSE_MODE_ASSETS:
+ if (!sfile->asset_params) {
+ fileselect_ensure_updated_asset_params(sfile);
+ }
+ return &sfile->asset_params->base_params;
}
- folderlist_pushdir(sfile->folders_prev, params->dir);
+ BLI_assert(!"Invalid browse mode set in file space.");
+ return NULL;
+}
- /* Switching thumbnails needs to recalc layout T28809. */
- if (sfile->layout) {
- sfile->layout->dirty = true;
+/**
+ * Get the file select parameters for the active browse mode.
+ */
+FileSelectParams *ED_fileselect_get_active_params(const SpaceFile *sfile)
+{
+ if (!sfile) {
+ /* Sometimes called in poll before space type was checked. */
+ return NULL;
}
- return params;
+ switch ((eFileBrowse_Mode)sfile->browse_mode) {
+ case FILE_BROWSE_MODE_FILES:
+ return sfile->params;
+ case FILE_BROWSE_MODE_ASSETS:
+ return (FileSelectParams *)sfile->asset_params;
+ }
+
+ BLI_assert(!"Invalid browse mode set in file space.");
+ return NULL;
}
-FileSelectParams *ED_fileselect_ensure_active_params(SpaceFile *sfile)
+FileSelectParams *ED_fileselect_get_file_params(const SpaceFile *sfile)
{
- if (!sfile->params) {
- fileselect_ensure_updated_file_params(sfile);
+ return (sfile->browse_mode == FILE_BROWSE_MODE_FILES) ? sfile->params : NULL;
+}
+
+FileAssetSelectParams *ED_fileselect_get_asset_params(const SpaceFile *sfile)
+{
+ return (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS) ? sfile->asset_params : NULL;
+}
+
+static void fileselect_refresh_asset_params(FileAssetSelectParams *asset_params)
+{
+ FileSelectAssetLibraryUID *library = &asset_params->asset_library;
+ FileSelectParams *base_params = &asset_params->base_params;
+ bUserAssetLibrary *user_library = NULL;
+
+ /* Ensure valid repo, or fall-back to local one. */
+ if (library->type == FILE_ASSET_LIBRARY_CUSTOM) {
+ BLI_assert(library->custom_library_index >= 0);
+
+ user_library = BKE_preferences_asset_library_find_from_index(&U,
+ library->custom_library_index);
+ if (!user_library) {
+ library->type = FILE_ASSET_LIBRARY_LOCAL;
+ }
+ }
+
+ switch (library->type) {
+ case FILE_ASSET_LIBRARY_LOCAL:
+ base_params->dir[0] = '\0';
+ break;
+ case FILE_ASSET_LIBRARY_CUSTOM:
+ BLI_assert(user_library);
+ BLI_strncpy(base_params->dir, user_library->path, sizeof(base_params->dir));
+ break;
}
- return sfile->params;
+ base_params->type = (library->type == FILE_ASSET_LIBRARY_LOCAL) ? FILE_MAIN_ASSET : FILE_LOADLIB;
+}
+
+void fileselect_refresh_params(SpaceFile *sfile)
+{
+ FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
+ if (asset_params) {
+ fileselect_refresh_asset_params(asset_params);
+ }
+}
+
+bool ED_fileselect_is_asset_browser(const SpaceFile *sfile)
+{
+ return (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS);
}
/* The subset of FileSelectParams.flag items we store into preferences. Note that FILE_SORT_ALPHA
@@ -371,6 +490,8 @@ void ED_fileselect_set_params_from_userdef(SpaceFile *sfile)
wmOperator *op = sfile->op;
UserDef_FileSpaceData *sfile_udata = &U.file_space_data;
+ sfile->browse_mode = FILE_BROWSE_MODE_FILES;
+
FileSelectParams *params = fileselect_ensure_updated_file_params(sfile);
if (!op) {
return;
@@ -438,15 +559,6 @@ void ED_fileselect_params_to_userdef(SpaceFile *sfile,
}
}
-void ED_fileselect_reset_params(SpaceFile *sfile)
-{
- FileSelectParams *params = ED_fileselect_get_active_params(sfile);
- params->type = FILE_UNIX;
- params->flag = 0;
- params->title[0] = '\0';
- params->active_file = -1;
-}
-
/**
* Sets FileSelectParams->file (name of selected file)
*/
@@ -1046,8 +1158,7 @@ void ED_fileselect_exit(wmWindowManager *wm, Scene *owner_scene, SpaceFile *sfil
sfile->op = NULL;
}
- folderlist_free(sfile->folders_prev);
- folderlist_free(sfile->folders_next);
+ folder_history_list_free(sfile);
if (sfile->files) {
ED_fileselect_clear(wm, owner_scene, sfile);
diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c
index c72ca58abba..774dc54700c 100644
--- a/source/blender/editors/space_file/space_file.c
+++ b/source/blender/editors/space_file/space_file.c
@@ -35,6 +35,8 @@
#include "BKE_screen.h"
#include "RNA_access.h"
+#include "RNA_define.h"
+#include "RNA_enum_types.h"
#include "WM_api.h"
#include "WM_message.h"
@@ -55,6 +57,23 @@
#include "filelist.h"
#include "fsmenu.h"
+static ARegion *file_ui_region_ensure(ScrArea *area, ARegion *region_prev)
+{
+ ARegion *region;
+
+ if ((region = BKE_area_find_region_type(area, RGN_TYPE_UI)) != NULL) {
+ return region;
+ }
+
+ region = MEM_callocN(sizeof(ARegion), "execute region for file");
+ BLI_insertlinkafter(&area->regionbase, region_prev, region);
+ region->regiontype = RGN_TYPE_UI;
+ region->alignment = RGN_ALIGN_TOP;
+ region->flag = RGN_FLAG_DYNAMIC_SIZE;
+
+ return region;
+}
+
static ARegion *file_execute_region_ensure(ScrArea *area, ARegion *region_prev)
{
ARegion *region;
@@ -149,22 +168,10 @@ static void file_free(SpaceLink *sl)
sfile->files = NULL;
}
- if (sfile->folders_prev) {
- folderlist_free(sfile->folders_prev);
- MEM_freeN(sfile->folders_prev);
- sfile->folders_prev = NULL;
- }
+ folder_history_list_free(sfile);
- if (sfile->folders_next) {
- folderlist_free(sfile->folders_next);
- MEM_freeN(sfile->folders_next);
- sfile->folders_next = NULL;
- }
-
- if (sfile->params) {
- MEM_freeN(sfile->params);
- sfile->params = NULL;
- }
+ MEM_SAFE_FREE(sfile->params);
+ MEM_SAFE_FREE(sfile->asset_params);
if (sfile->layout) {
MEM_freeN(sfile->layout);
@@ -205,19 +212,20 @@ static SpaceLink *file_duplicate(SpaceLink *sl)
sfilen->previews_timer = NULL;
sfilen->smoothscroll_timer = NULL;
+ FileSelectParams *active_params_old = ED_fileselect_get_active_params(sfileo);
+ if (active_params_old) {
+ sfilen->files = filelist_new(active_params_old->type);
+ filelist_setdir(sfilen->files, active_params_old->dir);
+ }
+
if (sfileo->params) {
- sfilen->files = filelist_new(sfileo->params->type);
sfilen->params = MEM_dupallocN(sfileo->params);
- filelist_setdir(sfilen->files, sfilen->params->dir);
}
-
- if (sfileo->folders_prev) {
- sfilen->folders_prev = folderlist_duplicate(sfileo->folders_prev);
+ if (sfileo->asset_params) {
+ sfilen->asset_params = MEM_dupallocN(sfileo->asset_params);
}
- if (sfileo->folders_next) {
- sfilen->folders_next = folderlist_duplicate(sfileo->folders_next);
- }
+ sfilen->folder_histories = folder_history_list_duplicate(&sfileo->folder_histories);
if (sfileo->layout) {
sfilen->layout = MEM_dupallocN(sfileo->layout);
@@ -232,15 +240,30 @@ static void file_ensure_valid_region_state(bContext *C,
SpaceFile *sfile,
FileSelectParams *params)
{
- ARegion *region_ui = BKE_area_find_region_type(area, RGN_TYPE_UI);
- ARegion *region_props = BKE_area_find_region_type(area, RGN_TYPE_TOOL_PROPS);
- ARegion *region_execute = BKE_area_find_region_type(area, RGN_TYPE_EXECUTE);
+ ARegion *region_tools = BKE_area_find_region_type(area, RGN_TYPE_TOOLS);
bool needs_init = false; /* To avoid multiple ED_area_init() calls. */
+ BLI_assert(region_tools);
+
+ if (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS) {
+ ARegion *region_execute = file_execute_region_ensure(area, region_tools);
+ ARegion *region_props = file_tool_props_region_ensure(area, region_execute);
+
+ /* Hide specific regions by default. */
+ region_props->flag |= RGN_FLAG_HIDDEN;
+ region_execute->flag |= RGN_FLAG_HIDDEN;
+
+ ARegion *region_ui = BKE_area_find_region_type(area, RGN_TYPE_UI);
+ if (region_ui) {
+ ED_region_remove(C, area, region_ui);
+ needs_init = true;
+ }
+ }
/* If there's an file-operation, ensure we have the option and execute region */
- if (sfile->op && (region_props == NULL)) {
- region_execute = file_execute_region_ensure(area, region_ui);
- region_props = file_tool_props_region_ensure(area, region_execute);
+ else if (sfile->op) {
+ ARegion *region_ui = file_ui_region_ensure(area, region_tools);
+ ARegion *region_execute = file_execute_region_ensure(area, region_ui);
+ ARegion *region_props = file_tool_props_region_ensure(area, region_execute);
if (params->flag & FILE_HIDE_TOOL_PROPS) {
region_props->flag |= RGN_FLAG_HIDDEN;
@@ -252,12 +275,19 @@ static void file_ensure_valid_region_state(bContext *C,
needs_init = true;
}
/* If there's _no_ file-operation, ensure we _don't_ have the option and execute region */
- else if ((sfile->op == NULL) && (region_props != NULL)) {
- BLI_assert(region_execute != NULL);
+ else {
+ ARegion *region_props = BKE_area_find_region_type(area, RGN_TYPE_TOOL_PROPS);
+ ARegion *region_execute = BKE_area_find_region_type(area, RGN_TYPE_EXECUTE);
+ ARegion *region_ui = file_ui_region_ensure(area, region_tools);
+ UNUSED_VARS(region_ui);
- ED_region_remove(C, area, region_props);
- ED_region_remove(C, area, region_execute);
- needs_init = true;
+ if (region_props) {
+ BLI_assert(region_execute);
+
+ ED_region_remove(C, area, region_props);
+ ED_region_remove(C, area, region_execute);
+ needs_init = true;
+ }
}
if (needs_init) {
@@ -265,24 +295,42 @@ static void file_ensure_valid_region_state(bContext *C,
}
}
+/**
+ * Tag the space to recreate the file-list.
+ */
+static void file_tag_reset_list(ScrArea *area, SpaceFile *sfile)
+{
+ filelist_tag_force_reset(sfile->files);
+ ED_area_tag_refresh(area);
+}
+
static void file_refresh(const bContext *C, ScrArea *area)
{
wmWindowManager *wm = CTX_wm_manager(C);
wmWindow *win = CTX_wm_window(C);
SpaceFile *sfile = CTX_wm_space_file(C);
FileSelectParams *params = ED_fileselect_ensure_active_params(sfile);
+ FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile);
struct FSMenu *fsmenu = ED_fsmenu_get();
- if (!sfile->folders_prev) {
- sfile->folders_prev = folderlist_new();
+ fileselect_refresh_params(sfile);
+ folder_history_list_ensure_for_active_browse_mode(sfile);
+
+ if (sfile->files && (sfile->tags & FILE_TAG_REBUILD_MAIN_FILES) &&
+ filelist_needs_reset_on_main_changes(sfile->files)) {
+ filelist_tag_force_reset(sfile->files);
}
+ sfile->tags &= ~FILE_TAG_REBUILD_MAIN_FILES;
+
if (!sfile->files) {
sfile->files = filelist_new(params->type);
params->highlight_file = -1; /* added this so it opens nicer (ton) */
}
+ filelist_settype(sfile->files, params->type);
filelist_setdir(sfile->files, params->dir);
filelist_setrecursion(sfile->files, params->recursion_level);
filelist_setsorting(sfile->files, params->sort, params->flag & FILE_SORT_INVERT);
+ filelist_setlibrary(sfile->files, asset_params ? &asset_params->asset_library : NULL);
filelist_setfilter_options(
sfile->files,
(params->flag & FILE_FILTER) != 0,
@@ -290,6 +338,7 @@ static void file_refresh(const bContext *C, ScrArea *area)
true, /* Just always hide parent, prefer to not add an extra user option for this. */
params->filter,
params->filter_id,
+ (params->flag & FILE_ASSETS_ONLY) != 0,
params->filter_glob,
params->filter_search);
@@ -300,12 +349,12 @@ static void file_refresh(const bContext *C, ScrArea *area)
sfile->bookmarknr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_BOOKMARKS, params->dir);
sfile->recentnr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_RECENT, params->dir);
- if (filelist_force_reset(sfile->files)) {
+ if (filelist_needs_force_reset(sfile->files)) {
filelist_readjob_stop(wm, CTX_data_scene(C));
filelist_clear(sfile->files);
}
- if (filelist_empty(sfile->files)) {
+ if (filelist_needs_reading(sfile->files)) {
if (!filelist_pending(sfile->files)) {
filelist_readjob_start(sfile->files, C);
}
@@ -363,8 +412,21 @@ static void file_listener(wmWindow *UNUSED(win),
ED_area_tag_refresh(area);
}
break;
+ case ND_SPACE_ASSET_PARAMS:
+ if (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS) {
+ ED_area_tag_refresh(area);
+ }
+ break;
}
break;
+ case NC_ASSET: {
+ if (sfile->files && filelist_needs_reset_on_main_changes(sfile->files)) {
+ /* Full refresh of the file list if local asset data was changed. Refreshing this view is
+ * cheap and users expect this to be updated immediately. */
+ file_tag_reset_list(area, sfile);
+ }
+ break;
+ }
}
}
@@ -442,6 +504,22 @@ static void file_main_region_message_subscribe(const struct bContext *UNUSED(C),
}
}
+static bool file_main_region_needs_refresh_before_draw(SpaceFile *sfile)
+{
+ /* Needed, because filelist is not initialized on loading */
+ if (!sfile->files || filelist_needs_reading(sfile->files)) {
+ return true;
+ }
+
+ /* File reading tagged the space because main data changed that may require a filelist reset. */
+ if (filelist_needs_reset_on_main_changes(sfile->files) &&
+ (sfile->tags & FILE_TAG_REBUILD_MAIN_FILES)) {
+ return true;
+ }
+
+ return false;
+}
+
static void file_main_region_draw(const bContext *C, ARegion *region)
{
/* draw entirely, view changes should be handled here */
@@ -450,8 +528,7 @@ static void file_main_region_draw(const bContext *C, ARegion *region)
View2D *v2d = &region->v2d;
- /* Needed, because filelist is not initialized on loading */
- if (!sfile->files || filelist_empty(sfile->files)) {
+ if (file_main_region_needs_refresh_before_draw(sfile)) {
file_refresh(C, NULL);
}
@@ -497,7 +574,9 @@ static void file_main_region_draw(const bContext *C, ARegion *region)
file_highlight_set(sfile, region, event->x, event->y);
}
- file_draw_list(C, region);
+ if (!file_draw_hint_if_invalid(sfile, region)) {
+ file_draw_list(C, region);
+ }
/* reset view matrix */
UI_view2d_view_restore(C);
@@ -681,6 +760,87 @@ static void file_dropboxes(void)
WM_dropbox_add(lb, "FILE_OT_filepath_drop", filepath_drop_poll, filepath_drop_copy);
}
+static int file_space_subtype_get(ScrArea *area)
+{
+ SpaceFile *sfile = area->spacedata.first;
+ return sfile->browse_mode;
+}
+
+static void file_space_subtype_set(ScrArea *area, int value)
+{
+ SpaceFile *sfile = area->spacedata.first;
+ sfile->browse_mode = value;
+}
+
+static void file_space_subtype_item_extend(bContext *UNUSED(C),
+ EnumPropertyItem **item,
+ int *totitem)
+{
+ RNA_enum_items_add(item, totitem, rna_enum_space_file_browse_mode_items);
+}
+
+static const char *file_context_dir[] = {"active_file", "active_id", NULL};
+
+static int /*eContextResult*/ file_context(const bContext *C,
+ const char *member,
+ bContextDataResult *result)
+{
+ bScreen *screen = CTX_wm_screen(C);
+ SpaceFile *sfile = CTX_wm_space_file(C);
+ FileSelectParams *params = ED_fileselect_get_active_params(sfile);
+
+ BLI_assert(!ED_area_is_global(CTX_wm_area(C)));
+
+ if (CTX_data_dir(member)) {
+ CTX_data_dir_set(result, file_context_dir);
+ return CTX_RESULT_OK;
+ }
+
+ /* The following member checks return file-list data, check if that needs refreshing first. */
+ if (file_main_region_needs_refresh_before_draw(sfile)) {
+ return CTX_RESULT_NO_DATA;
+ }
+
+ if (CTX_data_equals(member, "active_file")) {
+ FileDirEntry *file = filelist_file(sfile->files, params->active_file);
+ if (file == NULL) {
+ return CTX_RESULT_NO_DATA;
+ }
+
+ CTX_data_pointer_set(result, &screen->id, &RNA_FileSelectEntry, file);
+ return CTX_RESULT_OK;
+ }
+ if (CTX_data_equals(member, "id")) {
+ const FileDirEntry *file = filelist_file(sfile->files, params->active_file);
+ if (file == NULL) {
+ return CTX_RESULT_NO_DATA;
+ }
+
+ ID *id = filelist_file_get_id(file);
+ if (id == NULL) {
+ return CTX_RESULT_NO_DATA;
+ }
+
+ CTX_data_id_pointer_set(result, id);
+ return CTX_RESULT_OK;
+ }
+
+ return CTX_RESULT_MEMBER_NOT_FOUND;
+}
+
+static void file_id_remap(ScrArea *area, SpaceLink *sl, ID *UNUSED(old_id), ID *UNUSED(new_id))
+{
+ SpaceFile *sfile = (SpaceFile *)sl;
+
+ /* If the file shows main data (IDs), tag it for reset. */
+ if (sfile->files && filelist_needs_reset_on_main_changes(sfile->files)) {
+ /* Full refresh of the file list if main data was changed, don't even attempt remap pointers.
+ * We could give file list types a id-remap callback, but it's probably not worth it.
+ * Refreshing local file lists is relatively cheap. */
+ file_tag_reset_list(area, sfile);
+ }
+}
+
/* only called once, from space/spacetypes.c */
void ED_spacetype_file(void)
{
@@ -700,6 +860,11 @@ void ED_spacetype_file(void)
st->operatortypes = file_operatortypes;
st->keymap = file_keymap;
st->dropboxes = file_dropboxes;
+ st->space_subtype_item_extend = file_space_subtype_item_extend;
+ st->space_subtype_get = file_space_subtype_get;
+ st->space_subtype_set = file_space_subtype_set;
+ st->context = file_context;
+ st->id_remap = file_id_remap;
/* regions: main window */
art = MEM_callocN(sizeof(ARegionType), "spacetype file region");
diff --git a/source/blender/editors/space_graph/CMakeLists.txt b/source/blender/editors/space_graph/CMakeLists.txt
index 414e5c87f5a..2a795dd954c 100644
--- a/source/blender/editors/space_graph/CMakeLists.txt
+++ b/source/blender/editors/space_graph/CMakeLists.txt
@@ -34,8 +34,8 @@ set(SRC
graph_draw.c
graph_edit.c
graph_ops.c
- graph_slider_ops.c
graph_select.c
+ graph_slider_ops.c
graph_utils.c
graph_view.c
space_graph.c
diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c
index c3e4eceef6e..e56d71913d6 100644
--- a/source/blender/editors/space_graph/graph_edit.c
+++ b/source/blender/editors/space_graph/graph_edit.c
@@ -2354,12 +2354,12 @@ static const EnumPropertyItem prop_graphkeys_mirror_types[] = {
{GRAPHKEYS_MIRROR_YAXIS,
"YAXIS",
0,
- "By Times Over Time=0",
+ "By Times Over Zero Time",
"Flip times of selected keyframes, effectively reversing the order they appear in"},
{GRAPHKEYS_MIRROR_XAXIS,
"XAXIS",
0,
- "By Values Over Value=0",
+ "By Values Over Zero Value",
"Flip values of selected keyframes (i.e. negative values become positive, and vice versa)"},
{GRAPHKEYS_MIRROR_MARKER,
"MARKER",
diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c
index 12035ab6b61..4ab4ef518fb 100644
--- a/source/blender/editors/space_graph/graph_select.c
+++ b/source/blender/editors/space_graph/graph_select.c
@@ -1288,8 +1288,8 @@ void GRAPH_OT_select_less(wmOperatorType *ot)
/* defines for left-right select tool */
static const EnumPropertyItem prop_graphkeys_leftright_select_types[] = {
{GRAPHKEYS_LRSEL_TEST, "CHECK", 0, "Check if Select Left or Right", ""},
- {GRAPHKEYS_LRSEL_LEFT, "LEFT", 0, "Before current frame", ""},
- {GRAPHKEYS_LRSEL_RIGHT, "RIGHT", 0, "After current frame", ""},
+ {GRAPHKEYS_LRSEL_LEFT, "LEFT", 0, "Before Current Frame", ""},
+ {GRAPHKEYS_LRSEL_RIGHT, "RIGHT", 0, "After Current Frame", ""},
{0, NULL, 0, NULL, NULL},
};
diff --git a/source/blender/editors/space_graph/graph_view.c b/source/blender/editors/space_graph/graph_view.c
index 0b9ba3762a3..cde0dab3503 100644
--- a/source/blender/editors/space_graph/graph_view.c
+++ b/source/blender/editors/space_graph/graph_view.c
@@ -533,4 +533,4 @@ void GRAPH_OT_ghost_curves_clear(wmOperatorType *ot)
/* Flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-} \ No newline at end of file
+}
diff --git a/source/blender/editors/space_info/info_report.c b/source/blender/editors/space_info/info_report.c
index 3ba088018c3..7dd8382c8ef 100644
--- a/source/blender/editors/space_info/info_report.c
+++ b/source/blender/editors/space_info/info_report.c
@@ -396,7 +396,7 @@ void INFO_OT_report_copy(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Copy Reports to Clipboard";
- ot->description = "Copy selected reports to Clipboard";
+ ot->description = "Copy selected reports to clipboard";
ot->idname = "INFO_OT_report_copy";
/* api callbacks */
diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt
index d8f31161c20..f4a3bb96aeb 100644
--- a/source/blender/editors/space_node/CMakeLists.txt
+++ b/source/blender/editors/space_node/CMakeLists.txt
@@ -21,6 +21,7 @@ set(INC
../../blenkernel
../../blenlib
../../blentranslation
+ ../../compositor
../../depsgraph
../../draw
../../gpu
@@ -29,7 +30,6 @@ set(INC
../../makesrna
../../nodes
../../render
- ../../compositor
../../windowmanager
../../../../intern/glew-mx
../../../../intern/guardedalloc
diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c
index 84e7a74fab3..45f3b6cf9c9 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.c
@@ -3182,6 +3182,46 @@ static void node_geometry_buts_attribute_math(uiLayout *layout,
uiItemR(layout, ptr, "input_type_b", DEFAULT_FLAGS, IFACE_("Type B"), ICON_NONE);
}
+static void node_geometry_buts_point_instance(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "instance_type", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+}
+
+static void node_geometry_buts_attribute_fill(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "data_type", DEFAULT_FLAGS, "", ICON_NONE);
+ // uiItemR(layout, ptr, "domain", DEFAULT_FLAGS, "", ICON_NONE);
+}
+
+static void node_geometry_buts_attribute_mix(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "blend_type", DEFAULT_FLAGS, "", ICON_NONE);
+ uiLayout *col = uiLayoutColumn(layout, false);
+ uiItemR(col, ptr, "input_type_factor", DEFAULT_FLAGS, IFACE_("Factor"), ICON_NONE);
+ uiItemR(col, ptr, "input_type_a", DEFAULT_FLAGS, IFACE_("A"), ICON_NONE);
+ uiItemR(col, ptr, "input_type_b", DEFAULT_FLAGS, IFACE_("B"), ICON_NONE);
+}
+
+static void node_geometry_buts_attribute_point_distribute(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiItemR(layout, ptr, "distribute_method", DEFAULT_FLAGS, "", ICON_NONE);
+}
+
+static void node_geometry_buts_attribute_color_ramp(uiLayout *layout,
+ bContext *UNUSED(C),
+ PointerRNA *ptr)
+{
+ uiTemplateColorRamp(layout, ptr, "color_ramp", 0);
+}
+
static void node_geometry_set_butfunc(bNodeType *ntype)
{
switch (ntype->type) {
@@ -3194,12 +3234,27 @@ static void node_geometry_set_butfunc(bNodeType *ntype)
case GEO_NODE_TRIANGULATE:
ntype->draw_buttons = node_geometry_buts_triangulate;
break;
- case GEO_NODE_RANDOM_ATTRIBUTE:
+ case GEO_NODE_ATTRIBUTE_RANDOMIZE:
ntype->draw_buttons = node_geometry_buts_random_attribute;
break;
case GEO_NODE_ATTRIBUTE_MATH:
ntype->draw_buttons = node_geometry_buts_attribute_math;
break;
+ case GEO_NODE_POINT_INSTANCE:
+ ntype->draw_buttons = node_geometry_buts_point_instance;
+ break;
+ case GEO_NODE_ATTRIBUTE_FILL:
+ ntype->draw_buttons = node_geometry_buts_attribute_fill;
+ break;
+ case GEO_NODE_ATTRIBUTE_MIX:
+ ntype->draw_buttons = node_geometry_buts_attribute_mix;
+ break;
+ case GEO_NODE_POINT_DISTRIBUTE:
+ ntype->draw_buttons = node_geometry_buts_attribute_point_distribute;
+ break;
+ case GEO_NODE_ATTRIBUTE_COLOR_RAMP:
+ ntype->draw_buttons = node_geometry_buts_attribute_color_ramp;
+ break;
}
}
@@ -3389,6 +3444,7 @@ static const float std_node_socket_colors[][4] = {
{0.93, 0.62, 0.36, 1.0}, /* SOCK_OBJECT */
{0.89, 0.76, 0.43, 1.0}, /* SOCK_IMAGE */
{0.00, 0.84, 0.64, 1.0}, /* SOCK_GEOMETRY */
+ {0.96, 0.96, 0.96, 1.0}, /* SOCK_COLLECTION */
};
/* common color callbacks for standard types */
@@ -3512,6 +3568,10 @@ static void std_node_socket_draw(
uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0);
break;
}
+ case SOCK_COLLECTION: {
+ uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0);
+ break;
+ }
default:
node_socket_button_label(C, layout, ptr, node_ptr, text);
break;
diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c
index fc7fa3a6caa..5a2eb0cc3a0 100644
--- a/source/blender/editors/space_node/node_draw.c
+++ b/source/blender/editors/space_node/node_draw.c
@@ -344,8 +344,6 @@ void node_from_view(struct bNode *node, float x, float y, float *rx, float *ry)
/* based on settings in node, sets drawing rect info. each redraw! */
static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
{
- uiLayout *layout, *row;
-
PointerRNA nodeptr;
RNA_pointer_create(&ntree->id, &RNA_Node, node, &nodeptr);
@@ -374,15 +372,15 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
PointerRNA sockptr;
RNA_pointer_create(&ntree->id, &RNA_NodeSocket, nsock, &sockptr);
- layout = UI_block_layout(node->block,
- UI_LAYOUT_VERTICAL,
- UI_LAYOUT_PANEL,
- locx + NODE_DYS,
- dy,
- NODE_WIDTH(node) - NODE_DY,
- NODE_DY,
- 0,
- UI_style_get_dpi());
+ uiLayout *layout = UI_block_layout(node->block,
+ UI_LAYOUT_VERTICAL,
+ UI_LAYOUT_PANEL,
+ locx + NODE_DYS,
+ dy,
+ NODE_WIDTH(node) - NODE_DY,
+ NODE_DY,
+ 0,
+ UI_style_get_dpi());
if (node->flag & NODE_MUTED) {
uiLayoutSetActive(layout, false);
@@ -393,7 +391,7 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
uiLayoutSetContextPointer(layout, "socket", &sockptr);
/* align output buttons to the right */
- row = uiLayoutRow(layout, 1);
+ uiLayout *row = uiLayoutRow(layout, true);
uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_RIGHT);
const char *socket_label = nodeSocketLabel(nsock);
nsock->typeinfo->draw((bContext *)C, row, &sockptr, &nodeptr, IFACE_(socket_label));
@@ -469,15 +467,15 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
node->butr.ymin = 0;
node->butr.ymax = 0;
- layout = UI_block_layout(node->block,
- UI_LAYOUT_VERTICAL,
- UI_LAYOUT_PANEL,
- locx + NODE_DYS,
- dy,
- node->butr.xmax,
- 0,
- 0,
- UI_style_get_dpi());
+ uiLayout *layout = UI_block_layout(node->block,
+ UI_LAYOUT_VERTICAL,
+ UI_LAYOUT_PANEL,
+ locx + NODE_DYS,
+ dy,
+ node->butr.xmax,
+ 0,
+ 0,
+ UI_style_get_dpi());
if (node->flag & NODE_MUTED) {
uiLayoutSetActive(layout, false);
@@ -502,15 +500,15 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
PointerRNA sockptr;
RNA_pointer_create(&ntree->id, &RNA_NodeSocket, nsock, &sockptr);
- layout = UI_block_layout(node->block,
- UI_LAYOUT_VERTICAL,
- UI_LAYOUT_PANEL,
- locx + NODE_DYS,
- dy,
- NODE_WIDTH(node) - NODE_DY,
- NODE_DY,
- 0,
- UI_style_get_dpi());
+ uiLayout *layout = UI_block_layout(node->block,
+ UI_LAYOUT_VERTICAL,
+ UI_LAYOUT_PANEL,
+ locx + NODE_DYS,
+ dy,
+ NODE_WIDTH(node) - NODE_DY,
+ NODE_DY,
+ 0,
+ UI_style_get_dpi());
if (node->flag & NODE_MUTED) {
uiLayoutSetActive(layout, false);
@@ -520,7 +518,7 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
uiLayoutSetContextPointer(layout, "node", &nodeptr);
uiLayoutSetContextPointer(layout, "socket", &sockptr);
- row = uiLayoutRow(layout, 1);
+ uiLayout *row = uiLayoutRow(layout, true);
const char *socket_label = nodeSocketLabel(nsock);
nsock->typeinfo->draw((bContext *)C, row, &sockptr, &nodeptr, IFACE_(socket_label));
@@ -771,7 +769,6 @@ void node_socket_color_get(
bContext *C, bNodeTree *ntree, PointerRNA *node_ptr, bNodeSocket *sock, float r_color[4])
{
PointerRNA ptr;
-
BLI_assert(RNA_struct_is_a(node_ptr->type, &RNA_Node));
RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &ptr);
diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c
index 039ddad71ef..fdce5e3f30b 100644
--- a/source/blender/editors/space_node/node_edit.c
+++ b/source/blender/editors/space_node/node_edit.c
@@ -100,9 +100,7 @@ typedef struct CompoJob {
static void compo_tag_output_nodes(bNodeTree *nodetree, int recalc_flags)
{
- bNode *node;
-
- for (node = nodetree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &nodetree->nodes) {
if (node->type == CMP_NODE_COMPOSITE) {
if (recalc_flags & COM_RECALC_COMPOSITE) {
node->flag |= NODE_DO_OUTPUT_RECALC;
@@ -124,14 +122,12 @@ static void compo_tag_output_nodes(bNodeTree *nodetree, int recalc_flags)
static int compo_get_recalc_flags(const bContext *C)
{
wmWindowManager *wm = CTX_wm_manager(C);
- wmWindow *win;
int recalc_flags = 0;
- for (win = wm->windows.first; win; win = win->next) {
+ LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
const bScreen *screen = WM_window_get_active_screen(win);
- ScrArea *area;
- for (area = screen->areabase.first; area; area = area->next) {
+ LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
if (area->spacetype == SPACE_IMAGE) {
SpaceImage *sima = area->spacedata.first;
if (sima->image) {
@@ -247,7 +243,6 @@ static void compo_startjob(void *cjv,
CompoJob *cj = cjv;
bNodeTree *ntree = cj->localtree;
Scene *scene = cj->scene;
- SceneRenderView *srv;
if (scene->use_nodes == false) {
return;
@@ -280,7 +275,7 @@ static void compo_startjob(void *cjv,
"");
}
else {
- for (srv = scene->r.views.first; srv; srv = srv->next) {
+ LISTBASE_FOREACH (SceneRenderView *, srv, &scene->r.views) {
if (BKE_scene_multiview_is_render_view_active(&scene->r, srv) == false) {
continue;
}
@@ -309,8 +304,6 @@ static void compo_startjob(void *cjv,
*/
void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene *scene_owner)
{
- wmJob *wm_job;
- CompoJob *cj;
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
@@ -327,13 +320,13 @@ void ED_node_composite_job(const bContext *C, struct bNodeTree *nodetree, Scene
BKE_image_backup_render(
scene, BKE_image_ensure_viewer(bmain, IMA_TYPE_R_RESULT, "Render Result"), false);
- wm_job = WM_jobs_get(CTX_wm_manager(C),
- CTX_wm_window(C),
- scene_owner,
- "Compositing",
- WM_JOB_EXCL_RENDER | WM_JOB_PROGRESS,
- WM_JOB_TYPE_COMPOSITE);
- cj = MEM_callocN(sizeof(CompoJob), "compo job");
+ wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
+ CTX_wm_window(C),
+ scene_owner,
+ "Compositing",
+ WM_JOB_EXCL_RENDER | WM_JOB_PROGRESS,
+ WM_JOB_TYPE_COMPOSITE);
+ CompoJob *cj = MEM_callocN(sizeof(CompoJob), "compo job");
/* customdata for preview thread */
cj->bmain = bmain;
@@ -524,9 +517,6 @@ void ED_node_shader_default(const bContext *C, ID *id)
/* called from shading buttons or header */
void ED_node_composit_default(const bContext *C, struct Scene *sce)
{
- bNode *in, *out;
- bNodeSocket *fromsock, *tosock;
-
/* but lets check it anyway */
if (sce->nodetree) {
if (G.debug & G_DEBUG) {
@@ -541,18 +531,18 @@ void ED_node_composit_default(const bContext *C, struct Scene *sce)
sce->nodetree->edit_quality = NTREE_QUALITY_HIGH;
sce->nodetree->render_quality = NTREE_QUALITY_HIGH;
- out = nodeAddStaticNode(C, sce->nodetree, CMP_NODE_COMPOSITE);
+ bNode *out = nodeAddStaticNode(C, sce->nodetree, CMP_NODE_COMPOSITE);
out->locx = 300.0f;
out->locy = 400.0f;
- in = nodeAddStaticNode(C, sce->nodetree, CMP_NODE_R_LAYERS);
+ bNode *in = nodeAddStaticNode(C, sce->nodetree, CMP_NODE_R_LAYERS);
in->locx = 10.0f;
in->locy = 400.0f;
nodeSetActive(sce->nodetree, in);
/* links from color to color */
- fromsock = in->outputs.first;
- tosock = out->inputs.first;
+ bNodeSocket *fromsock = in->outputs.first;
+ bNodeSocket *tosock = out->inputs.first;
nodeAddLink(sce->nodetree, in, fromsock, out, tosock);
ntreeUpdateTree(CTX_data_main(C), sce->nodetree);
@@ -562,9 +552,6 @@ void ED_node_composit_default(const bContext *C, struct Scene *sce)
/* called from shading buttons or header */
void ED_node_texture_default(const bContext *C, Tex *tex)
{
- bNode *in, *out;
- bNodeSocket *fromsock, *tosock;
-
/* but lets check it anyway */
if (tex->nodetree) {
if (G.debug & G_DEBUG) {
@@ -575,17 +562,17 @@ void ED_node_texture_default(const bContext *C, Tex *tex)
tex->nodetree = ntreeAddTree(NULL, "Texture Nodetree", ntreeType_Texture->idname);
- out = nodeAddStaticNode(C, tex->nodetree, TEX_NODE_OUTPUT);
+ bNode *out = nodeAddStaticNode(C, tex->nodetree, TEX_NODE_OUTPUT);
out->locx = 300.0f;
out->locy = 300.0f;
- in = nodeAddStaticNode(C, tex->nodetree, TEX_NODE_CHECKER);
+ bNode *in = nodeAddStaticNode(C, tex->nodetree, TEX_NODE_CHECKER);
in->locx = 10.0f;
in->locy = 300.0f;
nodeSetActive(tex->nodetree, in);
- fromsock = in->outputs.first;
- tosock = out->inputs.first;
+ bNodeSocket *fromsock = in->outputs.first;
+ bNodeSocket *tosock = out->inputs.first;
nodeAddLink(tex->nodetree, in, fromsock, out, tosock);
ntreeUpdateTree(CTX_data_main(C), tex->nodetree);
@@ -634,15 +621,13 @@ void snode_set_context(const bContext *C)
void snode_update(SpaceNode *snode, bNode *node)
{
- bNodeTreePath *path;
-
/* XXX this only updates nodes in the current node space tree path.
* The function supposedly should update any potential group node linking to changed tree,
* this really requires a working depsgraph ...
*/
/* update all edited group nodes */
- path = snode->treepath.last;
+ bNodeTreePath *path = snode->treepath.last;
if (path) {
bNodeTree *ngroup = path->nodetree;
for (path = path->prev; path; path = path->prev) {
@@ -671,10 +656,9 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node, bool *r_acti
/* generic node group output: set node as active output */
if (node->type == NODE_GROUP_OUTPUT) {
- bNode *tnode;
- for (tnode = ntree->nodes.first; tnode; tnode = tnode->next) {
- if (tnode->type == NODE_GROUP_OUTPUT) {
- tnode->flag &= ~NODE_DO_OUTPUT;
+ LISTBASE_FOREACH (bNode *, node_iter, &ntree->nodes) {
+ if (node_iter->type == NODE_GROUP_OUTPUT) {
+ node_iter->flag &= ~NODE_DO_OUTPUT;
}
}
@@ -696,11 +680,9 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node, bool *r_acti
SH_NODE_OUTPUT_WORLD,
SH_NODE_OUTPUT_LIGHT,
SH_NODE_OUTPUT_LINESTYLE)) {
- bNode *tnode;
-
- for (tnode = ntree->nodes.first; tnode; tnode = tnode->next) {
- if (tnode->type == node->type) {
- tnode->flag &= ~NODE_DO_OUTPUT;
+ LISTBASE_FOREACH (bNode *, node_iter, &ntree->nodes) {
+ if (node_iter->type == node->type) {
+ node_iter->flag &= ~NODE_DO_OUTPUT;
}
}
@@ -715,16 +697,13 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node, bool *r_acti
/* if active texture changed, free glsl materials */
if ((node->flag & NODE_ACTIVE_TEXTURE) && !was_active_texture) {
- Material *ma;
- World *wo;
-
- for (ma = bmain->materials.first; ma; ma = ma->id.next) {
+ LISTBASE_FOREACH (Material *, ma, &bmain->materials) {
if (ma->nodetree && ma->use_nodes && ntreeHasTree(ma->nodetree, ntree)) {
GPU_material_free(&ma->gpumaterial);
}
}
- for (wo = bmain->worlds.first; wo; wo = wo->id.next) {
+ LISTBASE_FOREACH (World *, wo, &bmain->materials) {
if (wo->nodetree && wo->use_nodes && ntreeHasTree(wo->nodetree, ntree)) {
GPU_material_free(&wo->gpumaterial);
}
@@ -742,11 +721,9 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node, bool *r_acti
else if (ntree->type == NTREE_COMPOSIT) {
/* make active viewer, currently only 1 supported... */
if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
- bNode *tnode;
-
- for (tnode = ntree->nodes.first; tnode; tnode = tnode->next) {
- if (ELEM(tnode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
- tnode->flag &= ~NODE_DO_OUTPUT;
+ LISTBASE_FOREACH (bNode *, node_iter, &ntree->nodes) {
+ if (ELEM(node_iter->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) {
+ node_iter->flag &= ~NODE_DO_OUTPUT;
}
}
@@ -760,11 +737,9 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node, bool *r_acti
}
else if (node->type == CMP_NODE_COMPOSITE) {
if (was_output == 0) {
- bNode *tnode;
-
- for (tnode = ntree->nodes.first; tnode; tnode = tnode->next) {
- if (tnode->type == CMP_NODE_COMPOSITE) {
- tnode->flag &= ~NODE_DO_OUTPUT;
+ LISTBASE_FOREACH (bNode *, node_iter, &ntree->nodes) {
+ if (node_iter->type == CMP_NODE_COMPOSITE) {
+ node_iter->flag &= ~NODE_DO_OUTPUT;
}
}
@@ -879,14 +854,12 @@ static void edit_node_properties_get(
/* is rct in visible part of node? */
static bNode *visible_node(SpaceNode *snode, const rctf *rct)
{
- bNode *node;
-
- for (node = snode->edittree->nodes.last; node; node = node->prev) {
+ LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
if (BLI_rctf_isect(&node->totr, rct, NULL)) {
- break;
+ return node;
}
}
- return node;
+ return NULL;
}
/* ********************** size widget operator ******************** */
@@ -952,23 +925,19 @@ static int node_resize_modal(bContext *C, wmOperator *op, const wmEvent *event)
ARegion *region = CTX_wm_region(C);
bNode *node = nodeGetActive(snode->edittree);
NodeSizeWidget *nsw = op->customdata;
- float mx, my, dx, dy;
switch (event->type) {
- case MOUSEMOVE:
-
+ case MOUSEMOVE: {
+ float mx, my;
UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &mx, &my);
- dx = (mx - nsw->mxstart) / UI_DPI_FAC;
- dy = (my - nsw->mystart) / UI_DPI_FAC;
+ float dx = (mx - nsw->mxstart) / UI_DPI_FAC;
+ float dy = (my - nsw->mystart) / UI_DPI_FAC;
if (node) {
- float *pwidth;
- float oldwidth, widthmin, widthmax;
-
- pwidth = &node->width;
- oldwidth = nsw->oldwidth;
- widthmin = node->typeinfo->minwidth;
- widthmax = node->typeinfo->maxwidth;
+ float *pwidth = &node->width;
+ float oldwidth = nsw->oldwidth;
+ float widthmin = node->typeinfo->minwidth;
+ float widthmax = node->typeinfo->maxwidth;
{
if (nsw->directions & NODE_RESIZE_RIGHT) {
@@ -1026,23 +995,24 @@ static int node_resize_modal(bContext *C, wmOperator *op, const wmEvent *event)
ED_region_tag_redraw(region);
break;
-
+ }
case LEFTMOUSE:
case MIDDLEMOUSE:
- case RIGHTMOUSE:
+ case RIGHTMOUSE: {
if (event->val == KM_RELEASE) {
node_resize_exit(C, op, false);
ED_node_post_apply_transform(C, snode->edittree);
return OPERATOR_FINISHED;
}
- else if (event->val == KM_PRESS) {
+ if (event->val == KM_PRESS) {
node_resize_exit(C, op, true);
ED_region_tag_redraw(region);
return OPERATOR_CANCELLED;
}
break;
+ }
}
return OPERATOR_RUNNING_MODAL;
@@ -1095,14 +1065,12 @@ void NODE_OT_resize(wmOperatorType *ot)
bool node_has_hidden_sockets(bNode *node)
{
- bNodeSocket *sock;
-
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
if (sock->flag & SOCK_HIDDEN) {
return true;
}
}
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
if (sock->flag & SOCK_HIDDEN) {
return true;
}
@@ -1112,24 +1080,22 @@ bool node_has_hidden_sockets(bNode *node)
void node_set_hidden_sockets(SpaceNode *snode, bNode *node, int set)
{
- bNodeSocket *sock;
-
if (set == 0) {
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
sock->flag &= ~SOCK_HIDDEN;
}
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
sock->flag &= ~SOCK_HIDDEN;
}
}
else {
/* hide unused sockets */
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
if (sock->link == NULL) {
sock->flag |= SOCK_HIDDEN;
}
}
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
if (nodeCountSocketLinks(snode->edittree, sock) == 0) {
sock->flag |= SOCK_HIDDEN;
}
@@ -1142,16 +1108,13 @@ void node_set_hidden_sockets(SpaceNode *snode, bNode *node, int set)
int node_find_indicated_socket(
SpaceNode *snode, bNode **nodep, bNodeSocket **sockp, float cursor[2], int in_out)
{
- bNode *node;
- bNodeSocket *sock;
rctf rect;
*nodep = NULL;
*sockp = NULL;
/* check if we click in a socket */
- for (node = snode->edittree->nodes.first; node; node = node->next) {
-
+ LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
BLI_rctf_init_pt_radius(&rect, cursor, NODE_SOCKSIZE + 4);
if (!(node->flag & NODE_HIDDEN)) {
@@ -1167,7 +1130,7 @@ int node_find_indicated_socket(
}
if (in_out & SOCK_IN) {
- for (sock = node->inputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
if (!nodeSocketIsHidden(sock)) {
if (BLI_rctf_isect_pt(&rect, sock->locx, sock->locy)) {
if (node == visible_node(snode, &rect)) {
@@ -1180,7 +1143,7 @@ int node_find_indicated_socket(
}
}
if (in_out & SOCK_OUT) {
- for (sock = node->outputs.first; sock; sock = sock->next) {
+ LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) {
if (!nodeSocketIsHidden(sock)) {
if (BLI_rctf_isect_pt(&rect, sock->locx, sock->locy)) {
if (node == visible_node(snode, &rect)) {
@@ -1226,17 +1189,15 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
- bNode *node, *newnode, *lastnode;
- bNodeLink *link, *newlink, *lastlink;
const bool keep_inputs = RNA_boolean_get(op->ptr, "keep_inputs");
bool do_tag_update = false;
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
- lastnode = ntree->nodes.last;
- for (node = ntree->nodes.first; node; node = node->next) {
+ bNode *lastnode = ntree->nodes.last;
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->flag & SELECT) {
- newnode = BKE_node_copy_store_new_pointers(ntree, node, LIB_ID_COPY_DEFAULT);
+ BKE_node_copy_store_new_pointers(ntree, node, LIB_ID_COPY_DEFAULT);
/* to ensure redraws or rerenders happen */
ED_node_tag_update_id(snode->id);
@@ -1251,14 +1212,14 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
/* copy links between selected nodes
* NB: this depends on correct node->new_node and sock->new_sock pointers from above copy!
*/
- lastlink = ntree->links.last;
- for (link = ntree->links.first; link; link = link->next) {
+ bNodeLink *lastlink = ntree->links.last;
+ LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
/* This creates new links between copied nodes.
* If keep_inputs is set, also copies input links from unselected (when fromnode==NULL)!
*/
if (link->tonode && (link->tonode->flag & NODE_SELECT) &&
(keep_inputs || (link->fromnode && (link->fromnode->flag & NODE_SELECT)))) {
- newlink = MEM_callocN(sizeof(bNodeLink), "bNodeLink");
+ bNodeLink *newlink = MEM_callocN(sizeof(bNodeLink), "bNodeLink");
newlink->flag = link->flag;
newlink->tonode = link->tonode->new_node;
newlink->tosock = link->tosock->new_sock;
@@ -1282,11 +1243,11 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
}
/* clear flags for recursive depth-first iteration */
- for (node = ntree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
node->flag &= ~NODE_TEST;
}
/* reparent copied nodes */
- for (node = ntree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if ((node->flag & SELECT) && !(node->flag & NODE_TEST)) {
node_duplicate_reparent_recursive(node);
}
@@ -1298,10 +1259,10 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
}
/* deselect old nodes, select the copies instead */
- for (node = ntree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->flag & SELECT) {
/* has been set during copy above */
- newnode = node->new_node;
+ bNode *newnode = node->new_node;
nodeSetSelected(node, false);
node->flag &= ~(NODE_ACTIVE | NODE_ACTIVE_TEXTURE);
@@ -1389,17 +1350,16 @@ static int node_read_viewlayers_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
- Scene *curscene = CTX_data_scene(C), *scene;
- bNode *node;
+ Scene *curscene = CTX_data_scene(C);
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
/* first tag scenes unread */
- for (scene = bmain->scenes.first; scene; scene = scene->id.next) {
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
scene->id.tag |= LIB_TAG_DOIT;
}
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
if (node->type == CMP_NODE_R_LAYERS) {
ID *id = node->id;
if (id->tag & LIB_TAG_DOIT) {
@@ -1434,13 +1394,14 @@ void NODE_OT_read_viewlayers(wmOperatorType *ot)
int node_render_changed_exec(bContext *C, wmOperator *UNUSED(op))
{
Scene *sce = CTX_data_scene(C);
- bNode *node;
/* This is actually a test whether scene is used by the compositor or not.
* All the nodes are using same render result, so there is no need to do
* anything smart about check how exactly scene is used. */
- for (node = sce->nodetree->nodes.first; node; node = node->next) {
- if (node->id == (ID *)sce) {
+ bNode *node = NULL;
+ LISTBASE_FOREACH (bNode *, node_iter, &sce->nodetree->nodes) {
+ if (node_iter->id == (ID *)sce) {
+ node = node_iter;
break;
}
}
@@ -1486,14 +1447,14 @@ void NODE_OT_render_changed(wmOperatorType *ot)
static void node_flag_toggle_exec(SpaceNode *snode, int toggle_flag)
{
- bNode *node;
int tot_eq = 0, tot_neq = 0;
/* Toggles the flag on all selected nodes.
* If the flag is set on all nodes it is unset.
* If the flag is not set on all nodes, it is set.
*/
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+
+ LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
if (node->flag & SELECT) {
if (toggle_flag == NODE_PREVIEW && (node->typeinfo->flag & NODE_PREVIEW) == 0) {
@@ -1512,7 +1473,7 @@ static void node_flag_toggle_exec(SpaceNode *snode, int toggle_flag)
}
}
}
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
if (node->flag & SELECT) {
if (toggle_flag == NODE_PREVIEW && (node->typeinfo->flag & NODE_PREVIEW) == 0) {
@@ -1631,8 +1592,6 @@ void NODE_OT_options_toggle(wmOperatorType *ot)
static int node_socket_toggle_exec(bContext *C, wmOperator *UNUSED(op))
{
SpaceNode *snode = CTX_wm_space_node(C);
- bNode *node;
- int hidden;
/* sanity checking (poll callback checks this already) */
if ((snode == NULL) || (snode->edittree == NULL)) {
@@ -1642,17 +1601,17 @@ static int node_socket_toggle_exec(bContext *C, wmOperator *UNUSED(op))
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
/* Toggle for all selected nodes */
- hidden = 0;
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ bool hidden = false;
+ LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
if (node->flag & SELECT) {
if (node_has_hidden_sockets(node)) {
- hidden = 1;
+ hidden = true;
break;
}
}
}
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
if (node->flag & SELECT) {
node_set_hidden_sockets(snode, node, !hidden);
}
@@ -1686,12 +1645,11 @@ static int node_mute_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
- bNode *node;
bool do_tag_update = false;
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
- for (node = snode->edittree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
/* Only allow muting of nodes having a mute func! */
if ((node->flag & SELECT) && node->typeinfo->update_internal_links) {
node->flag ^= NODE_MUTED;
@@ -1731,13 +1689,11 @@ static int node_delete_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
- bNode *node, *next;
bool do_tag_update = false;
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
- for (node = snode->edittree->nodes.first; node; node = next) {
- next = node->next;
+ LISTBASE_FOREACH_MUTABLE (bNode *, node, &snode->edittree->nodes) {
if (node->flag & SELECT) {
do_tag_update |= (do_tag_update || node_connected_to_output(bmain, snode->edittree, node));
nodeRemoveNode(bmain, snode->edittree, node, true);
@@ -1787,10 +1743,8 @@ static bool node_switch_view_poll(bContext *C)
static int node_switch_view_exec(bContext *C, wmOperator *UNUSED(op))
{
SpaceNode *snode = CTX_wm_space_node(C);
- bNode *node, *next;
- for (node = snode->edittree->nodes.first; node; node = next) {
- next = node->next;
+ LISTBASE_FOREACH_MUTABLE (bNode *, node, &snode->edittree->nodes) {
if (node->flag & SELECT) {
/* call the update function from the Switch View node */
node->update = NODE_UPDATE_OPERATOR;
@@ -1825,12 +1779,10 @@ static int node_delete_reconnect_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
- bNode *node, *next;
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
- for (node = snode->edittree->nodes.first; node; node = next) {
- next = node->next;
+ LISTBASE_FOREACH_MUTABLE (bNode *, node, &snode->edittree->nodes) {
if (node->flag & SELECT) {
nodeInternalRelink(snode->edittree, node);
nodeRemoveNode(bmain, snode->edittree, node, true);
@@ -1963,9 +1915,6 @@ static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op)
SpaceNode *snode = CTX_wm_space_node(C);
PointerRNA ptr = CTX_data_pointer_get(C, "node");
bNode *node = NULL;
- NodeImageMultiFile *nimf;
- bNodeSocket *sock;
- int direction;
if (ptr.data) {
node = ptr.data;
@@ -1978,14 +1927,14 @@ static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- nimf = node->storage;
+ NodeImageMultiFile *nimf = node->storage;
- sock = BLI_findlink(&node->inputs, nimf->active_input);
+ bNodeSocket *sock = BLI_findlink(&node->inputs, nimf->active_input);
if (!sock) {
return OPERATOR_CANCELLED;
}
- direction = RNA_enum_get(op->ptr, "direction");
+ int direction = RNA_enum_get(op->ptr, "direction");
if (direction == 1) {
bNodeSocket *before = sock->prev;
@@ -2037,24 +1986,23 @@ static int node_copy_color_exec(bContext *C, wmOperator *UNUSED(op))
{
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
- bNode *node, *tnode;
if (!ntree) {
return OPERATOR_CANCELLED;
}
- node = nodeGetActive(ntree);
+ bNode *node = nodeGetActive(ntree);
if (!node) {
return OPERATOR_CANCELLED;
}
- for (tnode = ntree->nodes.first; tnode; tnode = tnode->next) {
- if (tnode->flag & NODE_SELECT && tnode != node) {
+ LISTBASE_FOREACH (bNode *, node_iter, &ntree->nodes) {
+ if (node_iter->flag & NODE_SELECT && node_iter != node) {
if (node->flag & NODE_CUSTOM_COLOR) {
- tnode->flag |= NODE_CUSTOM_COLOR;
- copy_v3_v3(tnode->color, node->color);
+ node_iter->flag |= NODE_CUSTOM_COLOR;
+ copy_v3_v3(node_iter->color, node->color);
}
else {
- tnode->flag &= ~NODE_CUSTOM_COLOR;
+ node_iter->flag &= ~NODE_CUSTOM_COLOR;
}
}
}
@@ -2086,8 +2034,6 @@ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op))
{
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
- bNode *node;
- bNodeLink *link, *newlink;
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
@@ -2095,7 +2041,7 @@ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op))
BKE_node_clipboard_clear();
BKE_node_clipboard_init(ntree);
- for (node = ntree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->flag & SELECT) {
/* No ID refcounting, this node is virtual,
* detached from any actual Blender data currently. */
@@ -2105,7 +2051,7 @@ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- for (node = ntree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->flag & SELECT) {
bNode *new_node = node->new_node;
@@ -2126,11 +2072,11 @@ static int node_clipboard_copy_exec(bContext *C, wmOperator *UNUSED(op))
/* copy links between selected nodes
* NB: this depends on correct node->new_node and sock->new_sock pointers from above copy!
*/
- for (link = ntree->links.first; link; link = link->next) {
+ LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
/* This creates new links between copied nodes. */
if (link->tonode && (link->tonode->flag & NODE_SELECT) && link->fromnode &&
(link->fromnode->flag & NODE_SELECT)) {
- newlink = MEM_callocN(sizeof(bNodeLink), "bNodeLink");
+ bNodeLink *newlink = MEM_callocN(sizeof(bNodeLink), "bNodeLink");
newlink->flag = link->flag;
newlink->tonode = link->tonode->new_node;
newlink->tosock = link->tosock->new_sock;
@@ -2165,18 +2111,11 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
{
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
- const ListBase *clipboard_nodes_lb;
- const ListBase *clipboard_links_lb;
- bNode *node;
- bNodeLink *link;
- int num_nodes;
- float center[2];
- bool is_clipboard_valid, all_nodes_valid;
/* validate pointers in the clipboard */
- is_clipboard_valid = BKE_node_clipboard_validate();
- clipboard_nodes_lb = BKE_node_clipboard_get_nodes();
- clipboard_links_lb = BKE_node_clipboard_get_links();
+ bool is_clipboard_valid = BKE_node_clipboard_validate();
+ const ListBase *clipboard_nodes_lb = BKE_node_clipboard_get_nodes();
+ const ListBase *clipboard_links_lb = BKE_node_clipboard_get_links();
if (BLI_listbase_is_empty(clipboard_nodes_lb)) {
BKE_report(op->reports, RPT_ERROR, "Clipboard is empty");
@@ -2196,8 +2135,8 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
}
/* make sure all clipboard nodes would be valid in the target tree */
- all_nodes_valid = true;
- for (node = clipboard_nodes_lb->first; node; node = node->next) {
+ bool all_nodes_valid = true;
+ LISTBASE_FOREACH (bNode *, node, clipboard_nodes_lb) {
if (!node->typeinfo->poll_instance || !node->typeinfo->poll_instance(node, ntree)) {
all_nodes_valid = false;
BKE_reportf(op->reports,
@@ -2217,15 +2156,16 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
node_deselect_all(snode);
/* calculate "barycenter" for placing on mouse cursor */
- zero_v2(center);
- for (node = clipboard_nodes_lb->first, num_nodes = 0; node; node = node->next, num_nodes++) {
+ float center[2] = {0.0f, 0.0f};
+ int num_nodes = 0;
+ LISTBASE_FOREACH_INDEX (bNode *, node, clipboard_nodes_lb, num_nodes) {
center[0] += BLI_rctf_cent_x(&node->totr);
center[1] += BLI_rctf_cent_y(&node->totr);
}
mul_v2_fl(center, 1.0 / num_nodes);
/* copy nodes from clipboard */
- for (node = clipboard_nodes_lb->first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, clipboard_nodes_lb) {
bNode *new_node = BKE_node_copy_store_new_pointers(ntree, node, LIB_ID_COPY_DEFAULT);
/* pasted nodes are selected */
@@ -2233,14 +2173,14 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
}
/* reparent copied nodes */
- for (node = clipboard_nodes_lb->first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, clipboard_nodes_lb) {
bNode *new_node = node->new_node;
if (new_node->parent) {
new_node->parent = new_node->parent->new_node;
}
}
- for (link = clipboard_links_lb->first; link; link = link->next) {
+ LISTBASE_FOREACH (bNodeLink *, link, clipboard_links_lb) {
nodeAddLink(ntree,
link->fromnode->new_node,
link->fromsock->new_sock,
@@ -2275,10 +2215,9 @@ void NODE_OT_clipboard_paste(wmOperatorType *ot)
static bNodeSocket *ntree_get_active_interface_socket(ListBase *lb)
{
- bNodeSocket *sock;
- for (sock = lb->first; sock; sock = sock->next) {
- if (sock->flag & SELECT) {
- return sock;
+ LISTBASE_FOREACH (bNodeSocket *, socket, lb) {
+ if (socket->flag & SELECT) {
+ return socket;
}
}
return NULL;
@@ -2289,12 +2228,12 @@ static int ntree_socket_add_exec(bContext *C, wmOperator *op)
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
int in_out = RNA_enum_get(op->ptr, "in_out");
- PointerRNA ntree_ptr;
- bNodeSocket *sock, *tsock, *active_sock;
- const char *default_name;
+ PointerRNA ntree_ptr;
RNA_id_pointer_create((ID *)ntree, &ntree_ptr);
+ const char *default_name;
+ bNodeSocket *active_sock;
if (in_out == SOCK_IN) {
active_sock = ntree_get_active_interface_socket(&ntree->inputs);
default_name = "Input";
@@ -2304,6 +2243,7 @@ static int ntree_socket_add_exec(bContext *C, wmOperator *op)
default_name = "Output";
}
+ bNodeSocket *sock;
if (active_sock) {
/* insert a copy of the active socket right after it */
sock = ntreeInsertSocketInterface(
@@ -2317,11 +2257,11 @@ static int ntree_socket_add_exec(bContext *C, wmOperator *op)
}
/* deactivate sockets (has to check both lists) */
- for (tsock = ntree->inputs.first; tsock; tsock = tsock->next) {
- tsock->flag &= ~SELECT;
+ LISTBASE_FOREACH (bNodeSocket *, socket_iter, &ntree->inputs) {
+ socket_iter->flag &= ~SELECT;
}
- for (tsock = ntree->outputs.first; tsock; tsock = tsock->next) {
- tsock->flag &= ~SELECT;
+ LISTBASE_FOREACH (bNodeSocket *, socket_iter, &ntree->outputs) {
+ socket_iter->flag &= ~SELECT;
}
/* make the new socket active */
sock->flag |= SELECT;
@@ -2359,9 +2299,8 @@ static int ntree_socket_remove_exec(bContext *C, wmOperator *UNUSED(op))
{
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
- bNodeSocket *iosock, *active_sock;
- iosock = ntree_get_active_interface_socket(&ntree->inputs);
+ bNodeSocket *iosock = ntree_get_active_interface_socket(&ntree->inputs);
if (!iosock) {
iosock = ntree_get_active_interface_socket(&ntree->outputs);
}
@@ -2370,7 +2309,7 @@ static int ntree_socket_remove_exec(bContext *C, wmOperator *UNUSED(op))
}
/* preferably next socket becomes active, otherwise try previous socket */
- active_sock = (iosock->next ? iosock->next : iosock->prev);
+ bNodeSocket *active_sock = (iosock->next ? iosock->next : iosock->prev);
ntreeRemoveSocketInterface(ntree, iosock);
/* set active socket */
@@ -2416,11 +2355,9 @@ static int ntree_socket_move_exec(bContext *C, wmOperator *op)
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
int direction = RNA_enum_get(op->ptr, "direction");
- bNodeSocket *iosock;
- ListBase *lb;
- lb = &ntree->inputs;
- iosock = ntree_get_active_interface_socket(lb);
+ ListBase *lb = &ntree->inputs;
+ bNodeSocket *iosock = ntree_get_active_interface_socket(lb);
if (!iosock) {
lb = &ntree->outputs;
iosock = ntree_get_active_interface_socket(lb);
@@ -2489,8 +2426,6 @@ static bool node_shader_script_update_poll(bContext *C)
Scene *scene = CTX_data_scene(C);
const RenderEngineType *type = RE_engines_find(scene->r.engine);
SpaceNode *snode = CTX_wm_space_node(C);
- bNode *node;
- Text *text;
/* test if we have a render engine that supports shaders scripts */
if (!(type && type->update_script_node)) {
@@ -2498,7 +2433,7 @@ static bool node_shader_script_update_poll(bContext *C)
}
/* see if we have a shader script node in context */
- node = CTX_data_pointer_get_type(C, "node", &RNA_ShaderNodeScript).data;
+ bNode *node = CTX_data_pointer_get_type(C, "node", &RNA_ShaderNodeScript).data;
if (!node && snode && snode->edittree) {
node = nodeGetActive(snode->edittree);
@@ -2513,7 +2448,7 @@ static bool node_shader_script_update_poll(bContext *C)
}
/* see if we have a text datablock in context */
- text = CTX_data_pointer_get_type(C, "edit_text", &RNA_Text).data;
+ Text *text = CTX_data_pointer_get_type(C, "edit_text", &RNA_Text).data;
if (text) {
return 1;
}
@@ -2530,12 +2465,11 @@ static bool node_shader_script_update_text_recursive(RenderEngine *engine,
Text *text)
{
bool found = false;
- bNode *node;
ntree->done = true;
/* update each script that is using this text datablock */
- for (node = ntree->nodes.first; node; node = node->next) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type == NODE_GROUP) {
bNodeTree *ngroup = (bNodeTree *)node->id;
if (ngroup && !ngroup->done) {
@@ -2557,18 +2491,16 @@ static int node_shader_script_update_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
SpaceNode *snode = CTX_wm_space_node(C);
PointerRNA nodeptr = CTX_data_pointer_get_type(C, "node", &RNA_ShaderNodeScript);
- bNodeTree *ntree_base = NULL;
- bNode *node = NULL;
- RenderEngine *engine;
- RenderEngineType *type;
bool found = false;
/* setup render engine */
- type = RE_engines_find(scene->r.engine);
- engine = RE_engine_create(type);
+ RenderEngineType *type = RE_engines_find(scene->r.engine);
+ RenderEngine *engine = RE_engine_create(type);
engine->reports = op->reports;
/* get node */
+ bNodeTree *ntree_base = NULL;
+ bNode *node = NULL;
if (nodeptr.data) {
ntree_base = (bNodeTree *)nodeptr.owner_id;
node = nodeptr.data;
@@ -2643,10 +2575,8 @@ static void viewer_border_corner_to_backdrop(SpaceNode *snode,
float *fx,
float *fy)
{
- float bufx, bufy;
-
- bufx = backdrop_width * snode->zoom;
- bufy = backdrop_height * snode->zoom;
+ float bufx = backdrop_width * snode->zoom;
+ float bufy = backdrop_height * snode->zoom;
*fx = (bufx > 0.0f ? ((float)x - 0.5f * region->winx - snode->xof) / bufx + 0.5f : 0.0f);
*fy = (bufy > 0.0f ? ((float)y - 0.5f * region->winy - snode->yof) / bufy + 0.5f : 0.0f);
@@ -2655,14 +2585,12 @@ static void viewer_border_corner_to_backdrop(SpaceNode *snode,
static int viewer_border_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
- Image *ima;
void *lock;
- ImBuf *ibuf;
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
- ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
- ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
+ Image *ima = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node");
+ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
if (ibuf) {
ARegion *region = CTX_wm_region(C);
diff --git a/source/blender/editors/space_node/node_view.c b/source/blender/editors/space_node/node_view.c
index 3c861896d37..d2c88ed787c 100644
--- a/source/blender/editors/space_node/node_view.c
+++ b/source/blender/editors/space_node/node_view.c
@@ -283,7 +283,7 @@ void NODE_OT_backimage_move(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Background Image Move";
- ot->description = "Move Node backdrop";
+ ot->description = "Move node backdrop";
ot->idname = "NODE_OT_backimage_move";
/* api callbacks */
diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c
index afc1a963f4f..ad7632377a3 100644
--- a/source/blender/editors/space_node/space_node.c
+++ b/source/blender/editors/space_node/space_node.c
@@ -645,7 +645,7 @@ static bool node_ima_drop_poll(bContext *UNUSED(C),
/* rule might not work? */
return (ELEM(drag->icon, 0, ICON_FILE_IMAGE, ICON_FILE_MOVIE));
}
- return WM_drag_ID(drag, ID_IM) != NULL;
+ return WM_drag_get_local_ID(drag, ID_IM) != NULL;
}
static bool node_mask_drop_poll(bContext *UNUSED(C),
@@ -653,19 +653,19 @@ static bool node_mask_drop_poll(bContext *UNUSED(C),
const wmEvent *UNUSED(event),
const char **UNUSED(r_tooltip))
{
- return WM_drag_ID(drag, ID_MSK) != NULL;
+ return WM_drag_get_local_ID(drag, ID_MSK) != NULL;
}
static void node_id_drop_copy(wmDrag *drag, wmDropBox *drop)
{
- ID *id = WM_drag_ID(drag, 0);
+ ID *id = WM_drag_get_local_ID(drag, 0);
RNA_string_set(drop->ptr, "name", id->name + 2);
}
static void node_id_path_drop_copy(wmDrag *drag, wmDropBox *drop)
{
- ID *id = WM_drag_ID(drag, 0);
+ ID *id = WM_drag_get_local_ID(drag, 0);
if (id) {
RNA_string_set(drop->ptr, "name", id->name + 2);
diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt
index 74f99540bee..e0262371559 100644
--- a/source/blender/editors/space_outliner/CMakeLists.txt
+++ b/source/blender/editors/space_outliner/CMakeLists.txt
@@ -34,6 +34,7 @@ set(INC
set(SRC
outliner_collections.c
+ outliner_context.c
outliner_dragdrop.c
outliner_draw.c
outliner_edit.c
@@ -46,12 +47,12 @@ set(SRC
space_outliner.c
tree/common.cc
tree/tree_display.cc
+ tree/tree_display_data.cc
tree/tree_display_libraries.cc
- tree/tree_display_view_layer.cc
- tree/tree_display_sequencer.cc
tree/tree_display_orphaned.cc
tree/tree_display_scenes.cc
- tree/tree_display_data.cc
+ tree/tree_display_sequencer.cc
+ tree/tree_display_view_layer.cc
tree/tree_element.cc
tree/tree_element_anim_data.cc
tree/tree_element_driver_base.cc
diff --git a/source/blender/editors/space_outliner/outliner_context.c b/source/blender/editors/space_outliner/outliner_context.c
new file mode 100644
index 00000000000..a314a640e42
--- /dev/null
+++ b/source/blender/editors/space_outliner/outliner_context.c
@@ -0,0 +1,73 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2017 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup spoutliner
+ */
+
+#include "BLI_listbase.h"
+
+#include "BKE_context.h"
+
+#include "DNA_space_types.h"
+
+#include "RNA_access.h"
+
+#include "outliner_intern.h"
+
+static void outliner_context_selected_ids_recursive(const ListBase *subtree,
+ bContextDataResult *result)
+{
+ LISTBASE_FOREACH (const TreeElement *, te, subtree) {
+ const TreeStoreElem *tse = TREESTORE(te);
+ if ((tse->flag & TSE_SELECTED) && (ELEM(tse->type, 0, TSE_LAYER_COLLECTION))) {
+ CTX_data_id_list_add(result, tse->id);
+ }
+ outliner_context_selected_ids_recursive(&te->subtree, result);
+ }
+}
+
+static void outliner_context_selected_ids(const SpaceOutliner *space_outliner,
+ bContextDataResult *result)
+{
+ outliner_context_selected_ids_recursive(&space_outliner->tree, result);
+ CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION);
+}
+
+static const char *outliner_context_dir[] = {"selected_ids", NULL};
+
+int /*eContextResult*/ outliner_context(const bContext *C,
+ const char *member,
+ bContextDataResult *result)
+{
+ SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
+
+ if (CTX_data_dir(member)) {
+ CTX_data_dir_set(result, outliner_context_dir);
+ return CTX_RESULT_OK;
+ }
+ if (CTX_data_equals(member, "selected_ids")) {
+ outliner_context_selected_ids(space_outliner, result);
+ return CTX_RESULT_OK;
+ }
+ /* Note: Querying non-ID selection could also work if tree elements stored their matching RNA
+ * struct type. */
+
+ return CTX_RESULT_MEMBER_NOT_FOUND;
+}
diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c
index d3da7b80765..152bbc96281 100644
--- a/source/blender/editors/space_outliner/outliner_dragdrop.c
+++ b/source/blender/editors/space_outliner/outliner_dragdrop.c
@@ -333,7 +333,7 @@ static bool parent_drop_poll(bContext *C,
ED_region_tag_redraw_no_rebuild(CTX_wm_region(C));
}
- Object *potential_child = (Object *)WM_drag_ID(drag, ID_OB);
+ Object *potential_child = (Object *)WM_drag_get_local_ID(drag, ID_OB);
if (!potential_child) {
return false;
}
@@ -421,7 +421,7 @@ static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
}
Object *par = (Object *)tselem->id;
- Object *ob = (Object *)WM_drag_ID_from_event(event, ID_OB);
+ Object *ob = (Object *)WM_drag_get_local_ID_from_event(event, ID_OB);
if (ELEM(NULL, ob, par)) {
return OPERATOR_CANCELLED;
@@ -445,7 +445,7 @@ static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void OUTLINER_OT_parent_drop(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Drop to Set Parent [+Alt keeps transforms]";
+ ot->name = "Drop to Set Parent (hold Alt to keep transforms)";
ot->description = "Drag to parent in Outliner";
ot->idname = "OUTLINER_OT_parent_drop";
@@ -473,7 +473,7 @@ static bool parent_clear_poll(bContext *C,
}
}
- Object *ob = (Object *)WM_drag_ID(drag, ID_OB);
+ Object *ob = (Object *)WM_drag_get_local_ID(drag, ID_OB);
if (!ob) {
return false;
}
@@ -531,7 +531,7 @@ static int parent_clear_invoke(bContext *C, wmOperator *UNUSED(op), const wmEven
void OUTLINER_OT_parent_clear(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Drop to Clear Parent [+Alt keeps transforms]";
+ ot->name = "Drop to Clear Parent (hold Alt to keep transforms)";
ot->description = "Drag to clear parent in Outliner";
ot->idname = "OUTLINER_OT_parent_clear";
@@ -552,7 +552,7 @@ static bool scene_drop_poll(bContext *C,
const char **UNUSED(r_tooltip))
{
/* Ensure item under cursor is valid drop target */
- Object *ob = (Object *)WM_drag_ID(drag, ID_OB);
+ Object *ob = (Object *)WM_drag_get_local_ID(drag, ID_OB);
return (ob && (outliner_ID_drop_find(C, event, ID_SCE) != NULL));
}
@@ -560,7 +560,7 @@ static int scene_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent
{
Main *bmain = CTX_data_main(C);
Scene *scene = (Scene *)outliner_ID_drop_find(C, event, ID_SCE);
- Object *ob = (Object *)WM_drag_ID_from_event(event, ID_OB);
+ Object *ob = (Object *)WM_drag_get_local_ID_from_event(event, ID_OB);
if (ELEM(NULL, ob, scene) || ID_IS_LINKED(scene)) {
return OPERATOR_CANCELLED;
@@ -620,7 +620,7 @@ static bool material_drop_poll(bContext *C,
const char **UNUSED(r_tooltip))
{
/* Ensure item under cursor is valid drop target */
- Material *ma = (Material *)WM_drag_ID(drag, ID_MA);
+ Material *ma = (Material *)WM_drag_get_local_ID(drag, ID_MA);
return (ma && (outliner_ID_drop_find(C, event, ID_OB) != NULL));
}
@@ -628,7 +628,7 @@ static int material_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEve
{
Main *bmain = CTX_data_main(C);
Object *ob = (Object *)outliner_ID_drop_find(C, event, ID_OB);
- Material *ma = (Material *)WM_drag_ID_from_event(event, ID_MA);
+ Material *ma = (Material *)WM_drag_get_local_ID_from_event(event, ID_MA);
if (ELEM(NULL, ob, ma)) {
return OPERATOR_CANCELLED;
@@ -1461,14 +1461,14 @@ static int outliner_item_drag_drop_invoke(bContext *C,
parent = scene->master_collection;
}
- WM_drag_add_ID(drag, id, &parent->id);
+ WM_drag_add_local_ID(drag, id, &parent->id);
}
BLI_freelistN(&selected.selected_array);
}
else {
/* Add single ID. */
- WM_drag_add_ID(drag, data.drag_id, data.drag_parent);
+ WM_drag_add_local_ID(drag, data.drag_id, data.drag_parent);
}
ED_outliner_select_sync_from_outliner(C, space_outliner);
diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h
index 0294b4836c8..339cc3068d0 100644
--- a/source/blender/editors/space_outliner/outliner_intern.h
+++ b/source/blender/editors/space_outliner/outliner_intern.h
@@ -42,6 +42,7 @@ struct TreeElement;
struct TreeStoreElem;
struct ViewLayer;
struct bContext;
+struct bContextDataResult;
struct bPoseChannel;
struct wmKeyConfig;
struct wmOperatorType;
@@ -561,6 +562,12 @@ void outliner_tag_redraw_avoid_rebuild_on_open_change(const struct SpaceOutliner
void outliner_sync_selection(const struct bContext *C, struct SpaceOutliner *space_outliner);
+/* outliner_context.c ------------------------------------------- */
+
+int outliner_context(const struct bContext *C,
+ const char *member,
+ struct bContextDataResult *result);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c
index 159a4616ba7..492fc5c23bc 100644
--- a/source/blender/editors/space_outliner/outliner_tools.c
+++ b/source/blender/editors/space_outliner/outliner_tools.c
@@ -737,7 +737,7 @@ static void id_local_fn(bContext *C,
}
static void object_proxy_to_override_convert_fn(bContext *C,
- ReportList *UNUSED(reports),
+ ReportList *reports,
Scene *UNUSED(scene),
TreeElement *UNUSED(te),
TreeStoreElem *UNUSED(tsep),
@@ -754,8 +754,15 @@ static void object_proxy_to_override_convert_fn(bContext *C,
return;
}
- BKE_lib_override_library_proxy_convert(
- CTX_data_main(C), scene, CTX_data_view_layer(C), ob_proxy);
+ if (!BKE_lib_override_library_proxy_convert(
+ CTX_data_main(C), scene, CTX_data_view_layer(C), ob_proxy)) {
+ BKE_reportf(
+ reports,
+ RPT_ERROR_INVALID_INPUT,
+ "Could not create a library override from proxy '%s' (might use already local data?)",
+ ob_proxy->id.name + 2);
+ return;
+ }
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS | ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_WINDOW, NULL);
@@ -1685,6 +1692,8 @@ typedef enum eOutlinerIdOpTypes {
OUTLINER_IDOP_INVALID = 0,
OUTLINER_IDOP_UNLINK,
+ OUTLINER_IDOP_MARK_ASSET,
+ OUTLINER_IDOP_CLEAR_ASSET,
OUTLINER_IDOP_LOCAL,
OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE,
OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY,
@@ -1710,6 +1719,8 @@ typedef enum eOutlinerIdOpTypes {
/* TODO: implement support for changing the ID-block used. */
static const EnumPropertyItem prop_id_op_types[] = {
{OUTLINER_IDOP_UNLINK, "UNLINK", 0, "Unlink", ""},
+ {OUTLINER_IDOP_MARK_ASSET, "MARK_ASSET", 0, "Mark Asset", ""},
+ {OUTLINER_IDOP_CLEAR_ASSET, "CLEAR_ASSET", 0, "Clear Asset", ""},
{OUTLINER_IDOP_LOCAL, "LOCAL", 0, "Make Local", ""},
{OUTLINER_IDOP_SINGLE, "SINGLE", 0, "Make Single User", ""},
{OUTLINER_IDOP_DELETE, "DELETE", ICON_X, "Delete", ""},
@@ -1915,6 +1926,14 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
}
break;
}
+ case OUTLINER_IDOP_MARK_ASSET: {
+ WM_operator_name_call(C, "ASSET_OT_mark", WM_OP_EXEC_DEFAULT, NULL);
+ break;
+ }
+ case OUTLINER_IDOP_CLEAR_ASSET: {
+ WM_operator_name_call(C, "ASSET_OT_clear", WM_OP_EXEC_DEFAULT, NULL);
+ break;
+ }
case OUTLINER_IDOP_LOCAL: {
/* make local */
outliner_do_libdata_operation(
@@ -2791,7 +2810,7 @@ static int do_outliner_operation_event(bContext *C,
}
if (datalevel == TSE_ID_BASE) {
/* do nothing... there are no ops needed here yet */
- return 0;
+ return OPERATOR_CANCELLED;
}
if (datalevel == TSE_CONSTRAINT) {
return outliner_operator_menu(C, "OUTLINER_OT_constraint_operation");
@@ -2802,7 +2821,7 @@ static int do_outliner_operation_event(bContext *C,
return outliner_operator_menu(C, "OUTLINER_OT_data_operation");
}
- return 0;
+ return OPERATOR_CANCELLED;
}
static int outliner_operation(bContext *C, wmOperator *op, const wmEvent *event)
diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c
index 7308b161d18..56eedcd3748 100644
--- a/source/blender/editors/space_outliner/outliner_tree.c
+++ b/source/blender/editors/space_outliner/outliner_tree.c
@@ -908,9 +908,9 @@ static void outliner_add_id_contents(SpaceOutliner *space_outliner,
/**
* TODO: this function needs to be split up! It's getting a bit too large...
*
- * \note: "ID" is not always a real ID
- * \note: If child items are only added to the tree if the item is open, the TSE_ type _must_ be
- * added to #outliner_element_needs_rebuild_on_open_change().
+ * \note "ID" is not always a real ID.
+ * \note If child items are only added to the tree if the item is open,
+ * the `TSE_` type _must_ be added to #outliner_element_needs_rebuild_on_open_change().
*/
TreeElement *outliner_add_element(SpaceOutliner *space_outliner,
ListBase *lb,
diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c
index 3d675fdd9e4..c7c207caca0 100644
--- a/source/blender/editors/space_outliner/space_outliner.c
+++ b/source/blender/editors/space_outliner/space_outliner.c
@@ -451,6 +451,7 @@ void ED_spacetype_outliner(void)
st->dropboxes = outliner_dropboxes;
st->id_remap = outliner_id_remap;
st->deactivate = outliner_deactivate;
+ st->context = outliner_context;
/* regions: main window */
art = MEM_callocN(sizeof(ARegionType), "spacetype outliner region");
diff --git a/source/blender/editors/space_outliner/tree/tree_display_libraries.cc b/source/blender/editors/space_outliner/tree/tree_display_libraries.cc
index bd0870c837c..cb5f42f08e1 100644
--- a/source/blender/editors/space_outliner/tree/tree_display_libraries.cc
+++ b/source/blender/editors/space_outliner/tree/tree_display_libraries.cc
@@ -24,6 +24,8 @@
#include "BKE_collection.h"
#include "BKE_main.h"
+#include "DNA_collection_types.h"
+
#include "BLT_translation.h"
#include "../outliner_intern.h"
diff --git a/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc b/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc
index c88eb957dd1..f7740f4648f 100644
--- a/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc
+++ b/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc
@@ -20,6 +20,7 @@
#include <iostream>
+#include "DNA_collection_types.h"
#include "DNA_scene_types.h"
#include "BKE_layer.h"
diff --git a/source/blender/editors/space_outliner/tree/tree_element_nla.hh b/source/blender/editors/space_outliner/tree/tree_element_nla.hh
index 3ca62b13bd8..c94287ce576 100644
--- a/source/blender/editors/space_outliner/tree/tree_element_nla.hh
+++ b/source/blender/editors/space_outliner/tree/tree_element_nla.hh
@@ -23,7 +23,6 @@
#include "tree_element.hh"
struct NlaTrack;
-struct NlaStrip;
namespace blender::ed::outliner {
diff --git a/source/blender/editors/space_script/script_edit.c b/source/blender/editors/space_script/script_edit.c
index bde7bdb77f1..50cfa2e71c7 100644
--- a/source/blender/editors/space_script/script_edit.c
+++ b/source/blender/editors/space_script/script_edit.c
@@ -152,7 +152,7 @@ void SCRIPT_OT_reload(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Reload Scripts";
- ot->description = "Reload Scripts";
+ ot->description = "Reload scripts";
ot->idname = "SCRIPT_OT_reload";
/* api callbacks */
diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c
index 37dfcdbc765..71433a6978a 100644
--- a/source/blender/editors/space_sequencer/sequencer_add.c
+++ b/source/blender/editors/space_sequencer/sequencer_add.c
@@ -81,9 +81,18 @@ typedef struct SequencerAddData {
#define SEQPROP_ENDFRAME (1 << 1)
#define SEQPROP_NOPATHS (1 << 2)
#define SEQPROP_NOCHAN (1 << 3)
+#define SEQPROP_FIT_METHOD (1 << 4)
#define SELECT 1
+static const EnumPropertyItem scale_fit_methods[] = {
+ {SEQ_SCALE_TO_FIT, "FIT", 0, "Scale to Fit", "Scale image to fit within the canvas"},
+ {SEQ_SCALE_TO_FILL, "FILL", 0, "Scale to Fill", "Scale image to completely fill the canvas"},
+ {SEQ_STRETCH_TO_FILL, "STRETCH", 0, "Stretch to Fill", "Stretch image to fill the canvas"},
+ {SEQ_USE_ORIGINAL_SIZE, "ORIGINAL", 0, "Use Original Size", "Keep image at its original size"},
+ {0, NULL, 0, NULL, NULL},
+};
+
static void sequencer_generic_props__internal(wmOperatorType *ot, int flag)
{
PropertyRNA *prop;
@@ -123,6 +132,15 @@ static void sequencer_generic_props__internal(wmOperatorType *ot, int flag)
prop = RNA_def_boolean(
ot->srna, "overlap", 0, "Allow Overlap", "Don't correct overlap on new sequence strips");
RNA_def_property_flag(prop, PROP_HIDDEN);
+
+ if (flag & SEQPROP_FIT_METHOD) {
+ ot->prop = RNA_def_enum(ot->srna,
+ "fit_method",
+ scale_fit_methods,
+ SEQ_SCALE_TO_FIT,
+ "Fit Method",
+ "Scale fit method");
+ }
}
static void sequencer_generic_invoke_path__internal(bContext *C,
@@ -206,6 +224,8 @@ static void seq_load_operator_info(SeqLoadInfo *seq_load, bContext *C, wmOperato
seq_load->end_frame = seq_load->start_frame;
seq_load->channel = RNA_int_get(op->ptr, "channel");
seq_load->len = 1;
+ seq_load->fit_method = RNA_enum_get(op->ptr, "fit_method");
+ SEQ_tool_settings_fit_method_set(CTX_data_scene(C), seq_load->fit_method);
if ((prop = RNA_struct_find_property(op->ptr, "filepath"))) {
/* Full path, file is set by the caller. */
@@ -659,6 +679,7 @@ static int sequencer_add_movie_strip_invoke(bContext *C,
if (ed && ed->seqbasep && ed->seqbasep->first) {
RNA_boolean_set(op->ptr, "use_framerate", false);
}
+ RNA_enum_set(op->ptr, "fit_method", SEQ_tool_settings_fit_method_get(scene));
/* This is for drag and drop. */
if ((RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) ||
@@ -725,7 +746,7 @@ void SEQUENCER_OT_movie_strip_add(struct wmOperatorType *ot)
WM_FILESEL_SHOW_PROPS | WM_FILESEL_DIRECTORY,
FILE_DEFAULTDISPLAY,
FILE_SORT_DEFAULT);
- sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME);
+ sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME | SEQPROP_FIT_METHOD);
RNA_def_boolean(ot->srna, "sound", true, "Sound", "Load sound with the movie");
RNA_def_boolean(ot->srna,
"use_framerate",
@@ -928,6 +949,9 @@ static int sequencer_add_image_strip_invoke(bContext *C,
PropertyRNA *prop;
Scene *scene = CTX_data_scene(C);
+ const SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings;
+ RNA_enum_set(op->ptr, "fit_method", tool_settings->fit_method);
+
/* Name set already by drag and drop. */
if (RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) {
sequencer_generic_invoke_xy__internal(
@@ -972,7 +996,8 @@ void SEQUENCER_OT_image_strip_add(struct wmOperatorType *ot)
WM_FILESEL_SHOW_PROPS | WM_FILESEL_DIRECTORY,
FILE_DEFAULTDISPLAY,
FILE_SORT_DEFAULT);
- sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME | SEQPROP_ENDFRAME);
+ sequencer_generic_props__internal(ot,
+ SEQPROP_STARTFRAME | SEQPROP_ENDFRAME | SEQPROP_FIT_METHOD);
RNA_def_boolean(ot->srna,
"use_placeholders",
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index 081714991ff..d7d601a3c76 100644
--- a/source/blender/editors/space_sequencer/sequencer_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_draw.c
@@ -227,16 +227,16 @@ void color3ubv_from_seq(Scene *curscene, Sequence *seq, uchar col[3])
* \param x1, x2, y1, y2: The starting and end X value to draw the wave, same for y1 and y2.
* \param stepsize: The width of a pixel.
*/
-static void draw_seq_waveform(View2D *v2d,
- const bContext *C,
- SpaceSeq *sseq,
- Scene *scene,
- Sequence *seq,
- float x1,
- float y1,
- float x2,
- float y2,
- float stepsize)
+static void draw_seq_waveform_overlay(View2D *v2d,
+ const bContext *C,
+ SpaceSeq *sseq,
+ Scene *scene,
+ Sequence *seq,
+ float x1,
+ float y1,
+ float x2,
+ float y2,
+ float stepsize)
{
/* Offset x1 and x2 values, to match view min/max, if strip is out of bounds. */
int x1_offset = max_ff(v2d->cur.xmin, x1);
@@ -603,121 +603,110 @@ static void draw_seq_outline(Sequence *seq,
}
}
-/* Draw info text on a sequence strip. */
-static void draw_seq_text(View2D *v2d,
- Sequence *seq,
- SpaceSeq *sseq,
- float x1,
- float x2,
- float y1,
- float y2,
- bool seq_active,
- bool y_threshold)
+static const char *draw_seq_text_get_name(Sequence *seq)
{
- rctf rect;
- char str[32 + FILE_MAX];
- size_t str_len;
const char *name = seq->name + 2;
- uchar col[4];
-
- /* All strings should include name. */
if (name[0] == '\0') {
name = BKE_sequence_give_name(seq);
}
+ return name;
+}
- if (ELEM(seq->type, SEQ_TYPE_META, SEQ_TYPE_ADJUSTMENT)) {
- str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len);
+static void draw_seq_text_get_source(Sequence *seq, char *r_source, size_t source_len)
+{
+ /* Set source for the most common types. */
+ if (ELEM(seq->type, SEQ_TYPE_IMAGE, SEQ_TYPE_MOVIE)) {
+ BLI_snprintf(r_source, source_len, "%s%s", seq->strip->dir, seq->strip->stripdata->name);
}
- else if (seq->type == SEQ_TYPE_SCENE) {
- if (seq->scene) {
- if (seq->scene_camera) {
- str_len = BLI_snprintf(str,
- sizeof(str),
- "%s: %s (%s) | %d",
- name,
- seq->scene->id.name + 2,
- ((ID *)seq->scene_camera)->name + 2,
- seq->len);
- }
- else {
- str_len = BLI_snprintf(
- str, sizeof(str), "%s: %s | %d", name, seq->scene->id.name + 2, seq->len);
- }
- }
- else {
- str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len);
+ else if (seq->type == SEQ_TYPE_SOUND_RAM) {
+ if (seq->sound) {
+ BLI_snprintf(r_source, source_len, "%s", seq->sound->filepath);
}
}
- else if (seq->type == SEQ_TYPE_MOVIECLIP) {
- if (seq->clip && !STREQ(name, seq->clip->id.name + 2)) {
- str_len = BLI_snprintf(
- str, sizeof(str), "%s: %s | %d", name, seq->clip->id.name + 2, seq->len);
- }
- else {
- str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len);
- }
+ else if (seq->type == SEQ_TYPE_MULTICAM) {
+ BLI_snprintf(r_source, source_len, "Channel: %d", seq->multicam_source);
}
- else if (seq->type == SEQ_TYPE_MASK) {
- if (seq->mask && !STREQ(name, seq->mask->id.name + 2)) {
- str_len = BLI_snprintf(
- str, sizeof(str), "%s: %s | %d", name, seq->mask->id.name + 2, seq->len);
+ else if (seq->type == SEQ_TYPE_TEXT) {
+ TextVars *textdata = seq->effectdata;
+ BLI_snprintf(r_source, source_len, "%s", textdata->text);
+ }
+ else if (seq->type == SEQ_TYPE_SCENE) {
+ if (seq->scene_camera) {
+ BLI_snprintf(r_source,
+ source_len,
+ "%s (%s)",
+ seq->scene->id.name + 2,
+ ((ID *)seq->scene_camera)->name + 2);
}
else {
- str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len);
+ BLI_snprintf(r_source, source_len, "%s", seq->scene->id.name + 2);
}
}
- else if (seq->type == SEQ_TYPE_MULTICAM) {
- str_len = BLI_snprintf(str, sizeof(str), "Cam %s: %d", name, seq->multicam_source);
- }
- else if (seq->type == SEQ_TYPE_IMAGE) {
- str_len = BLI_snprintf(str,
- sizeof(str),
- "%s: %s%s | %d",
- name,
- seq->strip->dir,
- seq->strip->stripdata->name,
- seq->len);
+ else if (seq->type == SEQ_TYPE_MOVIECLIP) {
+ BLI_snprintf(r_source, source_len, "%s", seq->clip->id.name + 2);
}
- else if (seq->type == SEQ_TYPE_TEXT) {
- TextVars *textdata = seq->effectdata;
- str_len = BLI_snprintf(str, sizeof(str), "%s | %d", textdata->text, seq->startdisp);
+ else if (seq->type == SEQ_TYPE_MASK) {
+ BLI_snprintf(r_source, source_len, "%s", seq->mask->id.name + 2);
}
- else if (seq->type & SEQ_TYPE_EFFECT) {
- str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len);
+ else {
+ *r_source = '\0';
}
- else if (seq->type == SEQ_TYPE_SOUND_RAM) {
- /* If a waveform is drawn, avoid to draw text when there is not enough vertical space. */
- if (!y_threshold && (sseq->flag & SEQ_NO_WAVEFORMS) == 0 &&
- ((sseq->flag & SEQ_ALL_WAVEFORMS) || (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) {
+}
- str[0] = 0;
- str_len = 0;
- }
- else if (seq->sound) {
- str_len = BLI_snprintf(
- str, sizeof(str), "%s: %s | %d", name, seq->sound->filepath, seq->len);
+static size_t draw_seq_text_get_overlay_string(SpaceSeq *sseq,
+ Sequence *seq,
+ char *r_overlay_string,
+ size_t overlay_string_len)
+{
+ const char *name = draw_seq_text_get_name(seq);
+ char source[FILE_MAX];
+ int strip_duration = seq->enddisp - seq->startdisp;
+ draw_seq_text_get_source(seq, source, sizeof(source));
+
+ bool show_name = sseq->flag & SEQ_SHOW_STRIP_NAME;
+ bool show_source = (sseq->flag & (SEQ_SHOW_STRIP_SOURCE)) && source[0] != '\0';
+ bool show_duration = sseq->flag & SEQ_SHOW_STRIP_DURATION;
+
+ size_t string_len = 0;
+ if (show_name) {
+ string_len = BLI_snprintf(r_overlay_string, overlay_string_len, "%s", name);
+ if (show_source || show_duration) {
+ string_len += BLI_snprintf(r_overlay_string + string_len, overlay_string_len, " | ");
}
- else {
- str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len);
+ }
+ if (show_source) {
+ string_len += BLI_snprintf(r_overlay_string + string_len, overlay_string_len, "%s", source);
+ if (show_duration) {
+ string_len += BLI_snprintf(r_overlay_string + string_len, overlay_string_len, " | ");
}
}
- else if (seq->type == SEQ_TYPE_MOVIE) {
- str_len = BLI_snprintf(str,
- sizeof(str),
- "%s: %s%s | %d",
- name,
- seq->strip->dir,
- seq->strip->stripdata->name,
- seq->len);
+ if (show_duration) {
+ string_len += BLI_snprintf(
+ r_overlay_string + string_len, overlay_string_len, "%d", strip_duration);
}
- else {
- /* Should never get here!, but might with files from future. */
- BLI_assert(0);
+ return string_len;
+}
+
+/* Draw info text on a sequence strip. */
+static void draw_seq_text_overlay(View2D *v2d,
+ Sequence *seq,
+ SpaceSeq *sseq,
+ float x1,
+ float x2,
+ float y1,
+ float y2,
+ bool seq_active)
+{
+ char overlay_string[FILE_MAX];
+ size_t overlay_string_len = draw_seq_text_get_overlay_string(
+ sseq, seq, overlay_string, sizeof(overlay_string));
- str_len = BLI_snprintf(str, sizeof(str), "%s | %d", name, seq->len);
+ if (overlay_string_len == 0) {
+ return;
}
/* White text for the active strip. */
+ uchar col[4];
col[0] = col[1] = col[2] = seq_active ? 255 : 10;
col[3] = 255;
@@ -731,15 +720,16 @@ static void draw_seq_text(View2D *v2d,
}
}
+ rctf rect;
rect.xmin = x1;
rect.ymin = y1;
rect.xmax = x2;
rect.ymax = y2;
- UI_view2d_text_cache_add_rectf(v2d, &rect, str, str_len, col);
+ UI_view2d_text_cache_add_rectf(v2d, &rect, overlay_string, overlay_string_len, col);
}
-static void draw_sequence_extensions(Scene *scene, Sequence *seq, uint pos, float pixely)
+static void draw_sequence_extensions_overlay(Scene *scene, Sequence *seq, uint pos, float pixely)
{
float x1, x2, y1, y2;
uchar col[4], blend_col[3];
@@ -988,7 +978,7 @@ static void fcurve_batch_add_verts(GPUVertBuf *vbo,
* - Volume for sound strips.
* - Opacity for the other types.
*/
-static void draw_seq_fcurve(
+static void draw_seq_fcurve_overlay(
Scene *scene, View2D *v2d, Sequence *seq, float x1, float y1, float x2, float y2, float pixelx)
{
FCurve *fcu;
@@ -1085,11 +1075,21 @@ static void draw_seq_strip(const bContext *C,
x2 = (seq->endstill) ? (seq->start + seq->len) : seq->enddisp;
y2 = seq->machine + SEQ_STRIP_OFSTOP;
- /* Calculate height needed for drawing text on strip. */
- float text_margin_y = y2 - min_ff(0.40f, 20 * U.dpi_fac * pixely);
+ float text_margin_y;
+ bool y_threshold;
+ if ((sseq->flag & SEQ_SHOW_STRIP_NAME) || (sseq->flag & SEQ_SHOW_STRIP_SOURCE) ||
+ (sseq->flag & SEQ_SHOW_STRIP_DURATION)) {
- /* Is there enough space for drawing something else than text? */
- bool y_threshold = ((y2 - y1) / pixely) > 20 * U.dpi_fac;
+ /* Calculate height needed for drawing text on strip. */
+ text_margin_y = y2 - min_ff(0.40f, 20 * U.dpi_fac * pixely);
+
+ /* Is there enough space for drawing something else than text? */
+ y_threshold = ((y2 - y1) / pixely) > 20 * U.dpi_fac;
+ }
+ else {
+ text_margin_y = y2;
+ y_threshold = 1;
+ }
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
@@ -1102,12 +1102,13 @@ static void draw_seq_strip(const bContext *C,
}
/* Draw strip offsets when flag is enabled or during "solo preview". */
- if (!is_single_image && (seq->startofs || seq->endofs) && pixely > 0) {
- if ((sseq->draw_flag & SEQ_DRAW_OFFSET_EXT) || (seq == special_seq_update)) {
- draw_sequence_extensions(scene, seq, pos, pixely);
+ if ((sseq->flag & SEQ_SHOW_STRIP_OVERLAY)) {
+ if (!is_single_image && (seq->startofs || seq->endofs) && pixely > 0) {
+ if ((sseq->draw_flag & SEQ_DRAW_OFFSET_EXT) || (seq == special_seq_update)) {
+ draw_sequence_extensions_overlay(scene, seq, pos, pixely);
+ }
}
}
-
immUnbindProgram();
x1 = seq->startdisp;
@@ -1118,24 +1119,24 @@ static void draw_seq_strip(const bContext *C,
drawmeta_contents(scene, seq, x1, y1, x2, y2);
}
- if (sseq->flag & SEQ_SHOW_FCURVES) {
- draw_seq_fcurve(scene, v2d, seq, x1, y1, x2, y2, pixelx);
+ if ((sseq->flag & SEQ_SHOW_STRIP_OVERLAY) && (sseq->flag & SEQ_SHOW_FCURVES)) {
+ draw_seq_fcurve_overlay(scene, v2d, seq, x1, y1, x2, y2, pixelx);
}
/* Draw sound strip waveform. */
- if ((seq->type == SEQ_TYPE_SOUND_RAM) && (sseq->flag & SEQ_NO_WAVEFORMS) == 0) {
- draw_seq_waveform(v2d,
- C,
- sseq,
- scene,
- seq,
- x1,
- y_threshold ? y1 + 0.05f : y1,
- x2,
- y_threshold ? text_margin_y : y2,
- BLI_rctf_size_x(&region->v2d.cur) / region->winx);
+ if ((seq->type == SEQ_TYPE_SOUND_RAM) && ((sseq->flag & SEQ_SHOW_STRIP_OVERLAY)) &&
+ (sseq->flag & SEQ_NO_WAVEFORMS) == 0) {
+ draw_seq_waveform_overlay(v2d,
+ C,
+ sseq,
+ scene,
+ seq,
+ x1,
+ y_threshold ? y1 + 0.05f : y1,
+ x2,
+ y_threshold ? text_margin_y : y2,
+ BLI_rctf_size_x(&region->v2d.cur) / region->winx);
}
-
/* Draw locked state. */
if (seq->flag & SEQ_LOCK) {
draw_seq_locked(x1, y1, x2, y2);
@@ -1162,11 +1163,21 @@ static void draw_seq_strip(const bContext *C,
calculate_seq_text_offsets(v2d, seq, &x1, &x2, pixelx);
- /* Don't draw strip if there is not enough vertical or horizontal space. */
- if (((x2 - x1) > 32 * pixelx * U.dpi_fac) && ((y2 - y1) > 8 * pixely * U.dpi_fac)) {
- /* Depending on the vertical space, draw text on top or in the center of strip. */
- draw_seq_text(
- v2d, seq, sseq, x1, x2, y_threshold ? text_margin_y : y1, y2, seq_active, y_threshold);
+ /* If a waveform is drawn, avoid drawing text when there is not enough vertical space. */
+ if (seq->type == SEQ_TYPE_SOUND_RAM) {
+ if (!y_threshold && (sseq->flag & SEQ_NO_WAVEFORMS) == 0 &&
+ ((sseq->flag & SEQ_ALL_WAVEFORMS) || (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) {
+ return;
+ }
+ }
+
+ if (sseq->flag & SEQ_SHOW_STRIP_OVERLAY) {
+ /* Don't draw strip if there is not enough vertical or horizontal space. */
+ if (((x2 - x1) > 32 * pixelx * U.dpi_fac) && ((y2 - y1) > 8 * pixely * U.dpi_fac)) {
+ /* Depending on the vertical space, draw text on top or in the center of strip. */
+ draw_seq_text_overlay(
+ v2d, seq, sseq, x1, x2, y_threshold ? text_margin_y : y1, y2, seq_active);
+ }
}
}
@@ -1360,7 +1371,7 @@ static void sequencer_display_size(Scene *scene, float r_viewrect[2])
r_viewrect[0] *= scene->r.xasp / scene->r.yasp;
}
-static void sequencer_draw_gpencil(const bContext *C)
+static void sequencer_draw_gpencil_overlay(const bContext *C)
{
/* Draw grease-pencil (image aligned). */
ED_annotation_draw_2dimage(C);
@@ -1373,7 +1384,9 @@ static void sequencer_draw_gpencil(const bContext *C)
}
/* Draw content and safety borders borders. */
-static void sequencer_draw_borders(const SpaceSeq *sseq, const View2D *v2d, const Scene *scene)
+static void sequencer_draw_borders_overlay(const SpaceSeq *sseq,
+ const View2D *v2d,
+ const Scene *scene)
{
float x1 = v2d->tot.xmin;
float y1 = v2d->tot.ymin;
@@ -1825,17 +1838,17 @@ void sequencer_draw_preview(const bContext *C,
C, scene, region, sseq, ibuf, scope, draw_overlay, draw_backdrop);
/* Draw over image. */
- if (sseq->flag & SEQ_SHOW_METADATA) {
+ if (sseq->flag & SEQ_SHOW_METADATA && sseq->flag & SEQ_SHOW_STRIP_OVERLAY) {
ED_region_image_metadata_draw(0.0, 0.0, ibuf, &v2d->tot, 1.0, 1.0);
}
}
- if (show_imbuf) {
- sequencer_draw_borders(sseq, v2d, scene);
+ if (show_imbuf && (sseq->flag & SEQ_SHOW_STRIP_OVERLAY)) {
+ sequencer_draw_borders_overlay(sseq, v2d, scene);
}
- if (draw_gpencil && show_imbuf) {
- sequencer_draw_gpencil(C);
+ if (draw_gpencil && show_imbuf && (sseq->flag & SEQ_SHOW_STRIP_OVERLAY)) {
+ sequencer_draw_gpencil_overlay(C);
}
#if 0
sequencer_draw_maskedit(C, scene, region, sseq);
@@ -2292,7 +2305,7 @@ void draw_timeline_seq(const bContext *C, ARegion *region)
UI_view2d_view_ortho(v2d);
/* Get timeline bound-box, needed for the scroll-bars. */
- boundbox_seq(scene, &v2d->tot);
+ SEQ_timeline_boundbox(scene, SEQ_active_seqbase_get(ed), &v2d->tot);
draw_seq_backdrop(v2d);
UI_view2d_constant_grid_draw(v2d, FPS);
diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index 93b17830c0f..ddc9ba2e0f6 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -183,117 +183,13 @@ bool sequencer_view_strips_poll(bContext *C)
/** \name Remove Gaps Operator
* \{ */
-static bool sequence_offset_after_frame(Scene *scene, const int delta, const int timeline_frame)
-{
- Sequence *seq;
- Editing *ed = BKE_sequencer_editing_get(scene, false);
- bool done = false;
- TimeMarker *marker;
-
- /* All strips >= timeline_frame are shifted. */
-
- if (ed == NULL) {
- return 0;
- }
-
- for (seq = ed->seqbasep->first; seq; seq = seq->next) {
- if (seq->startdisp >= timeline_frame) {
- BKE_sequence_translate(scene, seq, delta);
- BKE_sequence_calc(scene, seq);
- BKE_sequence_invalidate_cache_preprocessed(scene, seq);
- done = true;
- }
- }
-
- if (!scene->toolsettings->lock_markers) {
- for (marker = scene->markers.first; marker; marker = marker->next) {
- if (marker->frame >= timeline_frame) {
- marker->frame += delta;
- }
- }
- }
-
- return done;
-}
-
-void boundbox_seq(Scene *scene, rctf *rect)
-{
- Sequence *seq;
- Editing *ed = BKE_sequencer_editing_get(scene, false);
- float min[2], max[2];
-
- if (ed == NULL) {
- return;
- }
-
- min[0] = SFRA;
- max[0] = EFRA + 1;
- min[1] = 0.0;
- max[1] = 8.0;
-
- seq = ed->seqbasep->first;
- while (seq) {
-
- if (min[0] > seq->startdisp - 1) {
- min[0] = seq->startdisp - 1;
- }
- if (max[0] < seq->enddisp + 1) {
- max[0] = seq->enddisp + 1;
- }
- if (max[1] < seq->machine + 2) {
- max[1] = seq->machine + 2;
- }
-
- seq = seq->next;
- }
-
- rect->xmin = min[0];
- rect->xmax = max[0];
- rect->ymin = min[1];
- rect->ymax = max[1];
-}
-
static int sequencer_gap_remove_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
- rctf rectf;
- int timeline_frame, efra, sfra;
- bool first = false, done;
- bool do_all = RNA_boolean_get(op->ptr, "all");
-
- /* Get first and last frame. */
- boundbox_seq(scene, &rectf);
- sfra = (int)rectf.xmin;
- efra = (int)rectf.xmax;
-
- /* Check if the current frame has a gap already. */
- for (timeline_frame = CFRA; timeline_frame >= sfra; timeline_frame--) {
- if (SEQ_render_evaluate_frame(scene, timeline_frame)) {
- first = true;
- break;
- }
- }
+ const bool do_all = RNA_boolean_get(op->ptr, "all");
+ const Editing *ed = BKE_sequencer_editing_get(scene, false);
- for (; timeline_frame < efra; timeline_frame++) {
- /* There's still no strip to remove a gap for. */
- if (first == false) {
- if (SEQ_render_evaluate_frame(scene, timeline_frame)) {
- first = true;
- }
- }
- else if (SEQ_render_evaluate_frame(scene, timeline_frame) == 0) {
- done = true;
- while (SEQ_render_evaluate_frame(scene, timeline_frame) == 0) {
- done = sequence_offset_after_frame(scene, -1, timeline_frame);
- if (done == false) {
- break;
- }
- }
- if (done == false || do_all == false) {
- break;
- }
- }
- }
+ SEQ_edit_remove_gaps(scene, ed->seqbasep, CFRA, do_all);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
@@ -330,9 +226,9 @@ void SEQUENCER_OT_gap_remove(struct wmOperatorType *ot)
static int sequencer_gap_insert_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
- int frames = RNA_int_get(op->ptr, "frames");
-
- sequence_offset_after_frame(scene, frames, CFRA);
+ const int frames = RNA_int_get(op->ptr, "frames");
+ const Editing *ed = BKE_sequencer_editing_get(scene, false);
+ SEQ_offset_after_frame(scene, ed->seqbasep, frames, CFRA);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
@@ -537,7 +433,7 @@ static int slip_add_sequences_recursive(
for (seq = seqbasep->first; seq; seq = seq->next) {
if (!do_trim || (!(seq->type & SEQ_TYPE_EFFECT) && (seq->flag & SELECT))) {
seq_array[offset + num_items] = seq;
- trim[offset + num_items] = do_trim;
+ trim[offset + num_items] = do_trim && ((seq->type & SEQ_TYPE_EFFECT) == 0);
num_items++;
if (seq->type == SEQ_TYPE_META) {
@@ -545,9 +441,6 @@ static int slip_add_sequences_recursive(
num_items += slip_add_sequences_recursive(
&seq->seqbase, seq_array, trim, num_items + offset, false);
}
- else if (seq->type & SEQ_TYPE_EFFECT) {
- trim[offset + num_items] = false;
- }
}
}
@@ -2634,7 +2527,7 @@ void ED_sequencer_deselect_all(Scene *scene)
SEQ_CURRENT_END;
}
-static int sequencer_paste_exec(bContext *C, wmOperator *UNUSED(op))
+static int sequencer_paste_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
@@ -2643,8 +2536,25 @@ static int sequencer_paste_exec(bContext *C, wmOperator *UNUSED(op))
int ofs;
Sequence *iseq, *iseq_first;
+ if (BLI_listbase_count(&seqbase_clipboard) == 0) {
+ BKE_report(op->reports, RPT_INFO, "No strips to paste");
+ return OPERATOR_CANCELLED;
+ }
+
ED_sequencer_deselect_all(scene);
- ofs = scene->r.cfra - seqbase_clipboard_frame;
+ if (RNA_boolean_get(op->ptr, "keep_offset")) {
+ ofs = scene->r.cfra - seqbase_clipboard_frame;
+ }
+ else {
+ int min_seq_startdisp = INT_MAX;
+ LISTBASE_FOREACH (Sequence *, seq, &seqbase_clipboard) {
+ if (seq->startdisp < min_seq_startdisp) {
+ min_seq_startdisp = seq->startdisp;
+ }
+ }
+ /* Paste strips after playhead. */
+ ofs = scene->r.cfra - min_seq_startdisp;
+ }
/* Copy strips, temporarily restoring pointers to actual data-blocks. This
* must happen on the clipboard itself, so that copying does user counting
@@ -2692,6 +2602,11 @@ void SEQUENCER_OT_paste(wmOperatorType *ot)
/* Flags. */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* Properties. */
+ PropertyRNA *prop = RNA_def_boolean(
+ ot->srna, "keep_offset", false, "Keep Offset", "Keep strip offset to playhead when pasting");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/** \} */
@@ -3499,3 +3414,148 @@ Sequence *find_nearest_seq(Scene *scene, View2D *v2d, int *hand, const int mval[
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Clear Strip Transform Operator
+ * \{ */
+
+enum {
+ STRIP_TRANSFORM_POSITION,
+ STRIP_TRANSFORM_SCALE,
+ STRIP_TRANSFORM_ROTATION,
+ STRIP_TRANSFORM_ALL,
+};
+
+static const EnumPropertyItem transform_reset_properties[] = {
+ {STRIP_TRANSFORM_POSITION, "POSITION", 0, "Position", "Reset strip transform location"},
+ {STRIP_TRANSFORM_SCALE, "SCALE", 0, "Scale", "Reset strip transform scale"},
+ {STRIP_TRANSFORM_ROTATION, "ROTATION", 0, "Rotation", "Reset strip transform rotation"},
+ {STRIP_TRANSFORM_ALL, "ALL", 0, "All", "Reset strip transform location, scale and rotation"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static int sequencer_strip_transform_clear_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ const Editing *ed = BKE_sequencer_editing_get(scene, false);
+ Sequence *seq;
+ const int property = RNA_enum_get(op->ptr, "property");
+
+ for (seq = ed->seqbasep->first; seq; seq = seq->next) {
+ if (seq->flag & SELECT && seq->type != SEQ_TYPE_SOUND_RAM) {
+ StripTransform *transform = seq->strip->transform;
+ switch (property) {
+ case STRIP_TRANSFORM_POSITION:
+ transform->xofs = 0;
+ transform->yofs = 0;
+ break;
+ case STRIP_TRANSFORM_SCALE:
+ transform->scale_x = 1.0f;
+ transform->scale_y = 1.0f;
+ break;
+ case STRIP_TRANSFORM_ROTATION:
+ transform->rotation = 0.0f;
+ break;
+ case STRIP_TRANSFORM_ALL:
+ transform->xofs = 0;
+ transform->yofs = 0;
+ transform->scale_x = 1.0f;
+ transform->scale_y = 1.0f;
+ transform->rotation = 0.0f;
+ break;
+ }
+ BKE_sequence_invalidate_cache_preprocessed(scene, seq);
+ }
+ }
+
+ WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
+ return OPERATOR_FINISHED;
+}
+
+void SEQUENCER_OT_strip_transform_clear(struct wmOperatorType *ot)
+{
+ /* Identifiers. */
+ ot->name = "Clear Strip Transform";
+ ot->idname = "SEQUENCER_OT_strip_transform_clear";
+ ot->description = "Reset image transformation to default value";
+
+ /* Api callbacks. */
+ ot->exec = sequencer_strip_transform_clear_exec;
+ ot->poll = sequencer_edit_poll;
+
+ /* Flags. */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ ot->prop = RNA_def_enum(ot->srna,
+ "property",
+ transform_reset_properties,
+ STRIP_TRANSFORM_ALL,
+ "Property",
+ "Strip transform property to be reset");
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Transform Set Fit Operator
+ * \{ */
+
+static const EnumPropertyItem scale_fit_methods[] = {
+ {SEQ_SCALE_TO_FIT, "FIT", 0, "Scale to Fit", "Scale image so fits in preview"},
+ {SEQ_SCALE_TO_FILL, "FILL", 0, "Scale to Fill", "Scale image so it fills preview completely"},
+ {SEQ_STRETCH_TO_FILL, "STRETCH", 0, "Stretch to Fill", "Stretch image so it fills preview"},
+ {0, NULL, 0, NULL, NULL},
+};
+
+static int sequencer_strip_transform_fit_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene = CTX_data_scene(C);
+ const Editing *ed = BKE_sequencer_editing_get(scene, false);
+ Sequence *seq;
+ const eSeqImageFitMethod fit_method = RNA_enum_get(op->ptr, "fit_method");
+
+ for (seq = ed->seqbasep->first; seq; seq = seq->next) {
+ if (seq->flag & SELECT && seq->type != SEQ_TYPE_SOUND_RAM) {
+ const int timeline_frame = CFRA;
+ StripElem *strip_elem = SEQ_render_give_stripelem(seq, timeline_frame);
+
+ if (strip_elem == NULL) {
+ continue;
+ }
+
+ SEQ_set_scale_to_fit(seq,
+ strip_elem->orig_width,
+ strip_elem->orig_height,
+ scene->r.xsch,
+ scene->r.ysch,
+ fit_method);
+ BKE_sequence_invalidate_cache_preprocessed(scene, seq);
+ }
+ }
+
+ WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
+ return OPERATOR_FINISHED;
+}
+
+void SEQUENCER_OT_strip_transform_fit(struct wmOperatorType *ot)
+{
+ /* Identifiers. */
+ ot->name = "Strip Transform Set Fit";
+ ot->idname = "SEQUENCER_OT_strip_transform_fit";
+
+ /* Api callbacks. */
+ ot->exec = sequencer_strip_transform_fit_exec;
+ ot->poll = sequencer_edit_poll;
+
+ /* Flags. */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ ot->prop = RNA_def_enum(ot->srna,
+ "fit_method",
+ scale_fit_methods,
+ SEQ_SCALE_TO_FIT,
+ "Fit Method",
+ "Scale fit fit_method");
+}
+
+/** \} */
diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h
index 1ea4fb05d53..4c942a83f2b 100644
--- a/source/blender/editors/space_sequencer/sequencer_intern.h
+++ b/source/blender/editors/space_sequencer/sequencer_intern.h
@@ -71,7 +71,6 @@ struct ImBuf *sequencer_ibuf_get(struct Main *bmain,
/* sequencer_edit.c */
struct View2D;
void seq_rectf(struct Sequence *seq, struct rctf *rectf);
-void boundbox_seq(struct Scene *scene, struct rctf *rect);
struct Sequence *find_nearest_seq(struct Scene *scene,
struct View2D *v2d,
int *hand,
@@ -145,6 +144,8 @@ void SEQUENCER_OT_enable_proxies(struct wmOperatorType *ot);
void SEQUENCER_OT_export_subtitles(struct wmOperatorType *ot);
void SEQUENCER_OT_set_range_to_strips(struct wmOperatorType *ot);
+void SEQUENCER_OT_strip_transform_clear(struct wmOperatorType *ot);
+void SEQUENCER_OT_strip_transform_fit(struct wmOperatorType *ot);
/* sequencer_select.c */
void SEQUENCER_OT_select_all(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c
index bdf6e4ece7f..7bfc8600544 100644
--- a/source/blender/editors/space_sequencer/sequencer_ops.c
+++ b/source/blender/editors/space_sequencer/sequencer_ops.c
@@ -81,6 +81,8 @@ void sequencer_operatortypes(void)
WM_operatortype_append(SEQUENCER_OT_change_path);
WM_operatortype_append(SEQUENCER_OT_set_range_to_strips);
+ WM_operatortype_append(SEQUENCER_OT_strip_transform_clear);
+ WM_operatortype_append(SEQUENCER_OT_strip_transform_fit);
/* sequencer_select.c */
WM_operatortype_append(SEQUENCER_OT_select_all);
diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c
index 75d92d5f00d..e12c43b7804 100644
--- a/source/blender/editors/space_sequencer/sequencer_view.c
+++ b/source/blender/editors/space_sequencer/sequencer_view.c
@@ -87,8 +87,10 @@ static int sequencer_view_all_exec(bContext *C, wmOperator *op)
rctf box;
const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
+ Scene *scene = CTX_data_scene(C);
+ const Editing *ed = BKE_sequencer_editing_get(scene, false);
- boundbox_seq(CTX_data_scene(C), &box);
+ SEQ_timeline_boundbox(scene, SEQ_active_seqbase_get(ed), &box);
UI_view2d_smooth_view(C, region, &box, smooth_viewtx);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c
index 45c7bac54f8..2bf4741e4f5 100644
--- a/source/blender/editors/space_sequencer/space_sequencer.c
+++ b/source/blender/editors/space_sequencer/space_sequencer.c
@@ -99,7 +99,8 @@ static SpaceLink *sequencer_create(const ScrArea *UNUSED(area), const Scene *sce
sseq->view = SEQ_VIEW_SEQUENCE;
sseq->mainb = SEQ_DRAW_IMG_IMBUF;
sseq->flag = SEQ_SHOW_GPENCIL | SEQ_USE_ALPHA | SEQ_SHOW_MARKERS | SEQ_SHOW_FCURVES |
- SEQ_ZOOM_TO_FIT;
+ SEQ_ZOOM_TO_FIT | SEQ_SHOW_STRIP_OVERLAY | SEQ_SHOW_STRIP_NAME |
+ SEQ_SHOW_STRIP_SOURCE | SEQ_SHOW_STRIP_DURATION;
/* Tool header. */
region = MEM_callocN(sizeof(ARegion), "tool header for sequencer");
@@ -706,7 +707,8 @@ static void sequencer_preview_region_draw(const bContext *C, ARegion *region)
SpaceSeq *sseq = area->spacedata.first;
Scene *scene = CTX_data_scene(C);
wmWindowManager *wm = CTX_wm_manager(C);
- const bool draw_overlay = (scene->ed && (scene->ed->over_flag & SEQ_EDIT_OVERLAY_SHOW));
+ const bool draw_overlay = (scene->ed && (scene->ed->over_flag & SEQ_EDIT_OVERLAY_SHOW) &&
+ (sseq->flag & SEQ_SHOW_STRIP_OVERLAY));
/* XXX temp fix for wrong setting in sseq->mainb */
if (sseq->mainb == SEQ_DRAW_SEQUENCE) {
diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c
index bb71a9b11be..0f5ac5abe1d 100644
--- a/source/blender/editors/space_text/space_text.c
+++ b/source/blender/editors/space_text/space_text.c
@@ -357,7 +357,7 @@ static bool text_drop_paste_poll(bContext *UNUSED(C),
static void text_drop_paste(wmDrag *drag, wmDropBox *drop)
{
char *text;
- ID *id = WM_drag_ID(drag, 0);
+ ID *id = WM_drag_get_local_ID(drag, 0);
/* copy drag path to properties */
text = RNA_path_full_ID_py(G_MAIN, id);
diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c
index f8b7c62686f..932bacfb8a0 100644
--- a/source/blender/editors/space_text/text_ops.c
+++ b/source/blender/editors/space_text/text_ops.c
@@ -2588,7 +2588,7 @@ static int text_scroll_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- txt_screen_skip(st, region, lines * U.wheellinescroll);
+ txt_screen_skip(st, region, lines * 3);
ED_area_tag_redraw(CTX_wm_area(C));
diff --git a/source/blender/editors/space_userpref/userpref_ops.c b/source/blender/editors/space_userpref/userpref_ops.c
index d823530fd89..ee23cde78c2 100644
--- a/source/blender/editors/space_userpref/userpref_ops.c
+++ b/source/blender/editors/space_userpref/userpref_ops.c
@@ -30,6 +30,7 @@
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_main.h"
+#include "BKE_preferences.h"
#include "BKE_report.h"
#include "RNA_access.h"
@@ -133,9 +134,71 @@ static void PREFERENCES_OT_autoexec_path_remove(wmOperatorType *ot)
/** \} */
+/* -------------------------------------------------------------------- */
+/** \name Add Asset Library Operator
+ * \{ */
+
+static int preferences_asset_library_add_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
+{
+ BKE_preferences_asset_library_add(&U, NULL, NULL);
+ U.runtime.is_dirty = true;
+ return OPERATOR_FINISHED;
+}
+
+static void PREFERENCES_OT_asset_library_add(wmOperatorType *ot)
+{
+ ot->name = "Add Asset Library";
+ ot->idname = "PREFERENCES_OT_asset_library_add";
+ ot->description =
+ "Add a path to a .blend file to be used by the Asset Browser as source of assets";
+
+ ot->exec = preferences_asset_library_add_exec;
+
+ ot->flag = OPTYPE_INTERNAL;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Remove Asset Library Operator
+ * \{ */
+
+static int preferences_asset_library_remove_exec(bContext *UNUSED(C), wmOperator *op)
+{
+ const int index = RNA_int_get(op->ptr, "index");
+ bUserAssetLibrary *library = BLI_findlink(&U.asset_libraries, index);
+ if (library) {
+ BKE_preferences_asset_library_remove(&U, library);
+ U.runtime.is_dirty = true;
+ /* Trigger refresh for the Asset Browser. */
+ WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, NULL);
+ }
+ return OPERATOR_FINISHED;
+}
+
+static void PREFERENCES_OT_asset_library_remove(wmOperatorType *ot)
+{
+ ot->name = "Remove Asset Library";
+ ot->idname = "PREFERENCES_OT_asset_library_remove";
+ ot->description =
+ "Remove a path to a .blend file, so the Asset Browser will not attempt to show it anymore";
+
+ ot->exec = preferences_asset_library_remove_exec;
+
+ ot->flag = OPTYPE_INTERNAL;
+
+ RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "", 0, 1000);
+}
+
+/** \} */
+
void ED_operatortypes_userpref(void)
{
WM_operatortype_append(PREFERENCES_OT_reset_default_theme);
+
WM_operatortype_append(PREFERENCES_OT_autoexec_path_add);
WM_operatortype_append(PREFERENCES_OT_autoexec_path_remove);
+
+ WM_operatortype_append(PREFERENCES_OT_asset_library_add);
+ WM_operatortype_append(PREFERENCES_OT_asset_library_remove);
}
diff --git a/source/blender/editors/space_view3d/CMakeLists.txt b/source/blender/editors/space_view3d/CMakeLists.txt
index 0371b4e271f..9242fc15021 100644
--- a/source/blender/editors/space_view3d/CMakeLists.txt
+++ b/source/blender/editors/space_view3d/CMakeLists.txt
@@ -22,13 +22,13 @@ set(INC
../../blenlib
../../blentranslation
../../bmesh
+ ../../depsgraph
../../draw
../../gpu
../../imbuf
../../makesdna
../../makesrna
../../render
- ../../depsgraph
../../windowmanager
../../../../intern/glew-mx
../../../../intern/guardedalloc
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index 9f31e7a411d..3761f4ad7c6 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -461,6 +461,12 @@ static void view3d_main_region_exit(wmWindowManager *wm, ARegion *region)
ED_view3d_stop_render_preview(wm, region);
}
+static bool view3d_drop_in_main_region_poll(bContext *C, const wmEvent *event)
+{
+ ScrArea *area = CTX_wm_area(C);
+ return ED_region_overlap_isect_any_xy(area, &event->x) == false;
+}
+
static ID *view3d_drop_id_in_main_region_poll_id(bContext *C,
wmDrag *drag,
const wmEvent *event,
@@ -470,7 +476,7 @@ static ID *view3d_drop_id_in_main_region_poll_id(bContext *C,
if (ED_region_overlap_isect_any_xy(area, &event->x)) {
return NULL;
}
- return WM_drag_ID(drag, id_type);
+ return view3d_drop_in_main_region_poll(C, event) ? WM_drag_get_local_ID(drag, id_type) : NULL;
}
static bool view3d_drop_id_in_main_region_poll(bContext *C,
@@ -478,7 +484,11 @@ static bool view3d_drop_id_in_main_region_poll(bContext *C,
const wmEvent *event,
ID_Type id_type)
{
- return (view3d_drop_id_in_main_region_poll_id(C, drag, event, id_type) != NULL);
+ if (!view3d_drop_in_main_region_poll(C, event)) {
+ return false;
+ }
+
+ return WM_drag_get_local_ID(drag, id_type) || WM_drag_get_asset_data(drag, id_type);
}
static bool view3d_ob_drop_poll(bContext *C,
@@ -533,7 +543,7 @@ static bool view3d_ima_drop_poll(bContext *C,
return (ELEM(drag->icon, 0, ICON_FILE_IMAGE, ICON_FILE_MOVIE));
}
- return WM_drag_ID(drag, ID_IM) != NULL;
+ return WM_drag_get_local_ID(drag, ID_IM) || WM_drag_get_asset_data(drag, ID_IM);
}
static bool view3d_ima_bg_is_camera_view(bContext *C)
@@ -596,14 +606,14 @@ static bool view3d_volume_drop_poll(bContext *UNUSED(C),
static void view3d_ob_drop_copy(wmDrag *drag, wmDropBox *drop)
{
- ID *id = WM_drag_ID(drag, ID_OB);
+ ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, ID_OB);
RNA_string_set(drop->ptr, "name", id->name + 2);
}
static void view3d_collection_drop_copy(wmDrag *drag, wmDropBox *drop)
{
- ID *id = WM_drag_ID(drag, ID_GR);
+ ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, ID_GR);
drop->opcontext = WM_OP_EXEC_DEFAULT;
RNA_string_set(drop->ptr, "name", id->name + 2);
@@ -611,14 +621,14 @@ static void view3d_collection_drop_copy(wmDrag *drag, wmDropBox *drop)
static void view3d_id_drop_copy(wmDrag *drag, wmDropBox *drop)
{
- ID *id = WM_drag_ID(drag, 0);
+ ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0);
RNA_string_set(drop->ptr, "name", id->name + 2);
}
static void view3d_id_drop_copy_with_type(wmDrag *drag, wmDropBox *drop)
{
- ID *id = WM_drag_ID(drag, 0);
+ ID *id = WM_drag_get_local_ID(drag, 0);
RNA_string_set(drop->ptr, "name", id->name + 2);
RNA_enum_set(drop->ptr, "type", GS(id->name));
@@ -626,7 +636,7 @@ static void view3d_id_drop_copy_with_type(wmDrag *drag, wmDropBox *drop)
static void view3d_id_path_drop_copy(wmDrag *drag, wmDropBox *drop)
{
- ID *id = WM_drag_ID(drag, 0);
+ ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0);
if (id) {
RNA_string_set(drop->ptr, "name", id->name + 2);
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index 5b400bbf60a..96bd25f85e7 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -2457,13 +2457,16 @@ static int view3d_select_exec(bContext *C, wmOperator *op)
else if (obact && BKE_paint_select_face_test(obact)) {
retval = paintface_mouse_select(C, obact, location, extend, deselect, toggle);
if (!retval && deselect_all) {
- retval = paintface_deselect_all_visible(C, CTX_data_active_object(C), SEL_DESELECT, false);
+ retval = paintface_deselect_all_visible(C, CTX_data_active_object(C), SEL_DESELECT, true);
}
}
else if (BKE_paint_select_vert_test(obact)) {
retval = ed_wpaint_vertex_select_pick(C, location, extend, deselect, toggle, obact);
if (!retval && deselect_all) {
retval = paintvert_deselect_all_visible(obact, SEL_DESELECT, false);
+ if (retval) {
+ paintvert_tag_select_update(C, obact);
+ }
}
}
else {
diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c
index 1be9bd27c7a..2b7b8255068 100644
--- a/source/blender/editors/space_view3d/view3d_utils.c
+++ b/source/blender/editors/space_view3d/view3d_utils.c
@@ -1620,6 +1620,41 @@ void ED_view3d_to_object(const Depsgraph *depsgraph,
BKE_object_apply_mat4_ex(ob, mat, ob_eval->parent, ob_eval->parentinv, true);
}
+bool ED_view3d_camera_to_view_selected(struct Main *bmain,
+ Depsgraph *depsgraph,
+ const Scene *scene,
+ Object *camera_ob)
+{
+ Object *camera_ob_eval = DEG_get_evaluated_object(depsgraph, camera_ob);
+ float co[3]; /* the new location to apply */
+ float scale; /* only for ortho cameras */
+
+ if (BKE_camera_view_frame_fit_to_scene(depsgraph, scene, camera_ob_eval, co, &scale)) {
+ ObjectTfmProtectedChannels obtfm;
+ float obmat_new[4][4];
+
+ if ((camera_ob_eval->type == OB_CAMERA) &&
+ (((Camera *)camera_ob_eval->data)->type == CAM_ORTHO)) {
+ ((Camera *)camera_ob->data)->ortho_scale = scale;
+ }
+
+ copy_m4_m4(obmat_new, camera_ob_eval->obmat);
+ copy_v3_v3(obmat_new[3], co);
+
+ /* only touch location */
+ BKE_object_tfm_protected_backup(camera_ob, &obtfm);
+ BKE_object_apply_mat4(camera_ob, obmat_new, true, true);
+ BKE_object_tfm_protected_restore(camera_ob, &obtfm, OB_LOCK_SCALE | OB_LOCK_ROT4D);
+
+ /* notifiers */
+ DEG_id_tag_update_ex(bmain, &camera_ob->id, ID_RECALC_TRANSFORM);
+
+ return true;
+ }
+
+ return false;
+}
+
/** \} */
/* -------------------------------------------------------------------- */
diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c
index a24f59019f0..9d947384bf0 100644
--- a/source/blender/editors/space_view3d/view3d_view.c
+++ b/source/blender/editors/space_view3d/view3d_view.c
@@ -535,40 +535,18 @@ void VIEW3D_OT_camera_to_view(wmOperatorType *ot)
* meant to take into account vertex/bone selection for eg. */
static int view3d_camera_to_view_selected_exec(bContext *C, wmOperator *op)
{
+ Main *bmain = CTX_data_main(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Scene *scene = CTX_data_scene(C);
View3D *v3d = CTX_wm_view3d(C); /* can be NULL */
Object *camera_ob = v3d ? v3d->camera : scene->camera;
- Object *camera_ob_eval = DEG_get_evaluated_object(depsgraph, camera_ob);
-
- float r_co[3]; /* the new location to apply */
- float r_scale; /* only for ortho cameras */
- if (camera_ob_eval == NULL) {
+ if (camera_ob == NULL) {
BKE_report(op->reports, RPT_ERROR, "No active camera");
return OPERATOR_CANCELLED;
}
- /* this function does all the important stuff */
- if (BKE_camera_view_frame_fit_to_scene(depsgraph, scene, camera_ob_eval, r_co, &r_scale)) {
- ObjectTfmProtectedChannels obtfm;
- float obmat_new[4][4];
-
- if ((camera_ob_eval->type == OB_CAMERA) &&
- (((Camera *)camera_ob_eval->data)->type == CAM_ORTHO)) {
- ((Camera *)camera_ob->data)->ortho_scale = r_scale;
- }
-
- copy_m4_m4(obmat_new, camera_ob_eval->obmat);
- copy_v3_v3(obmat_new[3], r_co);
-
- /* only touch location */
- BKE_object_tfm_protected_backup(camera_ob, &obtfm);
- BKE_object_apply_mat4(camera_ob, obmat_new, true, true);
- BKE_object_tfm_protected_restore(camera_ob, &obtfm, OB_LOCK_SCALE | OB_LOCK_ROT4D);
-
- /* notifiers */
- DEG_id_tag_update(&camera_ob->id, ID_RECALC_TRANSFORM);
+ if (ED_view3d_camera_to_view_selected(bmain, depsgraph, scene, camera_ob)) {
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, camera_ob);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/transform/CMakeLists.txt b/source/blender/editors/transform/CMakeLists.txt
index 73d6a376da6..a2eb68a1b71 100644
--- a/source/blender/editors/transform/CMakeLists.txt
+++ b/source/blender/editors/transform/CMakeLists.txt
@@ -22,12 +22,12 @@ set(INC
../../blenlib
../../blentranslation
../../bmesh
+ ../../depsgraph
../../gpu
../../ikplugin
../../makesdna
../../makesrna
../../render
- ../../depsgraph
../../sequencer
../../windowmanager
../../../../intern/glew-mx
diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h
index 227330e8524..91bf2bf8aac 100644
--- a/source/blender/editors/transform/transform.h
+++ b/source/blender/editors/transform/transform.h
@@ -52,12 +52,12 @@ struct SnapObjectContext;
struct TransDataContainer;
struct TransInfo;
struct TransSnap;
-struct TransformOrientation;
struct ViewLayer;
struct bContext;
struct wmEvent;
struct wmKeyConfig;
struct wmKeyMap;
+struct wmOperator;
struct wmTimer;
/** #TransInfo.redraw */
diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h
index b753572ea7b..59fcd016020 100644
--- a/source/blender/editors/transform/transform_convert.h
+++ b/source/blender/editors/transform/transform_convert.h
@@ -29,12 +29,9 @@ struct FCurve;
struct ListBase;
struct Object;
struct TransData;
-struct TransDataContainer;
struct TransDataCurveHandleFlags;
struct TransInfo;
struct bContext;
-struct bKinematicConstraint;
-struct bPoseChannel;
/* transform_convert.c */
void transform_autoik_update(TransInfo *t, short mode);
diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c
index 90c1f241338..8597c372537 100644
--- a/source/blender/editors/transform/transform_mode_translate.c
+++ b/source/blender/editors/transform/transform_mode_translate.c
@@ -377,6 +377,9 @@ static void applyTranslation(TransInfo *t, const int UNUSED(mval[2]))
else {
mul_v3_m3v3(global_dir, t->spacemtx, global_dir);
}
+ if (t->flag & T_2D_EDIT) {
+ removeAspectRatio(t, global_dir);
+ }
}
else {
copy_v3_v3(global_dir, t->values);
diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c
index 5153fdedcae..5d758a6c6c6 100644
--- a/source/blender/editors/transform/transform_ops.c
+++ b/source/blender/editors/transform/transform_ops.c
@@ -717,7 +717,7 @@ void Transform_Properties(struct wmOperatorType *ot, int flags)
"use_automerge_and_split",
0,
"Auto Merge & Split",
- "Forces the use of Auto Merge & Split");
+ "Forces the use of Auto Merge and Split");
RNA_def_property_flag(prop, PROP_HIDDEN);
}
}
diff --git a/source/blender/editors/transform/transform_snap.h b/source/blender/editors/transform/transform_snap.h
index 5bee572c603..db8ec943bfd 100644
--- a/source/blender/editors/transform/transform_snap.h
+++ b/source/blender/editors/transform/transform_snap.h
@@ -25,8 +25,6 @@
/* For enum. */
#include "DNA_space_types.h"
-struct SnapObjectParams;
-
bool peelObjectsTransform(struct TransInfo *t,
const float mval[2],
const bool use_peel_object,
diff --git a/source/blender/editors/undo/memfile_undo.c b/source/blender/editors/undo/memfile_undo.c
index 49417a54472..4fded419b5b 100644
--- a/source/blender/editors/undo/memfile_undo.c
+++ b/source/blender/editors/undo/memfile_undo.c
@@ -27,6 +27,7 @@
#include "BLI_listbase.h"
#include "DNA_ID.h"
+#include "DNA_collection_types.h"
#include "DNA_node_types.h"
#include "DNA_object_enums.h"
#include "DNA_object_types.h"
diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt
index c88169778f7..bbaf5d5475a 100644
--- a/source/blender/editors/util/CMakeLists.txt
+++ b/source/blender/editors/util/CMakeLists.txt
@@ -47,6 +47,7 @@ set(SRC
../include/BIF_glutil.h
../include/ED_anim_api.h
../include/ED_armature.h
+ ../include/ED_asset.h
../include/ED_buttons.h
../include/ED_clip.h
../include/ED_curve.h
diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c
index 76c261c9cba..d78758dcc1c 100644
--- a/source/blender/editors/util/ed_util.c
+++ b/source/blender/editors/util/ed_util.c
@@ -35,6 +35,7 @@
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
+#include "BLI_fileops.h"
#include "BLI_listbase.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
@@ -44,6 +45,7 @@
#include "BKE_context.h"
#include "BKE_global.h"
+#include "BKE_icons.h"
#include "BKE_layer.h"
#include "BKE_main.h"
#include "BKE_material.h"
@@ -51,6 +53,7 @@
#include "BKE_object.h"
#include "BKE_packedFile.h"
#include "BKE_paint.h"
+#include "BKE_report.h"
#include "BKE_screen.h"
#include "BKE_undo_system.h"
#include "BKE_workspace.h"
@@ -64,6 +67,7 @@
#include "ED_object.h"
#include "ED_outliner.h"
#include "ED_paint.h"
+#include "ED_render.h"
#include "ED_space_api.h"
#include "ED_util.h"
@@ -501,3 +505,104 @@ void ED_OT_flush_edits(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_INTERNAL;
}
+
+static bool lib_id_preview_editing_poll(bContext *C)
+{
+ const PointerRNA idptr = CTX_data_pointer_get(C, "id");
+ BLI_assert(!idptr.data || RNA_struct_is_ID(idptr.type));
+
+ const ID *id = idptr.data;
+ if (!id) {
+ return false;
+ }
+ if (ID_IS_LINKED(id)) {
+ CTX_wm_operator_poll_msg_set(C, TIP_("Can't edit external library data"));
+ return false;
+ }
+ if (ID_IS_OVERRIDE_LIBRARY(id)) {
+ CTX_wm_operator_poll_msg_set(C, TIP_("Can't edit previews of overridden library data"));
+ return false;
+ }
+ if (!BKE_previewimg_id_get_p(id)) {
+ CTX_wm_operator_poll_msg_set(C, TIP_("Data-block does not support previews"));
+ return false;
+ }
+
+ return true;
+}
+
+static int lib_id_load_custom_preview_exec(bContext *C, wmOperator *op)
+{
+ char path[FILE_MAX];
+
+ RNA_string_get(op->ptr, "filepath", path);
+
+ if (!BLI_is_file(path)) {
+ BKE_reportf(op->reports, RPT_ERROR, "File not found '%s'", path);
+ return OPERATOR_CANCELLED;
+ }
+
+ PointerRNA idptr = CTX_data_pointer_get(C, "id");
+ ID *id = idptr.data;
+
+ BKE_previewimg_id_custom_set(id, path);
+
+ WM_event_add_notifier(C, NC_ASSET, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void ED_OT_lib_id_load_custom_preview(wmOperatorType *ot)
+{
+ ot->name = "Load Custom Preview";
+ ot->description = "Choose an image to help identify the data-block visually";
+ ot->idname = "ED_OT_lib_id_load_custom_preview";
+
+ /* api callbacks */
+ ot->poll = lib_id_preview_editing_poll;
+ ot->exec = lib_id_load_custom_preview_exec;
+ ot->invoke = WM_operator_filesel;
+
+ /* flags */
+ ot->flag = OPTYPE_INTERNAL;
+
+ WM_operator_properties_filesel(ot,
+ FILE_TYPE_FOLDER | FILE_TYPE_IMAGE,
+ FILE_SPECIAL,
+ FILE_OPENFILE,
+ WM_FILESEL_FILEPATH,
+ FILE_DEFAULTDISPLAY,
+ FILE_SORT_DEFAULT);
+}
+
+static int lib_id_generate_preview_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ PointerRNA idptr = CTX_data_pointer_get(C, "id");
+ ID *id = idptr.data;
+
+ ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
+
+ PreviewImage *preview = BKE_previewimg_id_get(id);
+ if (preview) {
+ BKE_previewimg_clear(preview);
+ }
+ UI_icon_render_id(C, NULL, id, true, true);
+
+ WM_event_add_notifier(C, NC_ASSET, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void ED_OT_lib_id_generate_preview(wmOperatorType *ot)
+{
+ ot->name = "Generate Preview";
+ ot->description = "Create an automatic preview for the selected data-block";
+ ot->idname = "ED_OT_lib_id_generate_preview";
+
+ /* api callbacks */
+ ot->poll = lib_id_preview_editing_poll;
+ ot->exec = lib_id_generate_preview_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_INTERNAL;
+}
diff --git a/source/blender/editors/uvedit/CMakeLists.txt b/source/blender/editors/uvedit/CMakeLists.txt
index 8e4a3df920e..1c8a56e0608 100644
--- a/source/blender/editors/uvedit/CMakeLists.txt
+++ b/source/blender/editors/uvedit/CMakeLists.txt
@@ -35,11 +35,11 @@ set(INC
set(SRC
uvedit_buttons.c
uvedit_draw.c
+ uvedit_islands.c
uvedit_ops.c
uvedit_parametrizer.c
uvedit_path.c
uvedit_rip.c
- uvedit_islands.c
uvedit_select.c
uvedit_smart_stitch.c
uvedit_unwrap_ops.c
diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h
index 306f8a2c561..28567234fab 100644
--- a/source/blender/editors/uvedit/uvedit_intern.h
+++ b/source/blender/editors/uvedit/uvedit_intern.h
@@ -25,7 +25,6 @@
struct BMFace;
struct BMLoop;
-struct Image;
struct Object;
struct Scene;
struct SpaceImage;
diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c
index 5b3d858329a..aac5b96f737 100644
--- a/source/blender/editors/uvedit/uvedit_ops.c
+++ b/source/blender/editors/uvedit/uvedit_ops.c
@@ -1812,7 +1812,7 @@ static void UV_OT_cursor_set(wmOperatorType *ot)
-FLT_MAX,
FLT_MAX,
"Location",
- "Cursor location in normalized (0.0-1.0) coordinates",
+ "Cursor location in normalized (0.0 to 1.0) coordinates",
-10.0f,
10.0f);
}
diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c
index 6dac31794b4..c1d222c9368 100644
--- a/source/blender/editors/uvedit/uvedit_smart_stitch.c
+++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c
@@ -2808,7 +2808,7 @@ void UV_OT_stitch(wmOperatorType *ot)
RNA_def_boolean(ot->srna,
"midpoint_snap",
0,
- "Snap At Midpoint",
+ "Snap at Midpoint",
"UVs are stitched at midpoint instead of at static island");
RNA_def_boolean(ot->srna, "clear_seams", 1, "Clear Seams", "Clear seams of stitched edges");
RNA_def_enum(ot->srna,