2023-08-16 00:20:26 +10:00
|
|
|
/* SPDX-FileCopyrightText: 2009 Blender Authors
|
2023-05-31 16:19:06 +02:00
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
2009-12-17 10:47:55 +00:00
|
|
|
|
2019-02-18 08:08:12 +11:00
|
|
|
/** \file
|
|
|
|
|
* \ingroup edinterface
|
2011-02-27 20:29:51 +00:00
|
|
|
*/
|
|
|
|
|
|
2022-07-29 23:16:58 -05:00
|
|
|
#include <cstring>
|
2009-12-17 10:47:55 +00:00
|
|
|
|
2024-01-31 17:08:09 +01:00
|
|
|
#include <fmt/format.h>
|
|
|
|
|
|
2009-12-17 10:47:55 +00:00
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
2018-11-20 17:34:56 +11:00
|
|
|
#include "DNA_armature_types.h"
|
2022-03-03 15:28:48 -08:00
|
|
|
#include "DNA_material_types.h"
|
2021-12-16 13:03:39 +01:00
|
|
|
#include "DNA_modifier_types.h" /* for handling geometry nodes properties */
|
|
|
|
|
#include "DNA_object_types.h" /* for OB_DATA_SUPPORT_ID */
|
2010-03-30 04:27:13 +00:00
|
|
|
#include "DNA_screen_types.h"
|
2009-12-17 10:47:55 +00:00
|
|
|
|
|
|
|
|
#include "BLI_blenlib.h"
|
2014-07-21 12:02:05 +02:00
|
|
|
#include "BLI_math_color.h"
|
2009-12-17 10:47:55 +00:00
|
|
|
|
2024-01-31 14:04:56 -05:00
|
|
|
#include "BLF_api.hh"
|
2024-02-09 18:59:42 +01:00
|
|
|
#include "BLT_lang.hh"
|
|
|
|
|
#include "BLT_translation.hh"
|
UI translation from inside Blender UI: first part.
This commit reshapes a bit runtime button info getter, by adding a new uiButGetStrInfo() which accepts a variable number of uiStringInfo parameters, and tries to fill them with the requested strings, for the given button (label, tip, context, RNA identifier, keymap, etc.). Currently used mostly by existing ui_tooltip_create(), and new UI_OT_edittranslation_init operator.
It also adds a few getters (to get RNA i18n context, and current language iso code).
Finally, it adds to C operators needed for the py ui_translation addon:
*UI_OT_edittranslation_init, which gathers requested data and launch the py operator.
*UI_OT_reloadtranslation, which forces a full reload of the whole UI translation (including rechecking the directory containing mo files).
For the first operator to work, it also adds a new user preferences path: i18n_branches_directory, to point to the /branch part of a bf-translation checkout.
2012-07-09 14:25:35 +00:00
|
|
|
|
2024-04-29 18:34:57 +02:00
|
|
|
#include "BKE_anim_data.hh"
|
2023-11-16 11:41:55 +01:00
|
|
|
#include "BKE_context.hh"
|
2024-04-29 18:34:57 +02:00
|
|
|
#include "BKE_fcurve.hh"
|
2024-01-20 19:17:36 +01:00
|
|
|
#include "BKE_idtype.hh"
|
2024-01-23 15:18:09 -05:00
|
|
|
#include "BKE_layer.hh"
|
2024-01-15 12:44:04 -05:00
|
|
|
#include "BKE_lib_id.hh"
|
2023-08-02 15:00:40 +02:00
|
|
|
#include "BKE_lib_override.hh"
|
2023-11-30 19:51:22 +01:00
|
|
|
#include "BKE_lib_remap.hh"
|
2022-03-03 15:28:48 -08:00
|
|
|
#include "BKE_material.h"
|
2023-05-15 15:14:22 +02:00
|
|
|
#include "BKE_node.hh"
|
2024-02-10 18:34:29 +01:00
|
|
|
#include "BKE_report.hh"
|
2023-09-25 17:48:21 -04:00
|
|
|
#include "BKE_screen.hh"
|
2009-12-17 10:47:55 +00:00
|
|
|
|
2024-01-18 22:50:23 +02:00
|
|
|
#include "IMB_colormanagement.hh"
|
2018-12-13 18:28:41 +01:00
|
|
|
|
2023-09-22 03:18:17 +02:00
|
|
|
#include "DEG_depsgraph.hh"
|
2024-04-29 18:34:57 +02:00
|
|
|
#include "DEG_depsgraph_build.hh"
|
2017-06-08 10:14:53 +02:00
|
|
|
|
2023-08-10 22:40:27 +02:00
|
|
|
#include "RNA_access.hh"
|
|
|
|
|
#include "RNA_define.hh"
|
|
|
|
|
#include "RNA_path.hh"
|
2022-03-14 16:54:46 +01:00
|
|
|
#include "RNA_prototypes.h"
|
2009-12-17 10:47:55 +00:00
|
|
|
|
2024-03-08 09:16:00 -05:00
|
|
|
#include "UI_abstract_view.hh"
|
2023-03-22 18:45:35 +01:00
|
|
|
#include "UI_interface.hh"
|
2010-03-30 04:27:13 +00:00
|
|
|
|
2022-11-26 00:21:17 -06:00
|
|
|
#include "interface_intern.hh"
|
2010-03-30 04:27:13 +00:00
|
|
|
|
2023-08-04 23:11:22 +02:00
|
|
|
#include "WM_api.hh"
|
|
|
|
|
#include "WM_types.hh"
|
2009-12-17 10:47:55 +00:00
|
|
|
|
2023-08-05 02:57:52 +02:00
|
|
|
#include "ED_object.hh"
|
|
|
|
|
#include "ED_paint.hh"
|
2023-10-19 16:02:34 +02:00
|
|
|
#include "ED_undo.hh"
|
2014-07-21 12:02:05 +02:00
|
|
|
|
2019-07-31 18:42:03 +03:00
|
|
|
/* for Copy As Driver */
|
2023-08-05 02:57:52 +02:00
|
|
|
#include "ED_keyframing.hh"
|
2019-07-31 18:42:03 +03:00
|
|
|
|
2011-10-23 04:13:56 +00:00
|
|
|
/* only for UI_OT_editsource */
|
|
|
|
|
#include "BLI_ghash.h"
|
2023-08-04 23:11:22 +02:00
|
|
|
#include "ED_screen.hh"
|
2009-12-17 10:47:55 +00:00
|
|
|
|
2023-03-22 18:45:35 +01:00
|
|
|
using namespace blender::ui;
|
|
|
|
|
|
2021-07-13 15:44:44 +02:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Immediate redraw helper
|
|
|
|
|
*
|
|
|
|
|
* Generally handlers shouldn't do any redrawing, that includes the layout/button definitions. That
|
|
|
|
|
* violates the Model-View-Controller pattern.
|
|
|
|
|
*
|
|
|
|
|
* But there are some operators which really need to re-run the layout definitions for various
|
|
|
|
|
* reasons. For example, "Edit Source" does it to find out which exact Python code added a button.
|
|
|
|
|
* Other operators may need to access buttons that aren't currently visible. In Blender's UI code
|
|
|
|
|
* design that typically means just not adding the button in the first place, for a particular
|
|
|
|
|
* redraw. So the operator needs to change context and re-create the layout, so the button becomes
|
|
|
|
|
* available to act on.
|
|
|
|
|
*
|
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
|
|
static void ui_region_redraw_immediately(bContext *C, ARegion *region)
|
|
|
|
|
{
|
|
|
|
|
ED_region_do_layout(C, region);
|
|
|
|
|
WM_draw_region_viewport_bind(region);
|
|
|
|
|
ED_region_do_draw(C, region);
|
|
|
|
|
WM_draw_region_viewport_unbind(region);
|
2024-05-09 12:10:59 +10:00
|
|
|
region->do_draw = 0;
|
2021-07-13 15:44:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
2019-03-23 12:07:05 +11:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Copy Data Path Operator
|
|
|
|
|
* \{ */
|
2009-12-17 10:47:55 +00:00
|
|
|
|
2018-07-02 11:47:00 +02:00
|
|
|
static bool copy_data_path_button_poll(bContext *C)
|
2012-05-09 15:54:25 +00:00
|
|
|
{
|
|
|
|
|
PointerRNA ptr;
|
|
|
|
|
PropertyRNA *prop;
|
|
|
|
|
int index;
|
|
|
|
|
|
2014-11-09 21:20:40 +01:00
|
|
|
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
|
2012-05-09 15:54:25 +00:00
|
|
|
|
2019-08-23 09:52:12 +02:00
|
|
|
if (ptr.owner_id && ptr.data && prop) {
|
2024-01-31 17:08:09 +01:00
|
|
|
if (const std::optional<std::string> path = RNA_path_from_ID_to_property(&ptr, prop)) {
|
2024-04-04 10:55:18 +11:00
|
|
|
UNUSED_VARS(path);
|
2020-09-02 19:10:18 +02:00
|
|
|
return true;
|
2012-05-09 15:54:25 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-02 19:10:18 +02:00
|
|
|
return false;
|
2012-05-09 15:54:25 +00:00
|
|
|
}
|
|
|
|
|
|
2016-10-20 00:27:14 +02:00
|
|
|
static int copy_data_path_button_exec(bContext *C, wmOperator *op)
|
2009-12-17 10:47:55 +00:00
|
|
|
{
|
2019-08-22 20:43:19 +03:00
|
|
|
Main *bmain = CTX_data_main(C);
|
2009-12-17 10:47:55 +00:00
|
|
|
PointerRNA ptr;
|
2009-12-17 17:15:38 +00:00
|
|
|
PropertyRNA *prop;
|
2009-12-17 10:47:55 +00:00
|
|
|
int index;
|
2019-08-22 20:43:19 +03:00
|
|
|
ID *id;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-10-20 00:27:14 +02:00
|
|
|
const bool full_path = RNA_boolean_get(op->ptr, "full_path");
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-12-17 10:47:55 +00:00
|
|
|
/* try to create driver using property retrieved from UI */
|
2014-11-09 21:20:40 +01:00
|
|
|
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-01-31 17:08:09 +01:00
|
|
|
std::optional<std::string> path;
|
2022-07-29 23:16:58 -05:00
|
|
|
if (ptr.owner_id != nullptr) {
|
2016-10-20 00:27:14 +02:00
|
|
|
if (full_path) {
|
|
|
|
|
if (prop) {
|
2022-09-07 11:07:44 +10:00
|
|
|
path = RNA_path_full_property_py_ex(&ptr, prop, index, true);
|
2016-10-20 00:27:14 +02:00
|
|
|
}
|
|
|
|
|
else {
|
2022-09-07 11:07:44 +10:00
|
|
|
path = RNA_path_full_struct_py(&ptr);
|
2016-10-20 00:27:14 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
2023-09-29 13:50:52 +10:00
|
|
|
const int index_dim = (index != -1 && RNA_property_array_check(prop)) ? 1 : 0;
|
|
|
|
|
path = RNA_path_from_real_ID_to_property_index(bmain, &ptr, prop, index_dim, index, &id);
|
2019-08-22 20:43:19 +03:00
|
|
|
|
|
|
|
|
if (!path) {
|
2023-09-29 13:50:52 +10:00
|
|
|
path = RNA_path_from_ID_to_property_index(&ptr, prop, index_dim, index);
|
2019-08-22 20:43:19 +03:00
|
|
|
}
|
2016-10-20 00:27:14 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-12-17 10:47:55 +00:00
|
|
|
if (path) {
|
2024-01-31 17:08:09 +01:00
|
|
|
WM_clipboard_text_set(path->c_str(), false);
|
2012-05-09 15:54:25 +00:00
|
|
|
return OPERATOR_FINISHED;
|
2009-12-17 10:47:55 +00:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-05-09 15:54:25 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
2009-12-17 10:47:55 +00:00
|
|
|
}
|
|
|
|
|
|
2011-02-13 14:16:36 +00:00
|
|
|
static void UI_OT_copy_data_path_button(wmOperatorType *ot)
|
2009-12-17 10:47:55 +00:00
|
|
|
{
|
2016-10-20 00:27:14 +02:00
|
|
|
PropertyRNA *prop;
|
|
|
|
|
|
2009-12-17 10:47:55 +00:00
|
|
|
/* identifiers */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->name = "Copy Data Path";
|
|
|
|
|
ot->idname = "UI_OT_copy_data_path_button";
|
|
|
|
|
ot->description = "Copy the RNA data path for this property to the clipboard";
|
2009-12-17 10:47:55 +00:00
|
|
|
|
|
|
|
|
/* callbacks */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->exec = copy_data_path_button_exec;
|
2012-05-09 15:54:25 +00:00
|
|
|
ot->poll = copy_data_path_button_poll;
|
2009-12-17 10:47:55 +00:00
|
|
|
|
|
|
|
|
/* flags */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER;
|
2016-10-20 00:27:14 +02:00
|
|
|
|
|
|
|
|
/* properties */
|
|
|
|
|
prop = RNA_def_boolean(ot->srna, "full_path", false, "full_path", "Copy full data path");
|
|
|
|
|
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
2009-12-17 10:47:55 +00:00
|
|
|
}
|
|
|
|
|
|
2019-07-31 18:42:03 +03:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Copy As Driver Operator
|
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
|
|
static bool copy_as_driver_button_poll(bContext *C)
|
2016-02-26 11:57:35 +11:00
|
|
|
{
|
2019-07-31 18:42:03 +03:00
|
|
|
PointerRNA ptr;
|
|
|
|
|
PropertyRNA *prop;
|
|
|
|
|
int index;
|
2016-02-26 11:57:35 +11:00
|
|
|
|
2019-07-31 18:42:03 +03:00
|
|
|
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
|
|
|
|
|
|
2019-08-23 09:52:12 +02:00
|
|
|
if (ptr.owner_id && ptr.data && prop &&
|
2019-08-14 19:17:52 +03:00
|
|
|
ELEM(RNA_property_type(prop), PROP_BOOLEAN, PROP_INT, PROP_FLOAT, PROP_ENUM) &&
|
|
|
|
|
(index >= 0 || !RNA_property_array_check(prop)))
|
|
|
|
|
{
|
2024-01-31 17:08:09 +01:00
|
|
|
if (const std::optional<std::string> path = RNA_path_from_ID_to_property(&ptr, prop)) {
|
2024-04-04 10:55:18 +11:00
|
|
|
UNUSED_VARS(path);
|
2020-09-02 19:10:18 +02:00
|
|
|
return true;
|
2019-07-31 18:42:03 +03:00
|
|
|
}
|
2016-02-26 11:57:35 +11:00
|
|
|
}
|
|
|
|
|
|
2020-09-02 19:10:18 +02:00
|
|
|
return false;
|
2016-02-26 11:57:35 +11:00
|
|
|
}
|
|
|
|
|
|
2019-08-22 20:43:19 +03:00
|
|
|
static int copy_as_driver_button_exec(bContext *C, wmOperator *op)
|
2019-07-31 18:42:03 +03:00
|
|
|
{
|
2019-08-22 20:43:19 +03:00
|
|
|
Main *bmain = CTX_data_main(C);
|
2019-07-31 18:42:03 +03:00
|
|
|
PointerRNA ptr;
|
|
|
|
|
PropertyRNA *prop;
|
|
|
|
|
int index;
|
|
|
|
|
|
|
|
|
|
/* try to create driver using property retrieved from UI */
|
|
|
|
|
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
|
|
|
|
|
|
2019-08-23 09:52:12 +02:00
|
|
|
if (ptr.owner_id && ptr.data && prop) {
|
2019-08-22 20:43:19 +03:00
|
|
|
ID *id;
|
2022-07-29 23:16:58 -05:00
|
|
|
const int dim = RNA_property_array_dimension(&ptr, prop, nullptr);
|
2024-01-31 17:08:09 +01:00
|
|
|
if (const std::optional<std::string> path = RNA_path_from_real_ID_to_property_index(
|
|
|
|
|
bmain, &ptr, prop, dim, index, &id))
|
|
|
|
|
{
|
|
|
|
|
ANIM_copy_as_driver(id, path->c_str(), RNA_property_identifier(prop));
|
2019-07-31 18:42:03 +03:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
2020-07-03 14:20:10 +02:00
|
|
|
|
|
|
|
|
BKE_reportf(op->reports, RPT_ERROR, "Could not compute a valid data path");
|
|
|
|
|
return OPERATOR_CANCELLED;
|
2019-07-31 18:42:03 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void UI_OT_copy_as_driver_button(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
2019-09-20 14:31:24 +02:00
|
|
|
ot->name = "Copy as New Driver";
|
2019-07-31 18:42:03 +03:00
|
|
|
ot->idname = "UI_OT_copy_as_driver_button";
|
|
|
|
|
ot->description =
|
|
|
|
|
"Create a new driver with this property as input, and copy it to the "
|
2023-04-14 12:12:30 +02:00
|
|
|
"internal clipboard. Use Paste Driver to add it to the target property, "
|
|
|
|
|
"or Paste Driver Variables to extend an existing driver";
|
2019-07-31 18:42:03 +03:00
|
|
|
|
|
|
|
|
/* callbacks */
|
|
|
|
|
ot->exec = copy_as_driver_button_exec;
|
|
|
|
|
ot->poll = copy_as_driver_button_poll;
|
|
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-23 12:07:05 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Copy Python Command Operator
|
|
|
|
|
* \{ */
|
|
|
|
|
|
2019-07-31 18:42:03 +03:00
|
|
|
static bool copy_python_command_button_poll(bContext *C)
|
|
|
|
|
{
|
|
|
|
|
uiBut *but = UI_context_active_but_get(C);
|
|
|
|
|
|
2022-07-29 23:16:58 -05:00
|
|
|
if (but && (but->optype != nullptr)) {
|
|
|
|
|
return true;
|
2019-07-31 18:42:03 +03:00
|
|
|
}
|
|
|
|
|
|
2022-07-29 23:16:58 -05:00
|
|
|
return false;
|
2019-07-31 18:42:03 +03:00
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int copy_python_command_button_exec(bContext *C, wmOperator * /*op*/)
|
2016-02-26 11:57:35 +11:00
|
|
|
{
|
|
|
|
|
uiBut *but = UI_context_active_but_get(C);
|
|
|
|
|
|
2022-07-29 23:16:58 -05:00
|
|
|
if (but && (but->optype != nullptr)) {
|
2024-01-29 16:33:06 -05:00
|
|
|
/* allocated when needed, the button owns it */
|
|
|
|
|
PointerRNA *opptr = UI_but_operator_ptr_ensure(but);
|
2016-02-26 11:57:35 +11:00
|
|
|
|
2024-01-29 16:33:06 -05:00
|
|
|
std::string str = WM_operator_pystring_ex(C, nullptr, false, true, but->optype, opptr);
|
2016-02-26 11:57:35 +11:00
|
|
|
|
2024-01-29 16:33:06 -05:00
|
|
|
WM_clipboard_text_set(str.c_str(), false);
|
2016-02-26 11:57:35 +11:00
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void UI_OT_copy_python_command_button(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Copy Python Command";
|
|
|
|
|
ot->idname = "UI_OT_copy_python_command_button";
|
|
|
|
|
ot->description = "Copy the Python command matching this button";
|
|
|
|
|
|
|
|
|
|
/* callbacks */
|
|
|
|
|
ot->exec = copy_python_command_button_exec;
|
|
|
|
|
ot->poll = copy_python_command_button_poll;
|
|
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-23 12:07:05 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Reset to Default Values Button Operator
|
|
|
|
|
* \{ */
|
2009-12-17 10:47:55 +00:00
|
|
|
|
2013-09-20 09:10:17 +00:00
|
|
|
static int operator_button_property_finish(bContext *C, PointerRNA *ptr, PropertyRNA *prop)
|
|
|
|
|
{
|
2019-08-23 09:52:12 +02:00
|
|
|
ID *id = ptr->owner_id;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-09-20 09:10:17 +00:00
|
|
|
/* perform updates required for this property */
|
|
|
|
|
RNA_property_update(C, ptr, prop);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-09-20 09:10:17 +00:00
|
|
|
/* as if we pressed the button */
|
2022-02-22 16:36:59 +11:00
|
|
|
UI_context_active_but_prop_handle(C, false);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-09-20 09:10:17 +00:00
|
|
|
/* Since we don't want to undo _all_ edits to settings, eg window
|
|
|
|
|
* edits on the screen or on operator settings.
|
|
|
|
|
* it might be better to move undo's inline - campbell */
|
|
|
|
|
if (id && ID_CHECK_UNDO(id)) {
|
|
|
|
|
/* do nothing, go ahead with undo */
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
2020-07-03 14:20:10 +02:00
|
|
|
return OPERATOR_CANCELLED;
|
2013-09-20 09:10:17 +00:00
|
|
|
}
|
|
|
|
|
|
2022-02-22 16:36:59 +11:00
|
|
|
static int operator_button_property_finish_with_undo(bContext *C,
|
|
|
|
|
PointerRNA *ptr,
|
|
|
|
|
PropertyRNA *prop)
|
|
|
|
|
{
|
|
|
|
|
/* Perform updates required for this property. */
|
|
|
|
|
RNA_property_update(C, ptr, prop);
|
|
|
|
|
|
|
|
|
|
/* As if we pressed the button. */
|
|
|
|
|
UI_context_active_but_prop_handle(C, true);
|
|
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-02 11:47:00 +02:00
|
|
|
static bool reset_default_button_poll(bContext *C)
|
2009-12-17 17:15:38 +00:00
|
|
|
{
|
|
|
|
|
PointerRNA ptr;
|
|
|
|
|
PropertyRNA *prop;
|
|
|
|
|
int index;
|
|
|
|
|
|
2014-11-09 21:20:40 +01:00
|
|
|
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
|
2018-05-23 10:47:12 +02:00
|
|
|
|
2009-12-17 17:15:38 +00:00
|
|
|
return (ptr.data && prop && RNA_property_editable(&ptr, prop));
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-17 10:47:55 +00:00
|
|
|
static int reset_default_button_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
|
|
|
|
PointerRNA ptr;
|
2009-12-17 17:15:38 +00:00
|
|
|
PropertyRNA *prop;
|
2014-02-03 18:55:59 +11:00
|
|
|
int index;
|
|
|
|
|
const bool all = RNA_boolean_get(op->ptr, "all");
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-12-17 10:47:55 +00:00
|
|
|
/* try to reset the nominated setting to its default value */
|
2014-11-09 21:20:40 +01:00
|
|
|
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-12-17 10:47:55 +00:00
|
|
|
/* if there is a valid property that is editable... */
|
|
|
|
|
if (ptr.data && prop && RNA_property_editable(&ptr, prop)) {
|
2019-03-25 10:15:20 +11:00
|
|
|
if (RNA_property_reset(&ptr, prop, (all) ? -1 : index)) {
|
2022-02-22 16:36:59 +11:00
|
|
|
return operator_button_property_finish_with_undo(C, &ptr, prop);
|
2019-03-25 10:15:20 +11:00
|
|
|
}
|
2011-09-28 18:45:17 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-09-20 09:10:17 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
2009-12-17 10:47:55 +00:00
|
|
|
}
|
|
|
|
|
|
2011-02-13 14:16:36 +00:00
|
|
|
static void UI_OT_reset_default_button(wmOperatorType *ot)
|
2009-12-17 10:47:55 +00:00
|
|
|
{
|
|
|
|
|
/* identifiers */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->name = "Reset to Default Value";
|
|
|
|
|
ot->idname = "UI_OT_reset_default_button";
|
|
|
|
|
ot->description = "Reset this property's value to its default value";
|
2009-12-17 10:47:55 +00:00
|
|
|
|
|
|
|
|
/* callbacks */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->poll = reset_default_button_poll;
|
|
|
|
|
ot->exec = reset_default_button_exec;
|
2009-12-17 10:47:55 +00:00
|
|
|
|
|
|
|
|
/* flags */
|
2022-02-22 16:36:59 +11:00
|
|
|
/* Don't set #OPTYPE_UNDO because #operator_button_property_finish_with_undo
|
|
|
|
|
* is responsible for the undo push. */
|
|
|
|
|
ot->flag = 0;
|
2018-05-23 10:47:12 +02:00
|
|
|
|
2009-12-17 17:15:38 +00:00
|
|
|
/* properties */
|
2022-07-29 23:16:58 -05:00
|
|
|
RNA_def_boolean(
|
|
|
|
|
ot->srna, "all", true, "All", "Reset to default values all elements of the array");
|
2009-12-17 17:15:38 +00:00
|
|
|
}
|
|
|
|
|
|
2019-03-23 12:07:05 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Assign Value as Default Button Operator
|
|
|
|
|
* \{ */
|
2018-12-15 22:37:12 +03:00
|
|
|
|
|
|
|
|
static bool assign_default_button_poll(bContext *C)
|
|
|
|
|
{
|
|
|
|
|
PointerRNA ptr;
|
|
|
|
|
PropertyRNA *prop;
|
|
|
|
|
int index;
|
|
|
|
|
|
|
|
|
|
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
|
|
|
|
|
|
|
|
|
|
if (ptr.data && prop && RNA_property_editable(&ptr, prop)) {
|
2020-08-26 10:11:13 +10:00
|
|
|
const PropertyType type = RNA_property_type(prop);
|
2018-12-15 22:37:12 +03:00
|
|
|
|
|
|
|
|
return RNA_property_is_idprop(prop) && !RNA_property_array_check(prop) &&
|
|
|
|
|
ELEM(type, PROP_INT, PROP_FLOAT);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int assign_default_button_exec(bContext *C, wmOperator * /*op*/)
|
2018-12-15 22:37:12 +03:00
|
|
|
{
|
|
|
|
|
PointerRNA ptr;
|
|
|
|
|
PropertyRNA *prop;
|
|
|
|
|
int index;
|
|
|
|
|
|
|
|
|
|
/* try to reset the nominated setting to its default value */
|
|
|
|
|
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
|
|
|
|
|
|
|
|
|
|
/* if there is a valid property that is editable... */
|
|
|
|
|
if (ptr.data && prop && RNA_property_editable(&ptr, prop)) {
|
2019-03-25 10:15:20 +11:00
|
|
|
if (RNA_property_assign_default(&ptr, prop)) {
|
2018-12-15 22:37:12 +03:00
|
|
|
return operator_button_property_finish(C, &ptr, prop);
|
2019-03-25 10:15:20 +11:00
|
|
|
}
|
2018-12-15 22:37:12 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void UI_OT_assign_default_button(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Assign Value as Default";
|
|
|
|
|
ot->idname = "UI_OT_assign_default_button";
|
|
|
|
|
ot->description = "Set this property's current value as the new default";
|
|
|
|
|
|
|
|
|
|
/* callbacks */
|
|
|
|
|
ot->poll = assign_default_button_poll;
|
|
|
|
|
ot->exec = assign_default_button_exec;
|
|
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_UNDO;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-23 12:07:05 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Unset Property Button Operator
|
|
|
|
|
* \{ */
|
2013-09-20 09:10:17 +00:00
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int unset_property_button_exec(bContext *C, wmOperator * /*op*/)
|
2013-09-20 09:10:17 +00:00
|
|
|
{
|
|
|
|
|
PointerRNA ptr;
|
|
|
|
|
PropertyRNA *prop;
|
|
|
|
|
int index;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-09-20 09:10:17 +00:00
|
|
|
/* try to unset the nominated property */
|
2014-11-09 21:20:40 +01:00
|
|
|
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-09-20 09:10:17 +00:00
|
|
|
/* if there is a valid property that is editable... */
|
2014-02-28 11:04:15 +11:00
|
|
|
if (ptr.data && prop && RNA_property_editable(&ptr, prop) &&
|
|
|
|
|
/* RNA_property_is_idprop(prop) && */
|
|
|
|
|
RNA_property_is_set(&ptr, prop))
|
|
|
|
|
{
|
2013-09-20 09:10:17 +00:00
|
|
|
RNA_property_unset(&ptr, prop);
|
|
|
|
|
return operator_button_property_finish(C, &ptr, prop);
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-09-20 09:10:17 +00:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void UI_OT_unset_property_button(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
2019-09-20 14:31:24 +02:00
|
|
|
ot->name = "Unset Property";
|
2013-09-20 09:10:17 +00:00
|
|
|
ot->idname = "UI_OT_unset_property_button";
|
|
|
|
|
ot->description = "Clear the property and use default or generated value in operators";
|
|
|
|
|
|
|
|
|
|
/* callbacks */
|
|
|
|
|
ot->poll = ED_operator_regionactive;
|
|
|
|
|
ot->exec = unset_property_button_exec;
|
|
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_UNDO;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-23 12:07:05 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Define Override Type Operator
|
|
|
|
|
* \{ */
|
2017-11-29 17:03:52 +01:00
|
|
|
|
|
|
|
|
/* Note that we use different values for UI/UX than 'real' override operations, user does not care
|
|
|
|
|
* whether it's added or removed for the differential operation e.g. */
|
|
|
|
|
enum {
|
|
|
|
|
UIOverride_Type_NOOP = 0,
|
|
|
|
|
UIOverride_Type_Replace = 1,
|
|
|
|
|
UIOverride_Type_Difference = 2, /* Add/subtract */
|
|
|
|
|
UIOverride_Type_Factor = 3, /* Multiply */
|
|
|
|
|
/* TODO: should/can we expose insert/remove ones for collections? Doubt it... */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static EnumPropertyItem override_type_items[] = {
|
|
|
|
|
{UIOverride_Type_NOOP,
|
|
|
|
|
"NOOP",
|
|
|
|
|
0,
|
|
|
|
|
"NoOp",
|
2018-07-01 20:15:21 +02:00
|
|
|
"'No-Operation', place holder preventing automatic override to ever affect the property"},
|
2017-11-29 17:03:52 +01:00
|
|
|
{UIOverride_Type_Replace,
|
|
|
|
|
"REPLACE",
|
|
|
|
|
0,
|
|
|
|
|
"Replace",
|
|
|
|
|
"Completely replace value from linked data by local one"},
|
|
|
|
|
{UIOverride_Type_Difference,
|
|
|
|
|
"DIFFERENCE",
|
|
|
|
|
0,
|
|
|
|
|
"Difference",
|
|
|
|
|
"Store difference to linked data value"},
|
|
|
|
|
{UIOverride_Type_Factor,
|
|
|
|
|
"FACTOR",
|
|
|
|
|
0,
|
|
|
|
|
"Factor",
|
|
|
|
|
"Store factor to linked data value (useful e.g. for scale)"},
|
2022-07-29 23:16:58 -05:00
|
|
|
{0, nullptr, 0, nullptr, nullptr},
|
2017-11-29 17:03:52 +01:00
|
|
|
};
|
|
|
|
|
|
2018-07-02 12:03:56 +02:00
|
|
|
static bool override_type_set_button_poll(bContext *C)
|
2017-11-29 17:03:52 +01:00
|
|
|
{
|
|
|
|
|
PointerRNA ptr;
|
|
|
|
|
PropertyRNA *prop;
|
|
|
|
|
int index;
|
|
|
|
|
|
|
|
|
|
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
|
|
|
|
|
|
2020-07-16 11:03:11 +02:00
|
|
|
const uint override_status = RNA_property_override_library_status(
|
|
|
|
|
CTX_data_main(C), &ptr, prop, index);
|
2017-11-29 17:03:52 +01:00
|
|
|
|
2018-03-14 11:47:35 +01:00
|
|
|
return (ptr.data && prop && (override_status & RNA_OVERRIDE_STATUS_OVERRIDABLE));
|
2017-11-29 17:03:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int override_type_set_button_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
|
|
|
|
PointerRNA ptr;
|
|
|
|
|
PropertyRNA *prop;
|
|
|
|
|
int index;
|
|
|
|
|
bool created;
|
|
|
|
|
const bool all = RNA_boolean_get(op->ptr, "all");
|
|
|
|
|
const int op_type = RNA_enum_get(op->ptr, "type");
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-29 17:03:52 +01:00
|
|
|
short operation;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-12-04 17:19:34 +11:00
|
|
|
switch (op_type) {
|
2017-11-29 17:03:52 +01:00
|
|
|
case UIOverride_Type_NOOP:
|
2023-05-02 16:13:02 +02:00
|
|
|
operation = LIBOVERRIDE_OP_NOOP;
|
2017-11-29 17:03:52 +01:00
|
|
|
break;
|
|
|
|
|
case UIOverride_Type_Replace:
|
2023-05-02 16:13:02 +02:00
|
|
|
operation = LIBOVERRIDE_OP_REPLACE;
|
2017-11-29 17:03:52 +01:00
|
|
|
break;
|
|
|
|
|
case UIOverride_Type_Difference:
|
2019-01-15 23:24:20 +11:00
|
|
|
/* override code will automatically switch to subtract if needed. */
|
2023-05-02 16:13:02 +02:00
|
|
|
operation = LIBOVERRIDE_OP_ADD;
|
2017-11-29 17:03:52 +01:00
|
|
|
break;
|
|
|
|
|
case UIOverride_Type_Factor:
|
2023-05-02 16:13:02 +02:00
|
|
|
operation = LIBOVERRIDE_OP_MULTIPLY;
|
2017-11-29 17:03:52 +01:00
|
|
|
break;
|
|
|
|
|
default:
|
2023-05-02 16:13:02 +02:00
|
|
|
operation = LIBOVERRIDE_OP_REPLACE;
|
2017-11-29 17:03:52 +01:00
|
|
|
BLI_assert(0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-29 17:03:52 +01:00
|
|
|
/* try to reset the nominated setting to its default value */
|
|
|
|
|
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-07-29 23:16:58 -05:00
|
|
|
BLI_assert(ptr.owner_id != nullptr);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-29 17:03:52 +01:00
|
|
|
if (all) {
|
|
|
|
|
index = -1;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-06-14 23:16:04 +02:00
|
|
|
IDOverrideLibraryPropertyOperation *opop = RNA_property_override_property_operation_get(
|
2022-07-29 23:16:58 -05:00
|
|
|
CTX_data_main(C), &ptr, prop, operation, index, true, nullptr, &created);
|
2020-10-13 20:34:18 +02:00
|
|
|
|
2022-07-29 23:16:58 -05:00
|
|
|
if (opop == nullptr) {
|
2020-10-13 20:34:18 +02:00
|
|
|
/* Sometimes e.g. RNA cannot generate a path to the given property. */
|
|
|
|
|
BKE_reportf(op->reports, RPT_WARNING, "Failed to create the override operation");
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-29 17:03:52 +01:00
|
|
|
if (!created) {
|
|
|
|
|
opop->operation = operation;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-01-27 17:51:07 +01:00
|
|
|
/* Outliner e.g. has to be aware of this change. */
|
2022-07-29 23:16:58 -05:00
|
|
|
WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, nullptr);
|
2022-01-27 17:51:07 +01:00
|
|
|
|
2017-11-29 17:03:52 +01:00
|
|
|
return operator_button_property_finish(C, &ptr, prop);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int override_type_set_button_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
2017-11-29 17:03:52 +01:00
|
|
|
{
|
2018-04-23 15:14:51 +02:00
|
|
|
#if 0 /* Disabled for now */
|
2017-11-29 17:03:52 +01:00
|
|
|
return WM_menu_invoke_ex(C, op, WM_OP_INVOKE_DEFAULT);
|
2018-04-23 15:14:51 +02:00
|
|
|
#else
|
2023-05-02 16:13:02 +02:00
|
|
|
RNA_enum_set(op->ptr, "type", LIBOVERRIDE_OP_REPLACE);
|
2018-04-23 15:14:51 +02:00
|
|
|
return override_type_set_button_exec(C, op);
|
|
|
|
|
#endif
|
2017-11-29 17:03:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void UI_OT_override_type_set_button(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Define Override Type";
|
|
|
|
|
ot->idname = "UI_OT_override_type_set_button";
|
|
|
|
|
ot->description = "Create an override operation, or set the type of an existing one";
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-29 17:03:52 +01:00
|
|
|
/* callbacks */
|
|
|
|
|
ot->poll = override_type_set_button_poll;
|
|
|
|
|
ot->exec = override_type_set_button_exec;
|
|
|
|
|
ot->invoke = override_type_set_button_invoke;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-29 17:03:52 +01:00
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_UNDO;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-29 17:03:52 +01:00
|
|
|
/* properties */
|
2022-07-29 23:16:58 -05:00
|
|
|
RNA_def_boolean(
|
|
|
|
|
ot->srna, "all", true, "All", "Reset to default values all elements of the array");
|
2018-07-01 20:15:21 +02:00
|
|
|
ot->prop = RNA_def_enum(ot->srna,
|
|
|
|
|
"type",
|
|
|
|
|
override_type_items,
|
|
|
|
|
UIOverride_Type_Replace,
|
|
|
|
|
"Type",
|
|
|
|
|
"Type of override operation");
|
2018-04-23 15:14:51 +02:00
|
|
|
/* TODO: add itemf callback, not all options are available for all data types... */
|
2017-11-29 17:03:52 +01:00
|
|
|
}
|
|
|
|
|
|
2018-07-02 12:03:56 +02:00
|
|
|
static bool override_remove_button_poll(bContext *C)
|
2017-11-29 17:03:52 +01:00
|
|
|
{
|
|
|
|
|
PointerRNA ptr;
|
|
|
|
|
PropertyRNA *prop;
|
|
|
|
|
int index;
|
|
|
|
|
|
|
|
|
|
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
|
|
|
|
|
|
2020-07-16 11:03:11 +02:00
|
|
|
const uint override_status = RNA_property_override_library_status(
|
|
|
|
|
CTX_data_main(C), &ptr, prop, index);
|
2017-11-29 17:03:52 +01:00
|
|
|
|
2019-08-23 09:52:12 +02:00
|
|
|
return (ptr.data && ptr.owner_id && prop && (override_status & RNA_OVERRIDE_STATUS_OVERRIDDEN));
|
2017-11-29 17:03:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int override_remove_button_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2018-06-29 12:46:54 +02:00
|
|
|
Main *bmain = CTX_data_main(C);
|
2023-09-06 00:48:50 +02:00
|
|
|
PointerRNA ptr, src;
|
2017-11-29 17:03:52 +01:00
|
|
|
PropertyRNA *prop;
|
|
|
|
|
int index;
|
|
|
|
|
const bool all = RNA_boolean_get(op->ptr, "all");
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-29 17:03:52 +01:00
|
|
|
/* try to reset the nominated setting to its default value */
|
|
|
|
|
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-08-23 09:52:12 +02:00
|
|
|
ID *id = ptr.owner_id;
|
2020-07-16 11:03:11 +02:00
|
|
|
IDOverrideLibraryProperty *oprop = RNA_property_override_property_find(bmain, &ptr, prop, &id);
|
2022-07-29 23:16:58 -05:00
|
|
|
BLI_assert(oprop != nullptr);
|
|
|
|
|
BLI_assert(id != nullptr && id->override_library != nullptr);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-12-20 17:53:34 +01:00
|
|
|
/* The source (i.e. linked data) is required to restore values of deleted overrides. */
|
|
|
|
|
PropertyRNA *src_prop;
|
|
|
|
|
PointerRNA id_refptr = RNA_id_pointer_create(id->override_library->reference);
|
|
|
|
|
if (!RNA_path_resolve_property(&id_refptr, oprop->rna_path, &src, &src_prop)) {
|
|
|
|
|
BLI_assert_msg(0, "Failed to create matching source (linked data) RNA pointer");
|
2017-11-29 17:03:52 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-11-29 17:03:52 +01:00
|
|
|
if (!all && index != -1) {
|
|
|
|
|
bool is_strict_find;
|
2019-01-15 23:24:20 +11:00
|
|
|
/* Remove override operation for given item,
|
|
|
|
|
* add singular operations for the other items as needed. */
|
2020-02-10 18:05:19 +01:00
|
|
|
IDOverrideLibraryPropertyOperation *opop = BKE_lib_override_library_property_operation_find(
|
LibOverride: Add ID pointer to operations over ID pointers.
In RNA collections storing ID references, the name of the collection
item may not always be unique, when several IDs from different libraries
are present.
While rare, this situation can become deadly to liboverride, by causing
random but exponential liboverride hierarchies corruptions.
This has already been alleviated by using preferably both name and index
in items lookup (a05419f18b) and by reducing the risk of name collision
in general between liboverrides and their linked reference (b9becc47de).
This commit goes further, by ensuring that references to items of RNA
collections of IDs stored in liboverride operations become completely
unambiguous. This is achieved by storing an extra pointer to the item's
ID itself, when relevant.
Lookup then requires a complete match `name + ID` to be successful,
which is guaranteed to match at most a single item in the whole RNA
collection (since RNA collection of IDs do not allow duplicates, and
the ID pointer is always unique).
Note that this ID pointer is implemented as an `std::optional` one
(either directly in C++ code, or using an new liboverride operation `flag`
in DNA). This allows to smoothly transition from existing data to the
added ID pointer info (when needed), without needing any dedicated
versioning. This solution also preserves forward compatibility as much
as possible.
It may also provide marginal performances improvements in some cases, as
looking up for ID items in RNA collections will first check for the
ID pointer, which should be faster than a string comparision.
Implements #110421.
Pull Request: https://projects.blender.org/blender/blender/pulls/110773
2023-08-03 20:32:36 +02:00
|
|
|
oprop, nullptr, nullptr, {}, {}, index, index, false, &is_strict_find);
|
2022-07-29 23:16:58 -05:00
|
|
|
BLI_assert(opop != nullptr);
|
2017-11-29 17:03:52 +01:00
|
|
|
if (!is_strict_find) {
|
|
|
|
|
/* No specific override operation, we have to get generic one,
|
2019-01-15 23:24:20 +11:00
|
|
|
* and create item-specific override operations for all but given index,
|
|
|
|
|
* before removing generic one. */
|
2017-11-29 17:03:52 +01:00
|
|
|
for (int idx = RNA_property_array_length(&ptr, prop); idx--;) {
|
|
|
|
|
if (idx != index) {
|
2020-02-10 18:05:19 +01:00
|
|
|
BKE_lib_override_library_property_operation_get(
|
LibOverride: Add ID pointer to operations over ID pointers.
In RNA collections storing ID references, the name of the collection
item may not always be unique, when several IDs from different libraries
are present.
While rare, this situation can become deadly to liboverride, by causing
random but exponential liboverride hierarchies corruptions.
This has already been alleviated by using preferably both name and index
in items lookup (a05419f18b) and by reducing the risk of name collision
in general between liboverrides and their linked reference (b9becc47de).
This commit goes further, by ensuring that references to items of RNA
collections of IDs stored in liboverride operations become completely
unambiguous. This is achieved by storing an extra pointer to the item's
ID itself, when relevant.
Lookup then requires a complete match `name + ID` to be successful,
which is guaranteed to match at most a single item in the whole RNA
collection (since RNA collection of IDs do not allow duplicates, and
the ID pointer is always unique).
Note that this ID pointer is implemented as an `std::optional` one
(either directly in C++ code, or using an new liboverride operation `flag`
in DNA). This allows to smoothly transition from existing data to the
added ID pointer info (when needed), without needing any dedicated
versioning. This solution also preserves forward compatibility as much
as possible.
It may also provide marginal performances improvements in some cases, as
looking up for ID items in RNA collections will first check for the
ID pointer, which should be faster than a string comparision.
Implements #110421.
Pull Request: https://projects.blender.org/blender/blender/pulls/110773
2023-08-03 20:32:36 +02:00
|
|
|
oprop, opop->operation, nullptr, nullptr, {}, {}, idx, idx, true, nullptr, nullptr);
|
2017-11-29 17:03:52 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-02-10 18:05:19 +01:00
|
|
|
BKE_lib_override_library_property_operation_delete(oprop, opop);
|
2023-12-20 17:53:34 +01:00
|
|
|
RNA_property_copy(bmain, &ptr, &src, prop, index);
|
2017-11-29 17:03:52 +01:00
|
|
|
if (BLI_listbase_is_empty(&oprop->operations)) {
|
2020-02-10 18:05:19 +01:00
|
|
|
BKE_lib_override_library_property_delete(id->override_library, oprop);
|
2017-11-29 17:03:52 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* Just remove whole generic override operation of this property. */
|
2020-02-10 18:05:19 +01:00
|
|
|
BKE_lib_override_library_property_delete(id->override_library, oprop);
|
2023-12-20 17:53:34 +01:00
|
|
|
RNA_property_copy(bmain, &ptr, &src, prop, -1);
|
2017-11-29 17:03:52 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-01-27 17:51:07 +01:00
|
|
|
/* Outliner e.g. has to be aware of this change. */
|
2022-07-29 23:16:58 -05:00
|
|
|
WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, nullptr);
|
2022-01-27 17:51:07 +01:00
|
|
|
|
2017-11-29 17:03:52 +01:00
|
|
|
return operator_button_property_finish(C, &ptr, prop);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void UI_OT_override_remove_button(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Remove Override";
|
|
|
|
|
ot->idname = "UI_OT_override_remove_button";
|
|
|
|
|
ot->description = "Remove an override operation";
|
|
|
|
|
|
|
|
|
|
/* callbacks */
|
|
|
|
|
ot->poll = override_remove_button_poll;
|
|
|
|
|
ot->exec = override_remove_button_exec;
|
|
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_UNDO;
|
|
|
|
|
|
|
|
|
|
/* properties */
|
2022-07-29 23:16:58 -05:00
|
|
|
RNA_def_boolean(
|
|
|
|
|
ot->srna, "all", true, "All", "Reset to default values all elements of the array");
|
2017-11-29 17:03:52 +01:00
|
|
|
}
|
|
|
|
|
|
2022-08-17 12:10:31 +02:00
|
|
|
static void override_idtemplate_ids_get(
|
|
|
|
|
bContext *C, ID **r_owner_id, ID **r_id, PointerRNA *r_owner_ptr, PropertyRNA **r_prop)
|
|
|
|
|
{
|
|
|
|
|
PointerRNA owner_ptr;
|
|
|
|
|
PropertyRNA *prop;
|
|
|
|
|
UI_context_active_but_prop_get_templateID(C, &owner_ptr, &prop);
|
|
|
|
|
|
2022-08-28 20:52:28 +10:00
|
|
|
if (owner_ptr.data == nullptr || prop == nullptr) {
|
|
|
|
|
*r_owner_id = *r_id = nullptr;
|
|
|
|
|
if (r_owner_ptr != nullptr) {
|
2022-08-17 12:10:31 +02:00
|
|
|
*r_owner_ptr = PointerRNA_NULL;
|
|
|
|
|
}
|
2022-08-28 20:52:28 +10:00
|
|
|
if (r_prop != nullptr) {
|
|
|
|
|
*r_prop = nullptr;
|
2022-08-17 12:10:31 +02:00
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*r_owner_id = owner_ptr.owner_id;
|
|
|
|
|
PointerRNA idptr = RNA_property_pointer_get(&owner_ptr, prop);
|
2022-08-17 21:33:46 +10:00
|
|
|
*r_id = static_cast<ID *>(idptr.data);
|
2022-08-28 20:52:28 +10:00
|
|
|
if (r_owner_ptr != nullptr) {
|
2022-08-17 12:10:31 +02:00
|
|
|
*r_owner_ptr = owner_ptr;
|
|
|
|
|
}
|
2022-08-28 20:52:28 +10:00
|
|
|
if (r_prop != nullptr) {
|
2022-08-17 12:10:31 +02:00
|
|
|
*r_prop = prop;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool override_idtemplate_poll(bContext *C, const bool is_create_op)
|
|
|
|
|
{
|
|
|
|
|
ID *owner_id, *id;
|
2022-08-28 20:52:28 +10:00
|
|
|
override_idtemplate_ids_get(C, &owner_id, &id, nullptr, nullptr);
|
2022-08-17 12:10:31 +02:00
|
|
|
|
2022-08-28 20:52:28 +10:00
|
|
|
if (owner_id == nullptr || id == nullptr) {
|
2022-08-17 12:10:31 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_create_op) {
|
|
|
|
|
if (!ID_IS_LINKED(id) && !ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Reset/Clear operations. */
|
|
|
|
|
if (ID_IS_LINKED(id) || !ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-24 12:11:50 +02:00
|
|
|
static bool override_idtemplate_make_poll(bContext *C)
|
2022-08-17 12:10:31 +02:00
|
|
|
{
|
|
|
|
|
return override_idtemplate_poll(C, true);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int override_idtemplate_make_exec(bContext *C, wmOperator * /*op*/)
|
2022-08-17 12:10:31 +02:00
|
|
|
{
|
|
|
|
|
ID *owner_id, *id;
|
|
|
|
|
PointerRNA owner_ptr;
|
|
|
|
|
PropertyRNA *prop;
|
|
|
|
|
override_idtemplate_ids_get(C, &owner_id, &id, &owner_ptr, &prop);
|
2022-08-28 20:52:28 +10:00
|
|
|
if (ELEM(nullptr, owner_id, id)) {
|
2022-08-17 12:10:31 +02:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-24 12:11:50 +02:00
|
|
|
ID *id_override = ui_template_id_liboverride_hierarchy_make(
|
2022-08-28 20:52:28 +10:00
|
|
|
C, CTX_data_main(C), owner_id, id, nullptr);
|
2022-08-17 12:10:31 +02:00
|
|
|
|
2022-08-28 20:52:28 +10:00
|
|
|
if (id_override == nullptr) {
|
2022-08-17 12:10:31 +02:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-24 17:07:43 +02:00
|
|
|
/* `idptr` is re-assigned to owner property to ensure proper updates etc. Here we also use it
|
|
|
|
|
* to ensure remapping of the owner property from the linked data to the newly created
|
|
|
|
|
* liboverride (note that in theory this remapping has already been done by code above), but
|
|
|
|
|
* only in case owner ID was already local ID (override or pure local data).
|
|
|
|
|
*
|
|
|
|
|
* Otherwise, owner ID will also have been overridden, and remapped already to use it's
|
|
|
|
|
* override of the data too. */
|
|
|
|
|
if (!ID_IS_LINKED(owner_id)) {
|
2023-09-06 00:48:50 +02:00
|
|
|
PointerRNA idptr = RNA_id_pointer_create(id_override);
|
2022-08-28 20:52:28 +10:00
|
|
|
RNA_property_pointer_set(&owner_ptr, prop, idptr, nullptr);
|
2022-08-18 17:21:28 +02:00
|
|
|
}
|
2022-08-24 17:07:43 +02:00
|
|
|
RNA_property_update(C, &owner_ptr, prop);
|
|
|
|
|
|
|
|
|
|
/* 'Security' extra tagging, since this process may also affect the owner ID and not only the
|
|
|
|
|
* used ID, relying on the property update code only is not always enough. */
|
2024-02-19 15:54:08 +01:00
|
|
|
DEG_id_tag_update(&CTX_data_scene(C)->id, ID_RECALC_BASE_FLAGS | ID_RECALC_SYNC_TO_EVAL);
|
2022-08-28 20:52:28 +10:00
|
|
|
WM_event_add_notifier(C, NC_WINDOW, nullptr);
|
|
|
|
|
WM_event_add_notifier(C, NC_WM | ND_LIB_OVERRIDE_CHANGED, nullptr);
|
|
|
|
|
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, nullptr);
|
2022-08-17 12:10:31 +02:00
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-24 12:11:50 +02:00
|
|
|
static void UI_OT_override_idtemplate_make(wmOperatorType *ot)
|
2022-08-17 12:10:31 +02:00
|
|
|
{
|
|
|
|
|
/* identifiers */
|
2022-08-24 12:11:50 +02:00
|
|
|
ot->name = "Make Library Override";
|
|
|
|
|
ot->idname = "UI_OT_override_idtemplate_make";
|
2022-08-17 12:10:31 +02:00
|
|
|
ot->description =
|
|
|
|
|
"Create a local override of the selected linked data-block, and its hierarchy of "
|
|
|
|
|
"dependencies";
|
|
|
|
|
|
|
|
|
|
/* callbacks */
|
2022-08-24 12:11:50 +02:00
|
|
|
ot->poll = override_idtemplate_make_poll;
|
|
|
|
|
ot->exec = override_idtemplate_make_exec;
|
2022-08-17 12:10:31 +02:00
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_UNDO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool override_idtemplate_reset_poll(bContext *C)
|
|
|
|
|
{
|
|
|
|
|
return override_idtemplate_poll(C, false);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int override_idtemplate_reset_exec(bContext *C, wmOperator * /*op*/)
|
2022-08-17 12:10:31 +02:00
|
|
|
{
|
|
|
|
|
ID *owner_id, *id;
|
|
|
|
|
PointerRNA owner_ptr;
|
|
|
|
|
PropertyRNA *prop;
|
|
|
|
|
override_idtemplate_ids_get(C, &owner_id, &id, &owner_ptr, &prop);
|
2022-08-28 20:52:28 +10:00
|
|
|
if (ELEM(nullptr, owner_id, id)) {
|
2022-08-17 12:10:31 +02:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ID_IS_LINKED(id) || !ID_IS_OVERRIDE_LIBRARY_REAL(id)) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BKE_lib_override_library_id_reset(CTX_data_main(C), id, false);
|
|
|
|
|
|
|
|
|
|
/* `idptr` is re-assigned to owner property to ensure proper updates etc. */
|
2023-09-06 00:48:50 +02:00
|
|
|
PointerRNA idptr = RNA_id_pointer_create(id);
|
2022-08-28 20:52:28 +10:00
|
|
|
RNA_property_pointer_set(&owner_ptr, prop, idptr, nullptr);
|
2022-08-17 12:10:31 +02:00
|
|
|
RNA_property_update(C, &owner_ptr, prop);
|
|
|
|
|
|
2022-08-24 17:07:43 +02:00
|
|
|
/* No need for 'security' extra tagging here, since this process will never affect the owner ID.
|
|
|
|
|
*/
|
|
|
|
|
|
2022-08-17 12:10:31 +02:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void UI_OT_override_idtemplate_reset(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Reset Library Override";
|
|
|
|
|
ot->idname = "UI_OT_override_idtemplate_reset";
|
|
|
|
|
ot->description = "Reset the selected local override to its linked reference values";
|
|
|
|
|
|
|
|
|
|
/* callbacks */
|
|
|
|
|
ot->poll = override_idtemplate_reset_poll;
|
|
|
|
|
ot->exec = override_idtemplate_reset_exec;
|
|
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_UNDO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool override_idtemplate_clear_poll(bContext *C)
|
|
|
|
|
{
|
|
|
|
|
return override_idtemplate_poll(C, false);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int override_idtemplate_clear_exec(bContext *C, wmOperator * /*op*/)
|
2022-08-17 12:10:31 +02:00
|
|
|
{
|
|
|
|
|
ID *owner_id, *id;
|
|
|
|
|
PointerRNA owner_ptr;
|
|
|
|
|
PropertyRNA *prop;
|
|
|
|
|
override_idtemplate_ids_get(C, &owner_id, &id, &owner_ptr, &prop);
|
2022-08-28 20:52:28 +10:00
|
|
|
if (ELEM(nullptr, owner_id, id)) {
|
2022-08-17 12:10:31 +02:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ID_IS_LINKED(id)) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Main *bmain = CTX_data_main(C);
|
2022-08-24 17:07:43 +02:00
|
|
|
ViewLayer *view_layer = CTX_data_view_layer(C);
|
|
|
|
|
Scene *scene = CTX_data_scene(C);
|
2022-08-17 12:10:31 +02:00
|
|
|
ID *id_new = id;
|
2022-08-24 17:07:43 +02:00
|
|
|
|
2022-08-17 12:10:31 +02:00
|
|
|
if (BKE_lib_override_library_is_hierarchy_leaf(bmain, id)) {
|
|
|
|
|
id_new = id->override_library->reference;
|
2022-08-24 17:07:43 +02:00
|
|
|
bool do_remap_active = false;
|
2022-09-14 21:33:51 +02:00
|
|
|
BKE_view_layer_synced_ensure(scene, view_layer);
|
2022-09-01 10:00:53 +02:00
|
|
|
if (BKE_view_layer_active_object_get(view_layer) == (Object *)id) {
|
2022-08-24 17:07:43 +02:00
|
|
|
BLI_assert(GS(id->name) == ID_OB);
|
|
|
|
|
BLI_assert(GS(id_new->name) == ID_OB);
|
|
|
|
|
do_remap_active = true;
|
|
|
|
|
}
|
2022-08-17 12:10:31 +02:00
|
|
|
BKE_libblock_remap(bmain, id, id_new, ID_REMAP_SKIP_INDIRECT_USAGE);
|
2022-08-24 17:07:43 +02:00
|
|
|
if (do_remap_active) {
|
|
|
|
|
Object *ref_object = (Object *)id_new;
|
|
|
|
|
Base *basact = BKE_view_layer_base_find(view_layer, ref_object);
|
2022-08-28 20:52:28 +10:00
|
|
|
if (basact != nullptr) {
|
2022-08-24 17:07:43 +02:00
|
|
|
view_layer->basact = basact;
|
|
|
|
|
}
|
|
|
|
|
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
|
|
|
|
|
}
|
2022-08-17 12:10:31 +02:00
|
|
|
BKE_id_delete(bmain, id);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
BKE_lib_override_library_id_reset(bmain, id, true);
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-24 17:07:43 +02:00
|
|
|
/* Here the affected ID may remain the same, or be replaced by its linked reference. In either
|
|
|
|
|
* case, the owner ID remains unchanged, and remapping is already handled by internal code, so
|
|
|
|
|
* calling `RNA_property_update` on it is enough to ensure proper notifiers are sent. */
|
2022-08-17 12:10:31 +02:00
|
|
|
RNA_property_update(C, &owner_ptr, prop);
|
|
|
|
|
|
2022-08-24 17:07:43 +02:00
|
|
|
/* 'Security' extra tagging, since this process may also affect the owner ID and not only the
|
|
|
|
|
* used ID, relying on the property update code only is not always enough. */
|
2024-02-19 15:54:08 +01:00
|
|
|
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS | ID_RECALC_SYNC_TO_EVAL);
|
2022-08-28 20:52:28 +10:00
|
|
|
WM_event_add_notifier(C, NC_WINDOW, nullptr);
|
|
|
|
|
WM_event_add_notifier(C, NC_WM | ND_LIB_OVERRIDE_CHANGED, nullptr);
|
|
|
|
|
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, nullptr);
|
2022-08-24 17:07:43 +02:00
|
|
|
|
2022-08-17 12:10:31 +02:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void UI_OT_override_idtemplate_clear(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Clear Library Override";
|
|
|
|
|
ot->idname = "UI_OT_override_idtemplate_clear";
|
|
|
|
|
ot->description =
|
|
|
|
|
"Delete the selected local override and relink its usages to the linked data-block if "
|
|
|
|
|
"possible, else reset it and mark it as non editable";
|
|
|
|
|
|
|
|
|
|
/* callbacks */
|
|
|
|
|
ot->poll = override_idtemplate_clear_poll;
|
|
|
|
|
ot->exec = override_idtemplate_clear_exec;
|
|
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_UNDO;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static bool override_idtemplate_menu_poll(const bContext *C_const, MenuType * /*mt*/)
|
2022-08-17 12:10:31 +02:00
|
|
|
{
|
|
|
|
|
bContext *C = (bContext *)C_const;
|
|
|
|
|
ID *owner_id, *id;
|
2022-08-28 20:52:28 +10:00
|
|
|
override_idtemplate_ids_get(C, &owner_id, &id, nullptr, nullptr);
|
2022-08-17 12:10:31 +02:00
|
|
|
|
2022-08-28 20:52:28 +10:00
|
|
|
if (owner_id == nullptr || id == nullptr) {
|
2022-08-17 12:10:31 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(ID_IS_LINKED(id) || ID_IS_OVERRIDE_LIBRARY_REAL(id))) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static void override_idtemplate_menu_draw(const bContext * /*C*/, Menu *menu)
|
2022-08-17 12:10:31 +02:00
|
|
|
{
|
|
|
|
|
uiLayout *layout = menu->layout;
|
2022-08-24 12:11:50 +02:00
|
|
|
uiItemO(layout, IFACE_("Make"), ICON_NONE, "UI_OT_override_idtemplate_make");
|
2022-08-17 12:10:31 +02:00
|
|
|
uiItemO(layout, IFACE_("Reset"), ICON_NONE, "UI_OT_override_idtemplate_reset");
|
|
|
|
|
uiItemO(layout, IFACE_("Clear"), ICON_NONE, "UI_OT_override_idtemplate_clear");
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-02 20:33:36 +02:00
|
|
|
static void override_idtemplate_menu()
|
2022-08-17 12:10:31 +02:00
|
|
|
{
|
|
|
|
|
MenuType *mt;
|
|
|
|
|
|
2022-08-17 21:33:46 +10:00
|
|
|
mt = MEM_cnew<MenuType>(__func__);
|
2023-06-19 11:37:05 +10:00
|
|
|
STRNCPY(mt->idname, "UI_MT_idtemplate_liboverride");
|
|
|
|
|
STRNCPY(mt->label, N_("Library Override"));
|
2022-08-17 12:10:31 +02:00
|
|
|
mt->poll = override_idtemplate_menu_poll;
|
|
|
|
|
mt->draw = override_idtemplate_menu_draw;
|
|
|
|
|
WM_menutype_add(mt);
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-23 12:07:05 +11:00
|
|
|
/** \} */
|
2017-11-29 17:03:52 +01:00
|
|
|
|
2019-03-23 12:07:05 +11:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Copy To Selected Operator
|
|
|
|
|
* \{ */
|
2009-12-17 17:15:38 +00:00
|
|
|
|
2022-07-29 23:16:58 -05:00
|
|
|
#define NOT_NULL(assignment) ((assignment) != nullptr)
|
|
|
|
|
#define NOT_RNA_NULL(assignment) ((assignment).data != nullptr)
|
2019-11-16 20:50:59 +03:00
|
|
|
|
2024-03-27 00:47:39 +01:00
|
|
|
static void ui_context_selected_bones_via_pose(bContext *C, blender::Vector<PointerRNA> *r_lb)
|
2019-11-16 20:50:59 +03:00
|
|
|
{
|
2024-03-27 00:47:39 +01:00
|
|
|
blender::Vector<PointerRNA> lb = CTX_data_collection_get(C, "selected_pose_bones");
|
2019-11-16 20:50:59 +03:00
|
|
|
|
2024-03-27 00:47:39 +01:00
|
|
|
if (!lb.is_empty()) {
|
|
|
|
|
for (PointerRNA &ptr : lb) {
|
|
|
|
|
bPoseChannel *pchan = static_cast<bPoseChannel *>(ptr.data);
|
|
|
|
|
ptr = RNA_pointer_create(ptr.owner_id, &RNA_Bone, pchan->bone);
|
2019-11-16 20:50:59 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-27 00:47:39 +01:00
|
|
|
*r_lb = std::move(lb);
|
2019-11-16 20:50:59 +03:00
|
|
|
}
|
|
|
|
|
|
2024-03-27 00:47:39 +01:00
|
|
|
static void ui_context_fcurve_modifiers_via_fcurve(bContext *C,
|
|
|
|
|
blender::Vector<PointerRNA> *r_lb,
|
|
|
|
|
FModifier *source)
|
2023-09-21 09:57:17 +02:00
|
|
|
{
|
2024-03-27 00:47:39 +01:00
|
|
|
blender::Vector<PointerRNA> fcurve_links;
|
2023-09-21 09:57:17 +02:00
|
|
|
fcurve_links = CTX_data_collection_get(C, "selected_editable_fcurves");
|
2024-03-27 00:47:39 +01:00
|
|
|
if (fcurve_links.is_empty()) {
|
2023-09-21 09:57:17 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2024-03-27 00:47:39 +01:00
|
|
|
r_lb->clear();
|
|
|
|
|
for (const PointerRNA &ptr : fcurve_links) {
|
2024-05-21 13:13:09 +10:00
|
|
|
const FCurve *fcu = static_cast<const FCurve *>(ptr.data);
|
2023-09-21 09:57:17 +02:00
|
|
|
LISTBASE_FOREACH (FModifier *, mod, &fcu->modifiers) {
|
|
|
|
|
if (STREQ(mod->name, source->name) && mod->type == source->type) {
|
2024-03-27 00:47:39 +01:00
|
|
|
r_lb->append(RNA_pointer_create(ptr.owner_id, &RNA_FModifier, mod));
|
2023-09-21 09:57:17 +02:00
|
|
|
/* Since names are unique it is safe to break here. */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-11 15:32:43 +10:00
|
|
|
bool UI_context_copy_to_selected_list(bContext *C,
|
2014-08-05 18:59:02 +02:00
|
|
|
PointerRNA *ptr,
|
|
|
|
|
PropertyRNA *prop,
|
2024-03-27 00:47:39 +01:00
|
|
|
blender::Vector<PointerRNA> *r_lb,
|
2014-08-05 18:59:02 +02:00
|
|
|
bool *r_use_path_from_id,
|
2024-01-31 17:08:09 +01:00
|
|
|
std::optional<std::string> *r_path)
|
2009-12-17 17:15:38 +00:00
|
|
|
{
|
2014-08-05 18:59:02 +02:00
|
|
|
*r_use_path_from_id = false;
|
2024-01-31 17:08:09 +01:00
|
|
|
*r_path = std::nullopt;
|
2020-02-20 12:36:59 +01:00
|
|
|
/* special case for bone constraints */
|
2023-09-29 13:50:56 +10:00
|
|
|
const bool is_rna = !RNA_property_is_idprop(prop);
|
2020-06-09 16:17:35 +10:00
|
|
|
/* Remove links from the collection list which don't contain 'prop'. */
|
|
|
|
|
bool ensure_list_items_contain_prop = false;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-11-16 20:50:59 +03:00
|
|
|
/* PropertyGroup objects don't have a reference to the struct that actually owns
|
|
|
|
|
* them, so it is normally necessary to do a brute force search to find it. This
|
|
|
|
|
* handles the search for non-ID owners by using the 'active' reference as a hint
|
|
|
|
|
* to preserve efficiency. Only properties defined through RNA are handled, as
|
|
|
|
|
* custom properties cannot be assumed to be valid for all instances.
|
|
|
|
|
*
|
|
|
|
|
* Properties owned by the ID are handled by the 'if (ptr->owner_id)' case below.
|
|
|
|
|
*/
|
2023-09-29 13:50:56 +10:00
|
|
|
if (is_rna && RNA_struct_is_a(ptr->type, &RNA_PropertyGroup)) {
|
2019-11-16 20:50:59 +03:00
|
|
|
PointerRNA owner_ptr;
|
2024-01-31 17:08:09 +01:00
|
|
|
std::optional<std::string> idpath;
|
2019-11-16 20:50:59 +03:00
|
|
|
|
|
|
|
|
/* First, check the active PoseBone and PoseBone->Bone. */
|
|
|
|
|
if (NOT_RNA_NULL(owner_ptr = CTX_data_pointer_get_type(C, "active_pose_bone", &RNA_PoseBone)))
|
|
|
|
|
{
|
2024-01-31 17:08:09 +01:00
|
|
|
idpath = RNA_path_from_struct_to_idproperty(&owner_ptr,
|
|
|
|
|
static_cast<const IDProperty *>(ptr->data));
|
|
|
|
|
if (idpath) {
|
2019-11-16 20:50:59 +03:00
|
|
|
*r_lb = CTX_data_collection_get(C, "selected_pose_bones");
|
|
|
|
|
}
|
|
|
|
|
else {
|
2022-07-29 23:16:58 -05:00
|
|
|
bPoseChannel *pchan = static_cast<bPoseChannel *>(owner_ptr.data);
|
2023-09-06 00:48:50 +02:00
|
|
|
owner_ptr = RNA_pointer_create(owner_ptr.owner_id, &RNA_Bone, pchan->bone);
|
2024-01-31 17:08:09 +01:00
|
|
|
idpath = RNA_path_from_struct_to_idproperty(&owner_ptr,
|
|
|
|
|
static_cast<const IDProperty *>(ptr->data));
|
|
|
|
|
if (idpath) {
|
2019-11-16 20:50:59 +03:00
|
|
|
ui_context_selected_bones_via_pose(C, r_lb);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-01-31 17:08:09 +01:00
|
|
|
if (!idpath) {
|
2019-11-16 20:50:59 +03:00
|
|
|
/* Check the active EditBone if in edit mode. */
|
2024-01-31 17:08:09 +01:00
|
|
|
idpath = RNA_path_from_struct_to_idproperty(&owner_ptr,
|
|
|
|
|
static_cast<const IDProperty *>(ptr->data));
|
2019-11-16 20:50:59 +03:00
|
|
|
if (NOT_RNA_NULL(
|
|
|
|
|
owner_ptr = CTX_data_pointer_get_type_silent(C, "active_bone", &RNA_EditBone)) &&
|
2024-01-31 17:08:09 +01:00
|
|
|
idpath)
|
2022-07-29 23:16:58 -05:00
|
|
|
{
|
2019-11-16 20:50:59 +03:00
|
|
|
*r_lb = CTX_data_collection_get(C, "selected_editable_bones");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Add other simple cases here (Node, NodeSocket, Sequence, ViewLayer etc). */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (idpath) {
|
2024-01-31 17:08:09 +01:00
|
|
|
*r_path = fmt::format("{}.{}", *idpath, RNA_property_identifier(prop));
|
2019-11-16 20:50:59 +03:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-05 18:59:02 +02:00
|
|
|
if (RNA_struct_is_a(ptr->type, &RNA_EditBone)) {
|
|
|
|
|
*r_lb = CTX_data_collection_get(C, "selected_editable_bones");
|
|
|
|
|
}
|
|
|
|
|
else if (RNA_struct_is_a(ptr->type, &RNA_PoseBone)) {
|
|
|
|
|
*r_lb = CTX_data_collection_get(C, "selected_pose_bones");
|
|
|
|
|
}
|
2015-07-21 15:08:06 +10:00
|
|
|
else if (RNA_struct_is_a(ptr->type, &RNA_Bone)) {
|
2019-11-16 20:50:59 +03:00
|
|
|
ui_context_selected_bones_via_pose(C, r_lb);
|
2015-07-21 15:08:06 +10:00
|
|
|
}
|
Anim: move bone colors from bone groups to individual bones
Move control over the color of bones from bone groups to the bones
themselves. Instead of using bone groups (which are defined on the pose,
and thus owned by the object), the color is stored on:
- the bone (`struct Bone`, or RNA `armature.bones['bone_name'].color`)
- a possible override on the pose bone (`struct bPoseChannel`, or RNA
`ob.pose.bones['bone_name'].color`).
When the pose bone is set to its default color, the color is determined
by the armature bone. In armature edit mode, the armature bone colors
are always used, as then the pose data is unavailable.
Versioning code converts bone group colors to bone colors. If the
Armature has a single user, the group color is stored on the bones
directly. If it has multiple users, the group colors will be stored on
the pose bones instead.
The bone group color is not removed from DNA for forward compatibility,
that is, to avoid immediate dataloss when saving a 3.6 file with 4.0.
This is part of the replacement of bone groups & armature layers with
bone collections. See the design task at #108941.
Pull request: https://projects.blender.org/blender/blender/pulls/109976
2023-08-22 11:11:52 +02:00
|
|
|
else if (RNA_struct_is_a(ptr->type, &RNA_BoneColor)) {
|
|
|
|
|
/* Get the things that own the bone color (bones, pose bones, or edit bones). */
|
2024-03-27 00:47:39 +01:00
|
|
|
/* First this will be bones, then gets remapped to colors. */
|
|
|
|
|
blender::Vector<PointerRNA> list_of_things = {};
|
Anim: move bone colors from bone groups to individual bones
Move control over the color of bones from bone groups to the bones
themselves. Instead of using bone groups (which are defined on the pose,
and thus owned by the object), the color is stored on:
- the bone (`struct Bone`, or RNA `armature.bones['bone_name'].color`)
- a possible override on the pose bone (`struct bPoseChannel`, or RNA
`ob.pose.bones['bone_name'].color`).
When the pose bone is set to its default color, the color is determined
by the armature bone. In armature edit mode, the armature bone colors
are always used, as then the pose data is unavailable.
Versioning code converts bone group colors to bone colors. If the
Armature has a single user, the group color is stored on the bones
directly. If it has multiple users, the group colors will be stored on
the pose bones instead.
The bone group color is not removed from DNA for forward compatibility,
that is, to avoid immediate dataloss when saving a 3.6 file with 4.0.
This is part of the replacement of bone groups & armature layers with
bone collections. See the design task at #108941.
Pull request: https://projects.blender.org/blender/blender/pulls/109976
2023-08-22 11:11:52 +02:00
|
|
|
switch (GS(ptr->owner_id->name)) {
|
|
|
|
|
case ID_OB:
|
|
|
|
|
list_of_things = CTX_data_collection_get(C, "selected_pose_bones");
|
|
|
|
|
break;
|
|
|
|
|
case ID_AR: {
|
|
|
|
|
/* Armature-owned bones can be accessed from both edit mode and pose mode.
|
|
|
|
|
* - Edit mode: visit selected edit bones.
|
|
|
|
|
* - Pose mode: visit the armature bones of selected pose bones.
|
|
|
|
|
*/
|
|
|
|
|
const bArmature *arm = reinterpret_cast<bArmature *>(ptr->owner_id);
|
|
|
|
|
if (arm->edbo) {
|
|
|
|
|
list_of_things = CTX_data_collection_get(C, "selected_editable_bones");
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
list_of_things = CTX_data_collection_get(C, "selected_pose_bones");
|
|
|
|
|
CTX_data_collection_remap_property(list_of_things, "bone");
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
printf("BoneColor is unexpectedly owned by %s '%s'\n",
|
|
|
|
|
BKE_idtype_idcode_to_name(GS(ptr->owner_id->name)),
|
|
|
|
|
ptr->owner_id->name + 2);
|
2024-04-03 14:24:39 +11:00
|
|
|
BLI_assert_msg(false,
|
|
|
|
|
"expected BoneColor to be owned by the Armature "
|
|
|
|
|
"(bone & edit bone) or the Object (pose bone)");
|
Anim: move bone colors from bone groups to individual bones
Move control over the color of bones from bone groups to the bones
themselves. Instead of using bone groups (which are defined on the pose,
and thus owned by the object), the color is stored on:
- the bone (`struct Bone`, or RNA `armature.bones['bone_name'].color`)
- a possible override on the pose bone (`struct bPoseChannel`, or RNA
`ob.pose.bones['bone_name'].color`).
When the pose bone is set to its default color, the color is determined
by the armature bone. In armature edit mode, the armature bone colors
are always used, as then the pose data is unavailable.
Versioning code converts bone group colors to bone colors. If the
Armature has a single user, the group color is stored on the bones
directly. If it has multiple users, the group colors will be stored on
the pose bones instead.
The bone group color is not removed from DNA for forward compatibility,
that is, to avoid immediate dataloss when saving a 3.6 file with 4.0.
This is part of the replacement of bone groups & armature layers with
bone collections. See the design task at #108941.
Pull request: https://projects.blender.org/blender/blender/pulls/109976
2023-08-22 11:11:52 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Remap from some bone to its color, to ensure the items of r_lb are of
|
|
|
|
|
* type ptr->type. Since all three structs `bPoseChan`, `Bone`, and
|
|
|
|
|
* `EditBone` have the same name for their embedded `BoneColor` struct, this
|
|
|
|
|
* code is suitable for all of them. */
|
|
|
|
|
CTX_data_collection_remap_property(list_of_things, "color");
|
|
|
|
|
|
|
|
|
|
*r_lb = list_of_things;
|
|
|
|
|
}
|
2014-08-05 18:59:02 +02:00
|
|
|
else if (RNA_struct_is_a(ptr->type, &RNA_Sequence)) {
|
2020-03-04 15:55:22 +01:00
|
|
|
/* Special case when we do this for 'Sequence.lock'.
|
2021-06-22 10:42:32 -07:00
|
|
|
* (if the sequence is locked, it won't be in "selected_editable_sequences"). */
|
2020-03-04 15:55:22 +01:00
|
|
|
const char *prop_id = RNA_property_identifier(prop);
|
|
|
|
|
if (STREQ(prop_id, "lock")) {
|
|
|
|
|
*r_lb = CTX_data_collection_get(C, "selected_sequences");
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
*r_lb = CTX_data_collection_get(C, "selected_editable_sequences");
|
|
|
|
|
}
|
2023-09-29 13:50:56 +10:00
|
|
|
|
|
|
|
|
if (is_rna) {
|
|
|
|
|
/* Account for properties only being available for some sequence types. */
|
|
|
|
|
ensure_list_items_contain_prop = true;
|
|
|
|
|
}
|
2014-08-05 18:59:02 +02:00
|
|
|
}
|
2017-08-19 19:58:39 +03:00
|
|
|
else if (RNA_struct_is_a(ptr->type, &RNA_FCurve)) {
|
|
|
|
|
*r_lb = CTX_data_collection_get(C, "selected_editable_fcurves");
|
2020-10-15 14:53:26 -04:00
|
|
|
}
|
2023-09-21 09:57:17 +02:00
|
|
|
else if (RNA_struct_is_a(ptr->type, &RNA_FModifier)) {
|
|
|
|
|
FModifier *mod = static_cast<FModifier *>(ptr->data);
|
|
|
|
|
ui_context_fcurve_modifiers_via_fcurve(C, r_lb, mod);
|
|
|
|
|
}
|
2020-10-15 14:53:26 -04:00
|
|
|
else if (RNA_struct_is_a(ptr->type, &RNA_Keyframe)) {
|
|
|
|
|
*r_lb = CTX_data_collection_get(C, "selected_editable_keyframes");
|
2017-08-19 19:58:39 +03:00
|
|
|
}
|
2021-07-16 12:36:57 +03:00
|
|
|
else if (RNA_struct_is_a(ptr->type, &RNA_Action)) {
|
|
|
|
|
*r_lb = CTX_data_collection_get(C, "selected_editable_actions");
|
|
|
|
|
}
|
2020-04-01 10:24:16 +02:00
|
|
|
else if (RNA_struct_is_a(ptr->type, &RNA_NlaStrip)) {
|
|
|
|
|
*r_lb = CTX_data_collection_get(C, "selected_nla_strips");
|
|
|
|
|
}
|
2021-10-19 16:24:23 +02:00
|
|
|
else if (RNA_struct_is_a(ptr->type, &RNA_MovieTrackingTrack)) {
|
|
|
|
|
*r_lb = CTX_data_collection_get(C, "selected_movieclip_tracks");
|
|
|
|
|
}
|
2024-01-31 17:08:09 +01:00
|
|
|
else if (const std::optional<std::string> path_from_bone =
|
|
|
|
|
RNA_path_resolve_from_type_to_property(ptr, prop, &RNA_PoseBone);
|
|
|
|
|
RNA_struct_is_a(ptr->type, &RNA_Constraint) && path_from_bone)
|
2022-07-29 23:16:58 -05:00
|
|
|
{
|
2020-02-20 12:36:59 +01:00
|
|
|
*r_lb = CTX_data_collection_get(C, "selected_pose_bones");
|
|
|
|
|
*r_path = path_from_bone;
|
|
|
|
|
}
|
2015-05-14 06:23:41 +10:00
|
|
|
else if (RNA_struct_is_a(ptr->type, &RNA_Node) || RNA_struct_is_a(ptr->type, &RNA_NodeSocket)) {
|
2024-03-27 00:47:39 +01:00
|
|
|
blender::Vector<PointerRNA> lb;
|
2024-01-31 17:08:09 +01:00
|
|
|
std::optional<std::string> path;
|
2022-07-29 23:16:58 -05:00
|
|
|
bNode *node = nullptr;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-05-14 06:23:41 +10:00
|
|
|
/* Get the node we're editing */
|
|
|
|
|
if (RNA_struct_is_a(ptr->type, &RNA_NodeSocket)) {
|
2019-08-23 09:52:12 +02:00
|
|
|
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
|
2022-07-29 23:16:58 -05:00
|
|
|
bNodeSocket *sock = static_cast<bNodeSocket *>(ptr->data);
|
2024-05-13 16:07:12 +02:00
|
|
|
if (blender::bke::nodeFindNodeTry(ntree, sock, &node, nullptr)) {
|
2024-01-31 17:08:09 +01:00
|
|
|
path = RNA_path_resolve_from_type_to_property(ptr, prop, &RNA_Node);
|
|
|
|
|
if (path) {
|
2015-05-14 06:23:41 +10:00
|
|
|
/* we're good! */
|
|
|
|
|
}
|
|
|
|
|
else {
|
2022-07-29 23:16:58 -05:00
|
|
|
node = nullptr;
|
2015-05-14 06:23:41 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
2022-07-29 23:16:58 -05:00
|
|
|
node = static_cast<bNode *>(ptr->data);
|
2015-05-14 06:23:41 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-05-14 06:23:41 +10:00
|
|
|
/* Now filter by type */
|
|
|
|
|
if (node) {
|
|
|
|
|
lb = CTX_data_collection_get(C, "selected_nodes");
|
2024-03-27 00:47:39 +01:00
|
|
|
lb.remove_if([&](const PointerRNA &link) {
|
|
|
|
|
bNode *node_data = static_cast<bNode *>(link.data);
|
2015-05-14 06:23:41 +10:00
|
|
|
if (node_data->type != node->type) {
|
2024-03-27 00:47:39 +01:00
|
|
|
return true;
|
2015-05-14 06:23:41 +10:00
|
|
|
}
|
2024-03-27 00:47:39 +01:00
|
|
|
return false;
|
|
|
|
|
});
|
2015-05-13 09:22:42 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-05-14 06:23:41 +10:00
|
|
|
*r_lb = lb;
|
|
|
|
|
*r_path = path;
|
2015-05-13 09:22:42 +10:00
|
|
|
}
|
2019-08-23 09:52:12 +02:00
|
|
|
else if (ptr->owner_id) {
|
|
|
|
|
ID *id = ptr->owner_id;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-08-05 18:59:02 +02:00
|
|
|
if (GS(id->name) == ID_OB) {
|
|
|
|
|
*r_lb = CTX_data_collection_get(C, "selected_editable_objects");
|
|
|
|
|
*r_use_path_from_id = true;
|
|
|
|
|
*r_path = RNA_path_from_ID_to_property(ptr, prop);
|
2014-01-04 17:16:19 +11:00
|
|
|
}
|
2015-05-13 07:23:03 +10:00
|
|
|
else if (OB_DATA_SUPPORT_ID(GS(id->name))) {
|
|
|
|
|
/* check we're using the active object */
|
2015-05-13 07:36:43 +10:00
|
|
|
const short id_code = GS(id->name);
|
2024-03-27 00:47:39 +01:00
|
|
|
blender::Vector<PointerRNA> lb = CTX_data_collection_get(C, "selected_editable_objects");
|
2024-01-31 17:08:09 +01:00
|
|
|
const std::optional<std::string> path = RNA_path_from_ID_to_property(ptr, prop);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-05-13 07:36:43 +10:00
|
|
|
/* de-duplicate obdata */
|
2024-03-27 00:47:39 +01:00
|
|
|
if (!lb.is_empty()) {
|
2024-04-03 14:41:43 +11:00
|
|
|
for (const PointerRNA &ob_ptr : lb) {
|
|
|
|
|
Object *ob = (Object *)ob_ptr.owner_id;
|
2024-03-27 00:47:39 +01:00
|
|
|
if (ID *id_data = static_cast<ID *>(ob->data)) {
|
Split id->flag in two, persistent flags and runtime tags.
This is purely internal sanitizing/cleanup, no change in behavior is expected at all.
This change was also needed because we were getting short on ID flags, and
future enhancement of 'user_one' ID behavior requires two new ones.
id->flag remains for persistent data (fakeuser only, so far!), this also allows us
100% backward & forward compatibility.
New id->tag is used for most flags. Though written in .blend files, its content
is cleared at read time.
Note that .blend file version was bumped, so that we can clear runtimeflags from
old .blends, important in case we add new persistent flags in future.
Also, behavior of tags (either status ones, or whether they need to be cleared before/after use)
has been added as comments to their declaration.
Reviewers: sergey, campbellbarton
Differential Revision: https://developer.blender.org/D1683
2015-12-27 11:53:50 +01:00
|
|
|
id_data->tag |= LIB_TAG_DOIT;
|
2015-05-13 07:23:03 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
|
2024-03-27 00:47:39 +01:00
|
|
|
blender::Vector<PointerRNA> new_lb;
|
|
|
|
|
for (const PointerRNA &link : lb) {
|
|
|
|
|
Object *ob = (Object *)link.owner_id;
|
2022-07-29 23:16:58 -05:00
|
|
|
ID *id_data = static_cast<ID *>(ob->data);
|
|
|
|
|
if ((id_data == nullptr) || (id_data->tag & LIB_TAG_DOIT) == 0 ||
|
2024-05-16 14:53:09 +02:00
|
|
|
!ID_IS_EDITABLE(id_data) || (GS(id_data->name) != id_code))
|
2022-07-29 23:16:58 -05:00
|
|
|
{
|
2024-03-27 00:47:39 +01:00
|
|
|
continue;
|
2015-05-13 07:23:03 +10:00
|
|
|
}
|
2024-03-27 00:47:39 +01:00
|
|
|
/* Avoid prepending 'data' to the path. */
|
|
|
|
|
new_lb.append(RNA_id_pointer_create(id_data));
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-05-27 11:14:39 +10:00
|
|
|
if (id_data) {
|
Split id->flag in two, persistent flags and runtime tags.
This is purely internal sanitizing/cleanup, no change in behavior is expected at all.
This change was also needed because we were getting short on ID flags, and
future enhancement of 'user_one' ID behavior requires two new ones.
id->flag remains for persistent data (fakeuser only, so far!), this also allows us
100% backward & forward compatibility.
New id->tag is used for most flags. Though written in .blend files, its content
is cleared at read time.
Note that .blend file version was bumped, so that we can clear runtimeflags from
old .blends, important in case we add new persistent flags in future.
Also, behavior of tags (either status ones, or whether they need to be cleared before/after use)
has been added as comments to their declaration.
Reviewers: sergey, campbellbarton
Differential Revision: https://developer.blender.org/D1683
2015-12-27 11:53:50 +01:00
|
|
|
id_data->tag &= ~LIB_TAG_DOIT;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2015-05-27 11:14:39 +10:00
|
|
|
}
|
2024-03-27 00:47:39 +01:00
|
|
|
|
|
|
|
|
lb = std::move(new_lb);
|
2015-05-13 07:23:03 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-05-13 07:36:43 +10:00
|
|
|
*r_lb = lb;
|
|
|
|
|
*r_path = path;
|
2015-05-13 07:23:03 +10:00
|
|
|
}
|
2014-08-05 18:59:02 +02:00
|
|
|
else if (GS(id->name) == ID_SCE) {
|
|
|
|
|
/* Sequencer's ID is scene :/ */
|
2019-01-15 23:24:20 +11:00
|
|
|
/* Try to recursively find an RNA_Sequence ancestor,
|
2023-02-12 14:37:16 +11:00
|
|
|
* to handle situations like #41062... */
|
2024-01-31 17:08:09 +01:00
|
|
|
*r_path = RNA_path_resolve_from_type_to_property(ptr, prop, &RNA_Sequence);
|
|
|
|
|
if (r_path->has_value()) {
|
2020-03-04 15:55:22 +01:00
|
|
|
/* Special case when we do this for 'Sequence.lock'.
|
2021-06-22 10:42:32 -07:00
|
|
|
* (if the sequence is locked, it won't be in "selected_editable_sequences"). */
|
2020-03-04 15:55:22 +01:00
|
|
|
const char *prop_id = RNA_property_identifier(prop);
|
2023-09-29 13:50:56 +10:00
|
|
|
if (is_rna && STREQ(prop_id, "lock")) {
|
2020-03-04 15:55:22 +01:00
|
|
|
*r_lb = CTX_data_collection_get(C, "selected_sequences");
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
*r_lb = CTX_data_collection_get(C, "selected_editable_sequences");
|
|
|
|
|
}
|
2023-09-29 13:50:56 +10:00
|
|
|
|
|
|
|
|
if (is_rna) {
|
|
|
|
|
/* Account for properties only being available for some sequence types. */
|
|
|
|
|
ensure_list_items_contain_prop = true;
|
|
|
|
|
}
|
2014-08-05 18:59:02 +02:00
|
|
|
}
|
2012-04-19 10:49:45 +00:00
|
|
|
}
|
2024-01-31 17:08:09 +01:00
|
|
|
return r_path->has_value();
|
2012-04-10 15:49:41 +00:00
|
|
|
}
|
2014-08-05 18:59:02 +02:00
|
|
|
else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-09-29 13:50:59 +10:00
|
|
|
if (RNA_property_is_idprop(prop)) {
|
2024-01-31 17:08:09 +01:00
|
|
|
if (!r_path->has_value()) {
|
2023-09-29 13:50:59 +10:00
|
|
|
*r_path = RNA_path_from_ptr_to_property_index(ptr, prop, 0, -1);
|
|
|
|
|
BLI_assert(*r_path);
|
|
|
|
|
}
|
|
|
|
|
/* Always resolve custom-properties because they can always exist per-item. */
|
|
|
|
|
ensure_list_items_contain_prop = true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-06-09 16:17:35 +10:00
|
|
|
if (ensure_list_items_contain_prop) {
|
2023-09-29 13:50:59 +10:00
|
|
|
if (is_rna) {
|
|
|
|
|
const char *prop_id = RNA_property_identifier(prop);
|
2024-03-27 00:47:39 +01:00
|
|
|
r_lb->remove_if([&](const PointerRNA &link) {
|
|
|
|
|
if ((ptr->type != link.type) &&
|
|
|
|
|
(RNA_struct_type_find_property(link.type, prop_id) != prop))
|
2023-09-29 13:50:59 +10:00
|
|
|
{
|
2024-03-27 00:47:39 +01:00
|
|
|
return true;
|
2023-09-29 13:50:59 +10:00
|
|
|
}
|
2024-03-27 00:47:39 +01:00
|
|
|
return false;
|
|
|
|
|
});
|
2023-09-29 13:50:59 +10:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
const bool prop_is_array = RNA_property_array_check(prop);
|
|
|
|
|
const int prop_array_len = prop_is_array ? RNA_property_array_length(ptr, prop) : -1;
|
|
|
|
|
const PropertyType prop_type = RNA_property_type(prop);
|
2024-03-27 00:47:39 +01:00
|
|
|
r_lb->remove_if([&](PointerRNA &link) {
|
2023-09-29 13:50:59 +10:00
|
|
|
PointerRNA lptr;
|
|
|
|
|
PropertyRNA *lprop = nullptr;
|
2024-01-31 17:08:09 +01:00
|
|
|
RNA_path_resolve_property(
|
2024-03-27 00:47:39 +01:00
|
|
|
&link, r_path->has_value() ? r_path->value().c_str() : nullptr, &lptr, &lprop);
|
2023-09-29 13:50:59 +10:00
|
|
|
|
|
|
|
|
if (lprop == nullptr) {
|
2024-03-27 00:47:39 +01:00
|
|
|
return true;
|
2023-09-29 13:50:59 +10:00
|
|
|
}
|
2024-03-27 00:47:39 +01:00
|
|
|
if (!RNA_property_is_idprop(lprop)) {
|
|
|
|
|
return true;
|
2023-09-29 13:50:59 +10:00
|
|
|
}
|
2024-03-27 00:47:39 +01:00
|
|
|
if (prop_type != RNA_property_type(lprop)) {
|
|
|
|
|
return true;
|
2023-09-29 13:50:59 +10:00
|
|
|
}
|
2024-03-27 00:47:39 +01:00
|
|
|
if (prop_is_array != RNA_property_array_check(lprop)) {
|
|
|
|
|
return true;
|
2023-09-29 13:50:59 +10:00
|
|
|
}
|
2024-03-27 00:47:39 +01:00
|
|
|
if (prop_is_array && (prop_array_len != RNA_property_array_length(&link, lprop))) {
|
|
|
|
|
return true;
|
2023-09-29 13:50:59 +10:00
|
|
|
}
|
2024-03-27 00:47:39 +01:00
|
|
|
return false;
|
|
|
|
|
});
|
2020-06-09 16:17:35 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-04 17:16:19 +11:00
|
|
|
return true;
|
2009-12-17 17:15:38 +00:00
|
|
|
}
|
|
|
|
|
|
2021-12-16 13:03:39 +01:00
|
|
|
bool UI_context_copy_to_selected_check(PointerRNA *ptr,
|
|
|
|
|
PointerRNA *ptr_link,
|
|
|
|
|
PropertyRNA *prop,
|
|
|
|
|
const char *path,
|
|
|
|
|
bool use_path_from_id,
|
|
|
|
|
PointerRNA *r_ptr,
|
|
|
|
|
PropertyRNA **r_prop)
|
|
|
|
|
{
|
|
|
|
|
PropertyRNA *lprop;
|
|
|
|
|
PointerRNA lptr;
|
|
|
|
|
|
|
|
|
|
if (ptr_link->data == ptr->data) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (use_path_from_id) {
|
|
|
|
|
/* Path relative to ID. */
|
2022-07-29 23:16:58 -05:00
|
|
|
lprop = nullptr;
|
2023-09-06 00:48:50 +02:00
|
|
|
PointerRNA idptr = RNA_id_pointer_create(ptr_link->owner_id);
|
2021-12-16 13:03:39 +01:00
|
|
|
RNA_path_resolve_property(&idptr, path, &lptr, &lprop);
|
|
|
|
|
}
|
|
|
|
|
else if (path) {
|
|
|
|
|
/* Path relative to elements from list. */
|
2022-07-29 23:16:58 -05:00
|
|
|
lprop = nullptr;
|
2021-12-16 13:03:39 +01:00
|
|
|
RNA_path_resolve_property(ptr_link, path, &lptr, &lprop);
|
|
|
|
|
}
|
|
|
|
|
else {
|
2023-09-29 13:50:59 +10:00
|
|
|
BLI_assert(!RNA_property_is_idprop(prop));
|
2021-12-16 13:03:39 +01:00
|
|
|
lptr = *ptr_link;
|
|
|
|
|
lprop = prop;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (lptr.data == ptr->data) {
|
2023-08-18 09:10:32 +10:00
|
|
|
/* The source & destination are the same, so there is nothing to copy. */
|
2021-12-16 13:03:39 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-17 15:26:28 +11:00
|
|
|
/* Skip non-existing properties on link. This was previously covered with the `lprop != prop`
|
|
|
|
|
* check but we are now more permissive when it comes to ID properties, see below. */
|
2022-07-29 23:16:58 -05:00
|
|
|
if (lprop == nullptr) {
|
2021-12-16 13:12:36 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (RNA_property_type(lprop) != RNA_property_type(prop)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-17 15:26:28 +11:00
|
|
|
/* Check property pointers matching.
|
|
|
|
|
* For ID properties, these pointers match:
|
|
|
|
|
* - If the property is API defined on an existing class (and they are equally named).
|
|
|
|
|
* - Never for ID properties on specific ID (even if they are equally named).
|
|
|
|
|
* - Never for NodesModifierSettings properties (even if they are equally named).
|
2021-12-16 13:12:36 +01:00
|
|
|
*
|
|
|
|
|
* Be permissive on ID properties in the following cases:
|
2021-12-17 15:26:28 +11:00
|
|
|
* - #NodesModifierSettings properties
|
|
|
|
|
* - (special check: only if the node-group matches, since the 'Input_n' properties are name
|
|
|
|
|
* based and similar on potentially very different node-groups).
|
2021-12-16 13:12:36 +01:00
|
|
|
* - ID properties on specific ID
|
2021-12-17 15:26:28 +11:00
|
|
|
* - (no special check, copying seems OK [even if type does not match -- does not do anything
|
|
|
|
|
* then])
|
2021-12-16 13:12:36 +01:00
|
|
|
*/
|
|
|
|
|
bool ignore_prop_eq = RNA_property_is_idprop(lprop) && RNA_property_is_idprop(prop);
|
|
|
|
|
if (RNA_struct_is_a(lptr.type, &RNA_NodesModifier) &&
|
|
|
|
|
RNA_struct_is_a(ptr->type, &RNA_NodesModifier))
|
|
|
|
|
{
|
|
|
|
|
ignore_prop_eq = false;
|
|
|
|
|
|
|
|
|
|
NodesModifierData *nmd_link = (NodesModifierData *)lptr.data;
|
|
|
|
|
NodesModifierData *nmd_src = (NodesModifierData *)ptr->data;
|
|
|
|
|
if (nmd_link->node_group == nmd_src->node_group) {
|
|
|
|
|
ignore_prop_eq = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((lprop != prop) && !ignore_prop_eq) {
|
2021-12-16 13:03:39 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!RNA_property_editable(&lptr, lprop)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (r_ptr) {
|
|
|
|
|
*r_ptr = lptr;
|
|
|
|
|
}
|
|
|
|
|
if (r_prop) {
|
|
|
|
|
*r_prop = lprop;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-11 08:33:54 +10:00
|
|
|
/**
|
2019-11-25 01:14:39 +11:00
|
|
|
* Called from both exec & poll.
|
2014-04-11 08:33:54 +10:00
|
|
|
*
|
2019-11-25 01:14:39 +11:00
|
|
|
* \note Normally we wouldn't call a loop from within a poll function,
|
|
|
|
|
* however this is a special case, and for regular poll calls, getting
|
2014-04-11 08:33:54 +10:00
|
|
|
* the context from the button will fail early.
|
|
|
|
|
*/
|
|
|
|
|
static bool copy_to_selected_button(bContext *C, bool all, bool poll)
|
2009-12-17 17:15:38 +00:00
|
|
|
{
|
2018-06-29 12:46:54 +02:00
|
|
|
Main *bmain = CTX_data_main(C);
|
2021-12-16 13:03:39 +01:00
|
|
|
PointerRNA ptr, lptr;
|
2012-04-10 15:49:41 +00:00
|
|
|
PropertyRNA *prop, *lprop;
|
2014-04-11 08:33:54 +10:00
|
|
|
int index;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-04-11 08:33:54 +10:00
|
|
|
/* try to reset the nominated setting to its default value */
|
2014-11-09 21:20:40 +01:00
|
|
|
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-04-11 08:33:54 +10:00
|
|
|
/* if there is a valid property that is editable... */
|
2022-07-29 23:16:58 -05:00
|
|
|
if (ptr.data == nullptr || prop == nullptr) {
|
2021-08-03 17:12:03 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-19 14:25:10 +10:00
|
|
|
bool success = false;
|
2024-01-31 17:08:09 +01:00
|
|
|
std::optional<std::string> path;
|
2021-08-03 17:12:03 +02:00
|
|
|
bool use_path_from_id;
|
2024-03-27 00:47:39 +01:00
|
|
|
blender::Vector<PointerRNA> lb;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-19 14:25:10 +10:00
|
|
|
if (UI_context_copy_to_selected_list(C, &ptr, prop, &lb, &use_path_from_id, &path)) {
|
2024-03-27 00:47:39 +01:00
|
|
|
for (PointerRNA &link : lb) {
|
|
|
|
|
if (link.data == ptr.data) {
|
2023-05-19 14:25:10 +10:00
|
|
|
continue;
|
|
|
|
|
}
|
2021-08-03 17:12:03 +02:00
|
|
|
|
2024-01-31 17:08:09 +01:00
|
|
|
if (!UI_context_copy_to_selected_check(&ptr,
|
2024-03-27 00:47:39 +01:00
|
|
|
&link,
|
2024-01-31 17:08:09 +01:00
|
|
|
prop,
|
|
|
|
|
path.has_value() ? path->c_str() : nullptr,
|
|
|
|
|
use_path_from_id,
|
|
|
|
|
&lptr,
|
|
|
|
|
&lprop))
|
2023-05-19 14:25:10 +10:00
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2021-08-03 17:12:03 +02:00
|
|
|
|
2023-05-19 14:25:10 +10:00
|
|
|
if (poll) {
|
|
|
|
|
success = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (RNA_property_copy(bmain, &lptr, &ptr, prop, (all) ? -1 : index)) {
|
|
|
|
|
RNA_property_update(C, &lptr, prop);
|
|
|
|
|
success = true;
|
|
|
|
|
}
|
2021-08-03 17:12:03 +02:00
|
|
|
}
|
2009-12-17 17:15:38 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-12-17 17:15:38 +00:00
|
|
|
return success;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-02 11:47:00 +02:00
|
|
|
static bool copy_to_selected_button_poll(bContext *C)
|
2009-12-17 17:15:38 +00:00
|
|
|
{
|
2014-04-11 08:33:54 +10:00
|
|
|
return copy_to_selected_button(C, false, true);
|
|
|
|
|
}
|
2012-04-19 10:49:45 +00:00
|
|
|
|
2014-04-11 08:33:54 +10:00
|
|
|
static int copy_to_selected_button_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
|
|
|
|
bool success;
|
2012-04-19 10:49:45 +00:00
|
|
|
|
2014-04-11 08:33:54 +10:00
|
|
|
const bool all = RNA_boolean_get(op->ptr, "all");
|
2009-12-17 17:15:38 +00:00
|
|
|
|
2014-04-11 08:33:54 +10:00
|
|
|
success = copy_to_selected_button(C, all, false);
|
2012-04-10 15:49:41 +00:00
|
|
|
|
2012-03-30 01:51:25 +00:00
|
|
|
return (success) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
|
2009-12-17 17:15:38 +00:00
|
|
|
}
|
|
|
|
|
|
2011-02-13 14:16:36 +00:00
|
|
|
static void UI_OT_copy_to_selected_button(wmOperatorType *ot)
|
2009-12-17 17:15:38 +00:00
|
|
|
{
|
|
|
|
|
/* identifiers */
|
2019-09-20 14:31:24 +02:00
|
|
|
ot->name = "Copy to Selected";
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->idname = "UI_OT_copy_to_selected_button";
|
2022-06-08 23:26:15 -04:00
|
|
|
ot->description =
|
|
|
|
|
"Copy the property's value from the active item to the same property of all selected items "
|
|
|
|
|
"if the same property exists";
|
2009-12-17 17:15:38 +00:00
|
|
|
|
|
|
|
|
/* callbacks */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->poll = copy_to_selected_button_poll;
|
|
|
|
|
ot->exec = copy_to_selected_button_exec;
|
2009-12-17 17:15:38 +00:00
|
|
|
|
|
|
|
|
/* flags */
|
2012-03-30 01:51:25 +00:00
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
2009-12-17 17:15:38 +00:00
|
|
|
|
2009-12-17 10:47:55 +00:00
|
|
|
/* properties */
|
2016-04-15 15:17:18 +02:00
|
|
|
RNA_def_boolean(ot->srna, "all", true, "All", "Copy to selected all elements of the array");
|
2009-12-17 10:47:55 +00:00
|
|
|
}
|
2010-11-17 12:02:36 +00:00
|
|
|
|
2019-03-23 12:07:05 +11:00
|
|
|
/** \} */
|
2018-11-20 17:34:56 +11:00
|
|
|
|
2024-04-29 18:34:57 +02:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Copy Driver To Selected Operator
|
|
|
|
|
* \{ */
|
|
|
|
|
|
2024-05-02 16:44:10 +10:00
|
|
|
/* Name-spaced for unit testing. Conceptually these functions should be static
|
2024-04-29 18:34:57 +02:00
|
|
|
* and not be used outside this source file. But they need to be externally
|
|
|
|
|
* accessible to add unit tests for them. */
|
|
|
|
|
namespace blender::interface::internal {
|
|
|
|
|
|
|
|
|
|
blender::Vector<FCurve *> get_property_drivers(
|
|
|
|
|
PointerRNA *ptr, PropertyRNA *prop, const bool get_all, const int index, bool *r_is_array_prop)
|
|
|
|
|
{
|
|
|
|
|
BLI_assert(ptr && prop);
|
|
|
|
|
|
|
|
|
|
const std::optional<std::string> path = RNA_path_from_ID_to_property(ptr, prop);
|
|
|
|
|
if (!path.has_value()) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AnimData *adt = BKE_animdata_from_id(ptr->owner_id);
|
|
|
|
|
if (!adt) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
blender::Vector<FCurve *> drivers = {};
|
|
|
|
|
const bool is_array_prop = RNA_property_array_check(prop);
|
|
|
|
|
if (!is_array_prop) {
|
2024-05-03 11:32:43 +10:00
|
|
|
/* NOTE: by convention Blender assigns 0 as the index for drivers of
|
2024-04-29 18:34:57 +02:00
|
|
|
* non-array properties, which is why we search for it here. Values > 0 are
|
|
|
|
|
* not recognized by Blender's driver system in that case. Values < 0 are
|
|
|
|
|
* recognized for driver evaluation, but `BKE_fcurve_find()` unconditionally
|
|
|
|
|
* returns nullptr in that case so it wouldn't matter here anyway. */
|
|
|
|
|
drivers.append(BKE_fcurve_find(&adt->drivers, path->c_str(), 0));
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* For array properties, we always allocate space for all elements of an
|
|
|
|
|
* array property, and the unused ones just remain null. */
|
|
|
|
|
drivers.resize(RNA_property_array_length(ptr, prop), nullptr);
|
|
|
|
|
for (int i = 0; i < drivers.size(); i++) {
|
|
|
|
|
if (get_all || i == index) {
|
|
|
|
|
drivers[i] = BKE_fcurve_find(&adt->drivers, path->c_str(), i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If we didn't get any drivers to copy, instead of returning a vector of all
|
|
|
|
|
* nullptr, return an empty vector for clarity. That way the caller gets
|
|
|
|
|
* either a useful result or an empty one. */
|
|
|
|
|
bool fetched_at_least_one = false;
|
2024-05-21 13:13:09 +10:00
|
|
|
for (const FCurve *driver : drivers) {
|
2024-04-29 18:34:57 +02:00
|
|
|
fetched_at_least_one |= driver != nullptr;
|
|
|
|
|
}
|
|
|
|
|
if (!fetched_at_least_one) {
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (r_is_array_prop) {
|
|
|
|
|
*r_is_array_prop = is_array_prop;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return drivers;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int paste_property_drivers(blender::Span<FCurve *> src_drivers,
|
|
|
|
|
const bool is_array_prop,
|
|
|
|
|
PointerRNA *dst_ptr,
|
|
|
|
|
PropertyRNA *dst_prop)
|
|
|
|
|
{
|
|
|
|
|
BLI_assert(src_drivers.size() > 0);
|
|
|
|
|
BLI_assert(is_array_prop || src_drivers.size() == 1);
|
|
|
|
|
|
|
|
|
|
/* Get the RNA path and relevant animdata for the property we're copying to. */
|
|
|
|
|
const std::optional<std::string> dst_path = RNA_path_from_ID_to_property(dst_ptr, dst_prop);
|
|
|
|
|
if (!dst_path.has_value()) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
AnimData *dst_adt = BKE_animdata_ensure_id(dst_ptr->owner_id);
|
|
|
|
|
if (!dst_adt) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Do the copying. */
|
|
|
|
|
int paste_count = 0;
|
|
|
|
|
for (int i = 0; i < src_drivers.size(); i++) {
|
|
|
|
|
if (!src_drivers[i]) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
const int dst_index = is_array_prop ? i : -1;
|
|
|
|
|
|
|
|
|
|
/* If it's already animated by something other than a driver, skip. This is
|
|
|
|
|
* because Blender's UI assumes that properties are either animated *or*
|
|
|
|
|
* driven, and things can get confusing for users otherwise. Additionally,
|
|
|
|
|
* no other parts of Blender's UI allow users to (at least easily) add
|
|
|
|
|
* drivers on already-animated properties, so this keeps things consistent
|
|
|
|
|
* across driver-related operators. */
|
|
|
|
|
bool driven;
|
|
|
|
|
{
|
|
|
|
|
const FCurve *fcu = BKE_fcurve_find_by_rna(
|
|
|
|
|
dst_ptr, dst_prop, dst_index, nullptr, nullptr, &driven, nullptr);
|
|
|
|
|
if (fcu && !driven) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If there's an existing matching driver, remove it first.
|
|
|
|
|
*
|
|
|
|
|
* TODO: in the context of `copy_driver_to_selected_button()` this has
|
|
|
|
|
* quadratic complexity when the drivers are within the same ID, due to this
|
|
|
|
|
* being inside of a loop and doing a linear scan of the drivers to find one
|
|
|
|
|
* that matches. We should be able to make this more efficient with a
|
|
|
|
|
* little cleverness .*/
|
|
|
|
|
if (driven) {
|
|
|
|
|
FCurve *old_driver = BKE_fcurve_find(&dst_adt->drivers, dst_path->c_str(), dst_index);
|
|
|
|
|
if (old_driver) {
|
|
|
|
|
BLI_remlink(&dst_adt->drivers, old_driver);
|
|
|
|
|
BKE_fcurve_free(old_driver);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Create the new driver. */
|
|
|
|
|
FCurve *new_driver = BKE_fcurve_copy(src_drivers[i]);
|
|
|
|
|
BKE_fcurve_rnapath_set(*new_driver, dst_path.value());
|
|
|
|
|
BLI_addtail(&dst_adt->drivers, new_driver);
|
|
|
|
|
|
|
|
|
|
paste_count++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return paste_count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace blender::interface::internal
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Called from both exec & poll.
|
|
|
|
|
*
|
|
|
|
|
* \note We use this function for both poll and exec because the logic for
|
|
|
|
|
* whether there is a valid selection to copy to is baked into
|
|
|
|
|
* `UI_context_copy_to_selected_list()`, and the setup required to call that
|
|
|
|
|
* would either be duplicated or need to be split out into its own awkward
|
|
|
|
|
* difficult-to-name function with a large number of parameters. So instead we
|
|
|
|
|
* follow the same pattern as `copy_to_selected_button()` further above, with a
|
|
|
|
|
* bool to switch between exec and poll behavior. This isn't great, but seems
|
|
|
|
|
* like the lesser evil under the circumstances.
|
|
|
|
|
*
|
2024-06-06 09:43:48 +10:00
|
|
|
* \param copy_entire_array: If true, copies drivers of all elements of an array
|
2024-04-29 18:34:57 +02:00
|
|
|
* property. Otherwise only copies one specific element.
|
2024-06-06 09:43:48 +10:00
|
|
|
* \param poll: If true, only checks if the driver(s) could be copied rather than
|
2024-04-29 18:34:57 +02:00
|
|
|
* actually performing the copy.
|
|
|
|
|
*
|
|
|
|
|
* \returns true in exec mode if any copies were successfully made, and false
|
|
|
|
|
* otherwise. Returns true in poll mode if a copy could be successfully made,
|
|
|
|
|
* and false otherwise.
|
|
|
|
|
*/
|
|
|
|
|
static bool copy_driver_to_selected_button(bContext *C, bool copy_entire_array, const bool poll)
|
|
|
|
|
{
|
|
|
|
|
using namespace blender::interface::internal;
|
|
|
|
|
|
|
|
|
|
PropertyRNA *prop;
|
|
|
|
|
PointerRNA ptr;
|
|
|
|
|
int index;
|
|
|
|
|
|
|
|
|
|
/* Get the property of the clicked button. */
|
|
|
|
|
UI_context_active_but_prop_get(C, &ptr, &prop, &index);
|
|
|
|
|
if (!ptr.data || !ptr.owner_id || !prop) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
copy_entire_array |= index == -1; /* -1 implies `copy_entire_array` for array properties. */
|
|
|
|
|
|
|
|
|
|
/* Get the property's driver(s). */
|
|
|
|
|
bool is_array_prop = false;
|
|
|
|
|
const blender::Vector<FCurve *> src_drivers = get_property_drivers(
|
|
|
|
|
&ptr, prop, copy_entire_array, index, &is_array_prop);
|
|
|
|
|
if (src_drivers.is_empty()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Build the list of properties to copy the driver(s) to, along with relevant
|
|
|
|
|
* side data. */
|
|
|
|
|
std::optional<std::string> path;
|
|
|
|
|
bool use_path_from_id;
|
|
|
|
|
blender::Vector<PointerRNA> target_properties;
|
|
|
|
|
if (!UI_context_copy_to_selected_list(
|
|
|
|
|
C, &ptr, prop, &target_properties, &use_path_from_id, &path))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Copy the driver(s) to the list of target properties. */
|
|
|
|
|
int total_copy_count = 0;
|
|
|
|
|
for (PointerRNA &target_prop : target_properties) {
|
|
|
|
|
if (target_prop.data == ptr.data) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get the target property and ensure that it's appropriate for adding
|
|
|
|
|
* drivers. */
|
|
|
|
|
PropertyRNA *dst_prop;
|
|
|
|
|
PointerRNA dst_ptr;
|
|
|
|
|
if (!UI_context_copy_to_selected_check(&ptr,
|
|
|
|
|
&target_prop,
|
|
|
|
|
prop,
|
|
|
|
|
path.has_value() ? path->c_str() : nullptr,
|
|
|
|
|
use_path_from_id,
|
|
|
|
|
&dst_ptr,
|
|
|
|
|
&dst_prop))
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (!RNA_property_driver_editable(&dst_ptr, dst_prop)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If we're just polling, then we early-out on the first property we would
|
|
|
|
|
* be able to copy to. */
|
|
|
|
|
if (poll) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const int paste_count = paste_property_drivers(
|
|
|
|
|
src_drivers.as_span(), is_array_prop, &dst_ptr, dst_prop);
|
|
|
|
|
if (paste_count == 0) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RNA_property_update(C, &dst_ptr, dst_prop);
|
|
|
|
|
total_copy_count += paste_count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return total_copy_count > 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool copy_driver_to_selected_button_poll(bContext *C)
|
|
|
|
|
{
|
|
|
|
|
return copy_driver_to_selected_button(C, false, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int copy_driver_to_selected_button_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
|
|
|
|
const bool all = RNA_boolean_get(op->ptr, "all");
|
|
|
|
|
|
|
|
|
|
if (!copy_driver_to_selected_button(C, all, false)) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DEG_relations_tag_update(CTX_data_main(C));
|
|
|
|
|
WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME_PROP, nullptr);
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void UI_OT_copy_driver_to_selected_button(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* Identifiers. */
|
|
|
|
|
ot->name = "Copy Driver to Selected";
|
|
|
|
|
ot->idname = "UI_OT_copy_driver_to_selected_button";
|
|
|
|
|
ot->description =
|
|
|
|
|
"Copy the property's driver from the active item to the same property "
|
|
|
|
|
"of all selected items, if the same property exists";
|
|
|
|
|
|
|
|
|
|
/* Callbacks. */
|
|
|
|
|
ot->poll = copy_driver_to_selected_button_poll;
|
|
|
|
|
ot->exec = copy_driver_to_selected_button_exec;
|
|
|
|
|
|
|
|
|
|
/* Flags. */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
|
|
|
|
|
|
/* Properties. */
|
|
|
|
|
RNA_def_boolean(
|
|
|
|
|
ot->srna, "all", false, "All", "Copy to selected the drivers of all elements of the array");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
2018-11-20 17:34:56 +11:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Jump to Target Operator
|
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
|
|
/** Jump to the object or bone referenced by the pointer, or check if it is possible. */
|
|
|
|
|
static bool jump_to_target_ptr(bContext *C, PointerRNA ptr, const bool poll)
|
|
|
|
|
{
|
|
|
|
|
if (RNA_pointer_is_null(&ptr)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-11-20 17:34:56 +11:00
|
|
|
/* Verify pointer type. */
|
|
|
|
|
char bone_name[MAXBONENAME];
|
2022-07-29 23:16:58 -05:00
|
|
|
const StructRNA *target_type = nullptr;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-11-20 17:34:56 +11:00
|
|
|
if (ELEM(ptr.type, &RNA_EditBone, &RNA_PoseBone, &RNA_Bone)) {
|
|
|
|
|
RNA_string_get(&ptr, "name", bone_name);
|
|
|
|
|
if (bone_name[0] != '\0') {
|
|
|
|
|
target_type = &RNA_Bone;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (RNA_struct_is_a(ptr.type, &RNA_Object)) {
|
|
|
|
|
target_type = &RNA_Object;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-07-29 23:16:58 -05:00
|
|
|
if (target_type == nullptr) {
|
2018-11-20 17:34:56 +11:00
|
|
|
return false;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-11-20 17:34:56 +11:00
|
|
|
/* Find the containing Object. */
|
2022-09-14 21:30:20 +02:00
|
|
|
const Scene *scene = CTX_data_scene(C);
|
2018-11-20 17:34:56 +11:00
|
|
|
ViewLayer *view_layer = CTX_data_view_layer(C);
|
2022-07-29 23:16:58 -05:00
|
|
|
Base *base = nullptr;
|
2019-08-23 09:52:12 +02:00
|
|
|
const short id_type = GS(ptr.owner_id->name);
|
2018-11-20 17:34:56 +11:00
|
|
|
if (id_type == ID_OB) {
|
2022-09-14 21:33:51 +02:00
|
|
|
BKE_view_layer_synced_ensure(scene, view_layer);
|
2019-08-23 09:52:12 +02:00
|
|
|
base = BKE_view_layer_base_find(view_layer, (Object *)ptr.owner_id);
|
2018-11-20 17:34:56 +11:00
|
|
|
}
|
|
|
|
|
else if (OB_DATA_SUPPORT_ID(id_type)) {
|
2024-03-28 01:30:38 +01:00
|
|
|
base = blender::ed::object::find_first_by_data_id(scene, view_layer, ptr.owner_id);
|
2018-11-20 17:34:56 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-11-20 17:34:56 +11:00
|
|
|
bool ok = false;
|
2022-07-29 23:16:58 -05:00
|
|
|
if ((base == nullptr) || ((target_type == &RNA_Bone) && (base->object->type != OB_ARMATURE))) {
|
2018-11-20 17:34:56 +11:00
|
|
|
/* pass */
|
|
|
|
|
}
|
|
|
|
|
else if (poll) {
|
|
|
|
|
ok = true;
|
|
|
|
|
}
|
|
|
|
|
else {
|
2018-11-21 13:04:03 +11:00
|
|
|
/* Make optional. */
|
|
|
|
|
const bool reveal_hidden = true;
|
2018-11-20 17:34:56 +11:00
|
|
|
/* Select and activate the target. */
|
|
|
|
|
if (target_type == &RNA_Bone) {
|
2024-03-28 01:30:38 +01:00
|
|
|
ok = blender::ed::object::jump_to_bone(C, base->object, bone_name, reveal_hidden);
|
2018-11-20 17:34:56 +11:00
|
|
|
}
|
|
|
|
|
else if (target_type == &RNA_Object) {
|
2024-03-28 01:30:38 +01:00
|
|
|
ok = blender::ed::object::jump_to_object(C, base->object, reveal_hidden);
|
2018-11-20 17:34:56 +11:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
BLI_assert(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return ok;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Jump to the object or bone referred to by the current UI field value.
|
|
|
|
|
*
|
|
|
|
|
* \note quite heavy for a poll callback, but the operator is only
|
|
|
|
|
* used as a right click menu item for certain UI field types, and
|
|
|
|
|
* this will fail quickly if the context is completely unsuitable.
|
|
|
|
|
*/
|
|
|
|
|
static bool jump_to_target_button(bContext *C, bool poll)
|
|
|
|
|
{
|
|
|
|
|
PointerRNA ptr, target_ptr;
|
|
|
|
|
PropertyRNA *prop;
|
|
|
|
|
int index;
|
|
|
|
|
|
2023-11-28 15:55:03 +01:00
|
|
|
const uiBut *but = UI_context_active_but_prop_get(C, &ptr, &prop, &index);
|
2018-11-20 17:34:56 +11:00
|
|
|
|
|
|
|
|
/* If there is a valid property... */
|
|
|
|
|
if (ptr.data && prop) {
|
|
|
|
|
const PropertyType type = RNA_property_type(prop);
|
|
|
|
|
|
|
|
|
|
/* For pointer properties, use their value directly. */
|
|
|
|
|
if (type == PROP_POINTER) {
|
|
|
|
|
target_ptr = RNA_property_pointer_get(&ptr, prop);
|
|
|
|
|
|
|
|
|
|
return jump_to_target_ptr(C, target_ptr, poll);
|
|
|
|
|
}
|
|
|
|
|
/* For string properties with prop_search, look up the search collection item. */
|
2020-07-03 14:20:10 +02:00
|
|
|
if (type == PROP_STRING) {
|
2020-08-07 14:34:11 +02:00
|
|
|
const uiButSearch *search_but = (but->type == UI_BTYPE_SEARCH_MENU) ? (uiButSearch *)but :
|
2022-07-29 23:16:58 -05:00
|
|
|
nullptr;
|
2018-11-20 17:34:56 +11:00
|
|
|
|
2020-08-07 14:34:11 +02:00
|
|
|
if (search_but && search_but->items_update_fn == ui_rna_collection_search_update_fn) {
|
2022-07-29 23:16:58 -05:00
|
|
|
uiRNACollectionSearch *coll_search = static_cast<uiRNACollectionSearch *>(search_but->arg);
|
2018-11-20 17:34:56 +11:00
|
|
|
|
|
|
|
|
char str_buf[MAXBONENAME];
|
2022-07-29 23:16:58 -05:00
|
|
|
char *str_ptr = RNA_property_string_get_alloc(
|
|
|
|
|
&ptr, prop, str_buf, sizeof(str_buf), nullptr);
|
2018-11-20 17:34:56 +11:00
|
|
|
|
2024-04-15 21:53:27 +10:00
|
|
|
bool found = false;
|
2022-09-01 10:52:15 +02:00
|
|
|
/* Jump to target only works with search properties currently, not search callbacks yet.
|
|
|
|
|
* See ui_but_add_search. */
|
2022-10-05 13:44:02 -05:00
|
|
|
if (coll_search->search_prop != nullptr) {
|
2022-09-01 10:52:15 +02:00
|
|
|
found = RNA_property_collection_lookup_string(
|
|
|
|
|
&coll_search->search_ptr, coll_search->search_prop, str_ptr, &target_ptr);
|
|
|
|
|
}
|
2018-11-20 17:34:56 +11:00
|
|
|
|
|
|
|
|
if (str_ptr != str_buf) {
|
|
|
|
|
MEM_freeN(str_ptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (found) {
|
|
|
|
|
return jump_to_target_ptr(C, target_ptr, poll);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-15 10:49:26 -03:00
|
|
|
bool ui_jump_to_target_button_poll(bContext *C)
|
2018-11-20 17:34:56 +11:00
|
|
|
{
|
|
|
|
|
return jump_to_target_button(C, true);
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int jump_to_target_button_exec(bContext *C, wmOperator * /*op*/)
|
2018-11-20 17:34:56 +11:00
|
|
|
{
|
2020-08-26 10:11:13 +10:00
|
|
|
const bool success = jump_to_target_button(C, false);
|
2018-11-20 17:34:56 +11:00
|
|
|
|
|
|
|
|
return (success) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void UI_OT_jump_to_target_button(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
2019-09-20 14:31:24 +02:00
|
|
|
ot->name = "Jump to Target";
|
2018-11-20 17:34:56 +11:00
|
|
|
ot->idname = "UI_OT_jump_to_target_button";
|
|
|
|
|
ot->description = "Switch to the target object or bone";
|
|
|
|
|
|
|
|
|
|
/* callbacks */
|
2019-03-15 10:49:26 -03:00
|
|
|
ot->poll = ui_jump_to_target_button_poll;
|
2018-11-20 17:34:56 +11:00
|
|
|
ot->exec = jump_to_target_button_exec;
|
|
|
|
|
|
|
|
|
|
/* flags */
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
2019-03-23 12:07:05 +11:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Edit Python Source Operator
|
|
|
|
|
* \{ */
|
|
|
|
|
|
2011-11-03 23:20:54 +00:00
|
|
|
#ifdef WITH_PYTHON
|
2011-10-23 04:13:56 +00:00
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------- */
|
2022-09-19 14:47:27 +10:00
|
|
|
/* EditSource Utility functions and operator,
|
2021-07-03 23:08:40 +10:00
|
|
|
* NOTE: this includes utility functions and button matching checks. */
|
2011-10-23 04:13:56 +00:00
|
|
|
|
2022-07-29 23:16:58 -05:00
|
|
|
struct uiEditSourceStore {
|
2011-10-23 04:13:56 +00:00
|
|
|
uiBut but_orig;
|
|
|
|
|
GHash *hash;
|
2022-07-29 23:16:58 -05:00
|
|
|
};
|
2011-10-23 04:13:56 +00:00
|
|
|
|
2022-07-29 23:16:58 -05:00
|
|
|
struct uiEditSourceButStore {
|
2012-01-16 05:52:33 +00:00
|
|
|
char py_dbg_fn[FILE_MAX];
|
2020-12-10 14:25:20 +11:00
|
|
|
int py_dbg_line_number;
|
2022-07-29 23:16:58 -05:00
|
|
|
};
|
2011-10-23 04:13:56 +00:00
|
|
|
|
|
|
|
|
/* should only ever be set while the edit source operator is running */
|
2022-07-29 23:16:58 -05:00
|
|
|
static uiEditSourceStore *ui_editsource_info = nullptr;
|
2011-10-23 04:13:56 +00:00
|
|
|
|
2023-07-02 19:37:22 +10:00
|
|
|
bool UI_editsource_enable_check()
|
2011-10-23 04:13:56 +00:00
|
|
|
{
|
2022-07-29 23:16:58 -05:00
|
|
|
return (ui_editsource_info != nullptr);
|
2011-10-23 04:13:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void ui_editsource_active_but_set(uiBut *but)
|
|
|
|
|
{
|
2022-07-29 23:16:58 -05:00
|
|
|
BLI_assert(ui_editsource_info == nullptr);
|
2011-10-23 04:13:56 +00:00
|
|
|
|
2023-02-03 16:12:14 +01:00
|
|
|
ui_editsource_info = MEM_new<uiEditSourceStore>(__func__);
|
|
|
|
|
ui_editsource_info->but_orig = *but;
|
2011-10-23 04:13:56 +00:00
|
|
|
|
2012-05-16 00:51:36 +00:00
|
|
|
ui_editsource_info->hash = BLI_ghash_ptr_new(__func__);
|
2011-10-23 04:13:56 +00:00
|
|
|
}
|
|
|
|
|
|
2022-07-29 23:16:58 -05:00
|
|
|
static void ui_editsource_active_but_clear()
|
2011-10-23 04:13:56 +00:00
|
|
|
{
|
2022-07-29 23:16:58 -05:00
|
|
|
BLI_ghash_free(ui_editsource_info->hash, nullptr, MEM_freeN);
|
2011-10-23 04:13:56 +00:00
|
|
|
MEM_freeN(ui_editsource_info);
|
2022-07-29 23:16:58 -05:00
|
|
|
ui_editsource_info = nullptr;
|
2011-10-23 04:13:56 +00:00
|
|
|
}
|
|
|
|
|
|
2014-01-04 17:16:19 +11:00
|
|
|
static bool ui_editsource_uibut_match(uiBut *but_a, uiBut *but_b)
|
2011-10-23 04:13:56 +00:00
|
|
|
{
|
|
|
|
|
# if 0
|
2019-04-17 08:50:46 +02:00
|
|
|
printf("matching buttons: '%s' == '%s'\n", but_a->drawstr, but_b->drawstr);
|
2011-10-23 04:13:56 +00:00
|
|
|
# endif
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2011-10-23 04:13:56 +00:00
|
|
|
/* this just needs to be a 'good-enough' comparison so we can know beyond
|
|
|
|
|
* reasonable doubt that these buttons are the same between redraws.
|
|
|
|
|
* if this fails it only means edit-source fails - campbell */
|
2012-08-18 18:11:51 +00:00
|
|
|
if (BLI_rctf_compare(&but_a->rect, &but_b->rect, FLT_EPSILON) && (but_a->type == but_b->type) &&
|
2012-03-30 01:51:25 +00:00
|
|
|
(but_a->rnaprop == but_b->rnaprop) && (but_a->optype == but_b->optype) &&
|
2024-01-22 14:54:44 -05:00
|
|
|
(but_a->unit_type == but_b->unit_type) && but_a->drawstr == but_b->drawstr)
|
2015-01-26 16:03:11 +01:00
|
|
|
{
|
2014-01-04 17:16:19 +11:00
|
|
|
return true;
|
2011-10-23 04:13:56 +00:00
|
|
|
}
|
2020-07-03 14:20:10 +02:00
|
|
|
return false;
|
2011-10-23 04:13:56 +00:00
|
|
|
}
|
|
|
|
|
|
2022-07-29 23:16:58 -05:00
|
|
|
extern "C" {
|
|
|
|
|
void PyC_FileAndNum_Safe(const char **r_filename, int *r_lineno);
|
|
|
|
|
}
|
|
|
|
|
|
2011-10-23 04:13:56 +00:00
|
|
|
void UI_editsource_active_but_test(uiBut *but)
|
|
|
|
|
{
|
|
|
|
|
|
2022-07-29 23:16:58 -05:00
|
|
|
uiEditSourceButStore *but_store = MEM_cnew<uiEditSourceButStore>(__func__);
|
2011-10-23 04:13:56 +00:00
|
|
|
|
|
|
|
|
const char *fn;
|
2020-12-10 14:25:20 +11:00
|
|
|
int line_number = -1;
|
2011-10-23 04:13:56 +00:00
|
|
|
|
|
|
|
|
# if 0
|
2019-04-17 08:50:46 +02:00
|
|
|
printf("comparing buttons: '%s' == '%s'\n", but->drawstr, ui_editsource_info->but_orig.drawstr);
|
2011-10-23 04:13:56 +00:00
|
|
|
# endif
|
|
|
|
|
|
2020-12-10 14:25:20 +11:00
|
|
|
PyC_FileAndNum_Safe(&fn, &line_number);
|
2011-10-23 04:13:56 +00:00
|
|
|
|
2020-12-10 14:25:20 +11:00
|
|
|
if (line_number != -1) {
|
2023-05-09 12:50:37 +10:00
|
|
|
STRNCPY(but_store->py_dbg_fn, fn);
|
2020-12-10 14:25:20 +11:00
|
|
|
but_store->py_dbg_line_number = line_number;
|
2011-10-23 04:13:56 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2012-03-30 01:51:25 +00:00
|
|
|
but_store->py_dbg_fn[0] = '\0';
|
2020-12-10 14:25:20 +11:00
|
|
|
but_store->py_dbg_line_number = -1;
|
2011-10-23 04:13:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BLI_ghash_insert(ui_editsource_info->hash, but, but_store);
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-18 16:33:00 +01:00
|
|
|
void UI_editsource_but_replace(const uiBut *old_but, uiBut *new_but)
|
|
|
|
|
{
|
2022-07-29 23:16:58 -05:00
|
|
|
uiEditSourceButStore *but_store = static_cast<uiEditSourceButStore *>(
|
|
|
|
|
BLI_ghash_lookup(ui_editsource_info->hash, old_but));
|
2021-02-18 16:33:00 +01:00
|
|
|
if (but_store) {
|
2022-07-29 23:16:58 -05:00
|
|
|
BLI_ghash_remove(ui_editsource_info->hash, old_but, nullptr, nullptr);
|
2021-02-18 16:33:00 +01:00
|
|
|
BLI_ghash_insert(ui_editsource_info->hash, new_but, but_store);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-05 03:13:47 +10:00
|
|
|
static int editsource_text_edit(bContext *C,
|
2023-06-07 15:13:32 +10:00
|
|
|
wmOperator * /*op*/,
|
2018-07-03 07:22:26 +02:00
|
|
|
const char filepath[FILE_MAX],
|
|
|
|
|
const int line)
|
2011-10-23 04:13:56 +00:00
|
|
|
{
|
2023-06-07 15:13:32 +10:00
|
|
|
wmOperatorType *ot = WM_operatortype_find("TEXT_OT_jump_to_file_at_point", true);
|
|
|
|
|
PointerRNA op_props;
|
2021-04-08 21:56:06 +10:00
|
|
|
|
2023-06-07 15:13:32 +10:00
|
|
|
WM_operator_properties_create_ptr(&op_props, ot);
|
|
|
|
|
RNA_string_set(&op_props, "filepath", filepath);
|
|
|
|
|
RNA_int_set(&op_props, "line", line - 1);
|
|
|
|
|
RNA_int_set(&op_props, "column", 0);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-06-10 16:30:58 +10:00
|
|
|
int result = WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &op_props, nullptr);
|
2023-06-07 15:13:32 +10:00
|
|
|
WM_operator_properties_free(&op_props);
|
|
|
|
|
return result;
|
2011-10-23 04:13:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int editsource_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2014-11-09 21:20:40 +01:00
|
|
|
uiBut *but = UI_context_active_but_get(C);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2011-10-23 04:13:56 +00:00
|
|
|
if (but) {
|
|
|
|
|
GHashIterator ghi;
|
2022-07-29 23:16:58 -05:00
|
|
|
uiEditSourceButStore *but_store = nullptr;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-03-06 16:56:42 +01:00
|
|
|
ARegion *region = CTX_wm_region(C);
|
2011-10-23 04:13:56 +00:00
|
|
|
int ret;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2011-10-23 05:08:02 +00:00
|
|
|
/* needed else the active button does not get tested */
|
2021-12-02 15:46:14 +11:00
|
|
|
UI_screen_free_active_but_highlight(C, CTX_wm_screen(C));
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2011-10-23 04:13:56 +00:00
|
|
|
// printf("%s: begin\n", __func__);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2011-10-23 05:08:02 +00:00
|
|
|
/* take care not to return before calling ui_editsource_active_but_clear */
|
2011-10-23 04:13:56 +00:00
|
|
|
ui_editsource_active_but_set(but);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2011-10-23 04:13:56 +00:00
|
|
|
/* redraw and get active button python info */
|
2021-07-13 15:44:44 +02:00
|
|
|
ui_region_redraw_immediately(C, region);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-24 06:38:07 +00:00
|
|
|
for (BLI_ghashIterator_init(&ghi, ui_editsource_info->hash);
|
2013-05-08 12:58:11 +00:00
|
|
|
BLI_ghashIterator_done(&ghi) == false;
|
2012-03-30 01:51:25 +00:00
|
|
|
BLI_ghashIterator_step(&ghi))
|
|
|
|
|
{
|
2022-07-29 23:16:58 -05:00
|
|
|
uiBut *but_key = static_cast<uiBut *>(BLI_ghashIterator_getKey(&ghi));
|
2012-10-12 14:35:10 +00:00
|
|
|
if (but_key && ui_editsource_uibut_match(&ui_editsource_info->but_orig, but_key)) {
|
2022-07-29 23:16:58 -05:00
|
|
|
but_store = static_cast<uiEditSourceButStore *>(BLI_ghashIterator_getValue(&ghi));
|
2011-10-23 04:13:56 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
|
2011-10-23 04:13:56 +00:00
|
|
|
if (but_store) {
|
2020-12-10 14:25:20 +11:00
|
|
|
if (but_store->py_dbg_line_number != -1) {
|
|
|
|
|
ret = editsource_text_edit(C, op, but_store->py_dbg_fn, but_store->py_dbg_line_number);
|
2011-10-23 04:13:56 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2012-10-26 17:32:50 +00:00
|
|
|
BKE_report(
|
|
|
|
|
op->reports, RPT_ERROR, "Active button is not from a script, cannot edit source");
|
2012-03-30 01:51:25 +00:00
|
|
|
ret = OPERATOR_CANCELLED;
|
2011-10-23 04:13:56 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
2012-10-26 17:32:50 +00:00
|
|
|
BKE_report(op->reports, RPT_ERROR, "Active button match cannot be found");
|
2012-03-30 01:51:25 +00:00
|
|
|
ret = OPERATOR_CANCELLED;
|
2011-10-23 04:13:56 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2011-10-23 04:13:56 +00:00
|
|
|
ui_editsource_active_but_clear();
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2011-10-23 04:13:56 +00:00
|
|
|
// printf("%s: end\n", __func__);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2011-10-23 04:13:56 +00:00
|
|
|
return ret;
|
|
|
|
|
}
|
2020-07-03 14:20:10 +02:00
|
|
|
|
|
|
|
|
BKE_report(op->reports, RPT_ERROR, "Active button not found");
|
|
|
|
|
return OPERATOR_CANCELLED;
|
2011-10-23 04:13:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void UI_OT_editsource(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
UI translation from inside Blender UI: first part.
This commit reshapes a bit runtime button info getter, by adding a new uiButGetStrInfo() which accepts a variable number of uiStringInfo parameters, and tries to fill them with the requested strings, for the given button (label, tip, context, RNA identifier, keymap, etc.). Currently used mostly by existing ui_tooltip_create(), and new UI_OT_edittranslation_init operator.
It also adds a few getters (to get RNA i18n context, and current language iso code).
Finally, it adds to C operators needed for the py ui_translation addon:
*UI_OT_edittranslation_init, which gathers requested data and launch the py operator.
*UI_OT_reloadtranslation, which forces a full reload of the whole UI translation (including rechecking the directory containing mo files).
For the first operator to work, it also adds a new user preferences path: i18n_branches_directory, to point to the /branch part of a bf-translation checkout.
2012-07-09 14:25:35 +00:00
|
|
|
ot->name = "Edit Source";
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->idname = "UI_OT_editsource";
|
UI translation from inside Blender UI: first part.
This commit reshapes a bit runtime button info getter, by adding a new uiButGetStrInfo() which accepts a variable number of uiStringInfo parameters, and tries to fill them with the requested strings, for the given button (label, tip, context, RNA identifier, keymap, etc.). Currently used mostly by existing ui_tooltip_create(), and new UI_OT_edittranslation_init operator.
It also adds a few getters (to get RNA i18n context, and current language iso code).
Finally, it adds to C operators needed for the py ui_translation addon:
*UI_OT_edittranslation_init, which gathers requested data and launch the py operator.
*UI_OT_reloadtranslation, which forces a full reload of the whole UI translation (including rechecking the directory containing mo files).
For the first operator to work, it also adds a new user preferences path: i18n_branches_directory, to point to the /branch part of a bf-translation checkout.
2012-07-09 14:25:35 +00:00
|
|
|
ot->description = "Edit UI source code of the active button";
|
2011-10-23 04:13:56 +00:00
|
|
|
|
|
|
|
|
/* callbacks */
|
2012-03-22 07:26:09 +00:00
|
|
|
ot->exec = editsource_exec;
|
2011-10-23 04:13:56 +00:00
|
|
|
}
|
|
|
|
|
|
2019-03-23 12:07:05 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Edit Translation Operator
|
|
|
|
|
* \{ */
|
UI translation from inside Blender UI: first part.
This commit reshapes a bit runtime button info getter, by adding a new uiButGetStrInfo() which accepts a variable number of uiStringInfo parameters, and tries to fill them with the requested strings, for the given button (label, tip, context, RNA identifier, keymap, etc.). Currently used mostly by existing ui_tooltip_create(), and new UI_OT_edittranslation_init operator.
It also adds a few getters (to get RNA i18n context, and current language iso code).
Finally, it adds to C operators needed for the py ui_translation addon:
*UI_OT_edittranslation_init, which gathers requested data and launch the py operator.
*UI_OT_reloadtranslation, which forces a full reload of the whole UI translation (including rechecking the directory containing mo files).
For the first operator to work, it also adds a new user preferences path: i18n_branches_directory, to point to the /branch part of a bf-translation checkout.
2012-07-09 14:25:35 +00:00
|
|
|
|
2015-05-31 14:20:03 +10:00
|
|
|
/**
|
2022-09-19 14:47:27 +10:00
|
|
|
* EditTranslation utility functions and operator.
|
2019-11-25 01:14:39 +11:00
|
|
|
*
|
|
|
|
|
* \note this includes utility functions and button matching checks.
|
|
|
|
|
* this only works in conjunction with a Python operator!
|
2015-05-31 14:20:03 +10:00
|
|
|
*/
|
2012-09-15 01:52:28 +00:00
|
|
|
static void edittranslation_find_po_file(const char *root,
|
|
|
|
|
const char *uilng,
|
|
|
|
|
char *path,
|
2023-05-07 15:22:58 +10:00
|
|
|
const size_t path_maxncpy)
|
UI translation from inside Blender UI: first part.
This commit reshapes a bit runtime button info getter, by adding a new uiButGetStrInfo() which accepts a variable number of uiStringInfo parameters, and tries to fill them with the requested strings, for the given button (label, tip, context, RNA identifier, keymap, etc.). Currently used mostly by existing ui_tooltip_create(), and new UI_OT_edittranslation_init operator.
It also adds a few getters (to get RNA i18n context, and current language iso code).
Finally, it adds to C operators needed for the py ui_translation addon:
*UI_OT_edittranslation_init, which gathers requested data and launch the py operator.
*UI_OT_reloadtranslation, which forces a full reload of the whole UI translation (including rechecking the directory containing mo files).
For the first operator to work, it also adds a new user preferences path: i18n_branches_directory, to point to the /branch part of a bf-translation checkout.
2012-07-09 14:25:35 +00:00
|
|
|
{
|
2012-09-05 01:42:52 +00:00
|
|
|
char tstr[32]; /* Should be more than enough! */
|
2019-04-17 06:17:24 +02:00
|
|
|
|
UI translation from inside Blender UI: first part.
This commit reshapes a bit runtime button info getter, by adding a new uiButGetStrInfo() which accepts a variable number of uiStringInfo parameters, and tries to fill them with the requested strings, for the given button (label, tip, context, RNA identifier, keymap, etc.). Currently used mostly by existing ui_tooltip_create(), and new UI_OT_edittranslation_init operator.
It also adds a few getters (to get RNA i18n context, and current language iso code).
Finally, it adds to C operators needed for the py ui_translation addon:
*UI_OT_edittranslation_init, which gathers requested data and launch the py operator.
*UI_OT_reloadtranslation, which forces a full reload of the whole UI translation (including rechecking the directory containing mo files).
For the first operator to work, it also adds a new user preferences path: i18n_branches_directory, to point to the /branch part of a bf-translation checkout.
2012-07-09 14:25:35 +00:00
|
|
|
/* First, full lang code. */
|
2023-05-09 12:50:37 +10:00
|
|
|
SNPRINTF(tstr, "%s.po", uilng);
|
2023-05-07 15:22:58 +10:00
|
|
|
BLI_path_join(path, path_maxncpy, root, uilng, tstr);
|
2019-03-25 10:15:20 +11:00
|
|
|
if (BLI_is_file(path)) {
|
UI translation from inside Blender UI: first part.
This commit reshapes a bit runtime button info getter, by adding a new uiButGetStrInfo() which accepts a variable number of uiStringInfo parameters, and tries to fill them with the requested strings, for the given button (label, tip, context, RNA identifier, keymap, etc.). Currently used mostly by existing ui_tooltip_create(), and new UI_OT_edittranslation_init operator.
It also adds a few getters (to get RNA i18n context, and current language iso code).
Finally, it adds to C operators needed for the py ui_translation addon:
*UI_OT_edittranslation_init, which gathers requested data and launch the py operator.
*UI_OT_reloadtranslation, which forces a full reload of the whole UI translation (including rechecking the directory containing mo files).
For the first operator to work, it also adds a new user preferences path: i18n_branches_directory, to point to the /branch part of a bf-translation checkout.
2012-07-09 14:25:35 +00:00
|
|
|
return;
|
2019-03-25 10:15:20 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-09-15 12:55:33 +02:00
|
|
|
/* Now try without the second ISO code part (`_BR` in `pt_BR`). */
|
2012-09-27 14:38:20 +00:00
|
|
|
{
|
2022-07-29 23:16:58 -05:00
|
|
|
const char *tc = nullptr;
|
2012-09-27 14:38:20 +00:00
|
|
|
size_t szt = 0;
|
|
|
|
|
tstr[0] = '\0';
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-09-27 14:38:20 +00:00
|
|
|
tc = strchr(uilng, '_');
|
|
|
|
|
if (tc) {
|
|
|
|
|
szt = tc - uilng;
|
2019-03-26 21:16:13 +11:00
|
|
|
if (szt < sizeof(tstr)) { /* Paranoid, should always be true! */
|
2012-09-27 14:38:20 +00:00
|
|
|
BLI_strncpy(tstr, uilng, szt + 1); /* +1 for '\0' char! */
|
2019-03-26 21:16:13 +11:00
|
|
|
}
|
2012-09-27 14:38:20 +00:00
|
|
|
}
|
|
|
|
|
if (tstr[0]) {
|
|
|
|
|
/* Because of some codes like sr_SR@latin... */
|
|
|
|
|
tc = strchr(uilng, '@');
|
2019-03-25 10:15:20 +11:00
|
|
|
if (tc) {
|
2012-09-27 14:38:20 +00:00
|
|
|
BLI_strncpy(tstr + szt, tc, sizeof(tstr) - szt);
|
2019-03-25 10:15:20 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-07 15:22:58 +10:00
|
|
|
BLI_path_join(path, path_maxncpy, root, tstr);
|
2023-05-02 16:47:57 +10:00
|
|
|
BLI_strncat(tstr, ".po", sizeof(tstr));
|
2023-05-07 15:22:58 +10:00
|
|
|
BLI_path_append(path, path_maxncpy, tstr);
|
2019-03-25 10:15:20 +11:00
|
|
|
if (BLI_is_file(path)) {
|
2012-09-27 14:38:20 +00:00
|
|
|
return;
|
2019-03-25 10:15:20 +11:00
|
|
|
}
|
2012-09-27 14:38:20 +00:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-09-27 14:38:20 +00:00
|
|
|
/* Else no po file! */
|
UI translation from inside Blender UI: first part.
This commit reshapes a bit runtime button info getter, by adding a new uiButGetStrInfo() which accepts a variable number of uiStringInfo parameters, and tries to fill them with the requested strings, for the given button (label, tip, context, RNA identifier, keymap, etc.). Currently used mostly by existing ui_tooltip_create(), and new UI_OT_edittranslation_init operator.
It also adds a few getters (to get RNA i18n context, and current language iso code).
Finally, it adds to C operators needed for the py ui_translation addon:
*UI_OT_edittranslation_init, which gathers requested data and launch the py operator.
*UI_OT_reloadtranslation, which forces a full reload of the whole UI translation (including rechecking the directory containing mo files).
For the first operator to work, it also adds a new user preferences path: i18n_branches_directory, to point to the /branch part of a bf-translation checkout.
2012-07-09 14:25:35 +00:00
|
|
|
path[0] = '\0';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int edittranslation_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
2014-11-09 21:20:40 +01:00
|
|
|
uiBut *but = UI_context_active_but_get(C);
|
2022-07-29 23:16:58 -05:00
|
|
|
if (but == nullptr) {
|
2021-04-30 15:26:17 +10:00
|
|
|
BKE_report(op->reports, RPT_ERROR, "Active button not found");
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-04-30 15:26:17 +10:00
|
|
|
wmOperatorType *ot;
|
|
|
|
|
PointerRNA ptr;
|
|
|
|
|
char popath[FILE_MAX];
|
|
|
|
|
const char *root = U.i18ndir;
|
|
|
|
|
const char *uilng = BLT_lang_get();
|
|
|
|
|
|
|
|
|
|
if (!BLI_is_dir(root)) {
|
|
|
|
|
BKE_report(op->reports,
|
|
|
|
|
RPT_ERROR,
|
|
|
|
|
"Please set your Preferences' 'Translation Branches "
|
|
|
|
|
"Directory' path to a valid directory");
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
2022-07-29 23:16:58 -05:00
|
|
|
ot = WM_operatortype_find(EDTSRC_I18N_OP_NAME, false);
|
|
|
|
|
if (ot == nullptr) {
|
2021-04-30 15:26:17 +10:00
|
|
|
BKE_reportf(op->reports,
|
|
|
|
|
RPT_ERROR,
|
|
|
|
|
"Could not find operator '%s'! Please enable ui_translate add-on "
|
|
|
|
|
"in the User Preferences",
|
|
|
|
|
EDTSRC_I18N_OP_NAME);
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
/* Try to find a valid po file for current language... */
|
|
|
|
|
edittranslation_find_po_file(root, uilng, popath, FILE_MAX);
|
2021-08-03 15:27:20 +10:00
|
|
|
// printf("po path: %s\n", popath);
|
2021-04-30 15:26:17 +10:00
|
|
|
if (popath[0] == '\0') {
|
|
|
|
|
BKE_reportf(
|
|
|
|
|
op->reports, RPT_ERROR, "No valid po found for language '%s' under %s", uilng, root);
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-04-30 15:26:17 +10:00
|
|
|
WM_operator_properties_create_ptr(&ptr, ot);
|
|
|
|
|
RNA_string_set(&ptr, "lang", uilng);
|
|
|
|
|
RNA_string_set(&ptr, "po_file", popath);
|
|
|
|
|
|
2024-01-18 23:12:09 -05:00
|
|
|
const EnumPropertyItem enum_item = UI_but_rna_enum_item_get(*C, *but).value_or(
|
|
|
|
|
EnumPropertyItem{});
|
|
|
|
|
RNA_string_set(&ptr, "enum_label", enum_item.name);
|
|
|
|
|
RNA_string_set(&ptr, "enum_tip", enum_item.description);
|
|
|
|
|
RNA_string_set(&ptr, "rna_enum", enum_item.identifier);
|
|
|
|
|
|
|
|
|
|
RNA_string_set(&ptr, "but_label", UI_but_string_get_label(*but).c_str());
|
|
|
|
|
RNA_string_set(&ptr, "rna_label", UI_but_string_get_rna_label(*but).c_str());
|
|
|
|
|
|
|
|
|
|
RNA_string_set(&ptr, "but_tip", UI_but_string_get_tooltip(*C, *but).c_str());
|
|
|
|
|
RNA_string_set(&ptr, "rna_tip", UI_but_string_get_rna_tooltip(*C, *but).c_str());
|
|
|
|
|
|
|
|
|
|
RNA_string_set(&ptr, "rna_struct", UI_but_string_get_rna_struct_identifier(*but).c_str());
|
|
|
|
|
RNA_string_set(&ptr, "rna_prop", UI_but_string_get_rna_property_identifier(*but).c_str());
|
|
|
|
|
RNA_string_set(&ptr, "rna_ctxt", UI_but_string_get_rna_label_context(*but).c_str());
|
|
|
|
|
|
|
|
|
|
const int ret = WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr, nullptr);
|
2020-07-03 14:20:10 +02:00
|
|
|
|
2021-04-30 15:26:17 +10:00
|
|
|
return ret;
|
UI translation from inside Blender UI: first part.
This commit reshapes a bit runtime button info getter, by adding a new uiButGetStrInfo() which accepts a variable number of uiStringInfo parameters, and tries to fill them with the requested strings, for the given button (label, tip, context, RNA identifier, keymap, etc.). Currently used mostly by existing ui_tooltip_create(), and new UI_OT_edittranslation_init operator.
It also adds a few getters (to get RNA i18n context, and current language iso code).
Finally, it adds to C operators needed for the py ui_translation addon:
*UI_OT_edittranslation_init, which gathers requested data and launch the py operator.
*UI_OT_reloadtranslation, which forces a full reload of the whole UI translation (including rechecking the directory containing mo files).
For the first operator to work, it also adds a new user preferences path: i18n_branches_directory, to point to the /branch part of a bf-translation checkout.
2012-07-09 14:25:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void UI_OT_edittranslation_init(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Edit Translation";
|
|
|
|
|
ot->idname = "UI_OT_edittranslation_init";
|
|
|
|
|
ot->description = "Edit i18n in current language for the active button";
|
|
|
|
|
|
|
|
|
|
/* callbacks */
|
|
|
|
|
ot->exec = edittranslation_exec;
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-03 23:20:54 +00:00
|
|
|
#endif /* WITH_PYTHON */
|
2011-10-23 04:13:56 +00:00
|
|
|
|
2019-03-23 12:07:05 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Reload Translation Operator
|
|
|
|
|
* \{ */
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int reloadtranslation_exec(bContext * /*C*/, wmOperator * /*op*/)
|
UI translation from inside Blender UI: first part.
This commit reshapes a bit runtime button info getter, by adding a new uiButGetStrInfo() which accepts a variable number of uiStringInfo parameters, and tries to fill them with the requested strings, for the given button (label, tip, context, RNA identifier, keymap, etc.). Currently used mostly by existing ui_tooltip_create(), and new UI_OT_edittranslation_init operator.
It also adds a few getters (to get RNA i18n context, and current language iso code).
Finally, it adds to C operators needed for the py ui_translation addon:
*UI_OT_edittranslation_init, which gathers requested data and launch the py operator.
*UI_OT_reloadtranslation, which forces a full reload of the whole UI translation (including rechecking the directory containing mo files).
For the first operator to work, it also adds a new user preferences path: i18n_branches_directory, to point to the /branch part of a bf-translation checkout.
2012-07-09 14:25:35 +00:00
|
|
|
{
|
2015-08-16 17:32:01 +10:00
|
|
|
BLT_lang_init();
|
UI translation from inside Blender UI: first part.
This commit reshapes a bit runtime button info getter, by adding a new uiButGetStrInfo() which accepts a variable number of uiStringInfo parameters, and tries to fill them with the requested strings, for the given button (label, tip, context, RNA identifier, keymap, etc.). Currently used mostly by existing ui_tooltip_create(), and new UI_OT_edittranslation_init operator.
It also adds a few getters (to get RNA i18n context, and current language iso code).
Finally, it adds to C operators needed for the py ui_translation addon:
*UI_OT_edittranslation_init, which gathers requested data and launch the py operator.
*UI_OT_reloadtranslation, which forces a full reload of the whole UI translation (including rechecking the directory containing mo files).
For the first operator to work, it also adds a new user preferences path: i18n_branches_directory, to point to the /branch part of a bf-translation checkout.
2012-07-09 14:25:35 +00:00
|
|
|
BLF_cache_clear();
|
2022-07-29 23:16:58 -05:00
|
|
|
BLT_lang_set(nullptr);
|
UI translation from inside Blender UI: first part.
This commit reshapes a bit runtime button info getter, by adding a new uiButGetStrInfo() which accepts a variable number of uiStringInfo parameters, and tries to fill them with the requested strings, for the given button (label, tip, context, RNA identifier, keymap, etc.). Currently used mostly by existing ui_tooltip_create(), and new UI_OT_edittranslation_init operator.
It also adds a few getters (to get RNA i18n context, and current language iso code).
Finally, it adds to C operators needed for the py ui_translation addon:
*UI_OT_edittranslation_init, which gathers requested data and launch the py operator.
*UI_OT_reloadtranslation, which forces a full reload of the whole UI translation (including rechecking the directory containing mo files).
For the first operator to work, it also adds a new user preferences path: i18n_branches_directory, to point to the /branch part of a bf-translation checkout.
2012-07-09 14:25:35 +00:00
|
|
|
UI_reinit_font();
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void UI_OT_reloadtranslation(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
/* identifiers */
|
|
|
|
|
ot->name = "Reload Translation";
|
|
|
|
|
ot->idname = "UI_OT_reloadtranslation";
|
|
|
|
|
ot->description = "Force a full reload of UI translation";
|
|
|
|
|
|
|
|
|
|
/* callbacks */
|
|
|
|
|
ot->exec = reloadtranslation_exec;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-23 12:07:05 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
2019-06-16 13:37:21 +10:00
|
|
|
/** \name Press Button Operator
|
2019-03-23 12:07:05 +11:00
|
|
|
* \{ */
|
2018-10-27 12:21:19 +11:00
|
|
|
|
|
|
|
|
static int ui_button_press_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|
|
|
|
{
|
2020-04-03 14:23:21 +02:00
|
|
|
bScreen *screen = CTX_wm_screen(C);
|
2018-10-27 12:21:19 +11:00
|
|
|
const bool skip_depressed = RNA_boolean_get(op->ptr, "skip_depressed");
|
2020-04-03 12:51:03 +02:00
|
|
|
ARegion *region_prev = CTX_wm_region(C);
|
2022-07-29 23:16:58 -05:00
|
|
|
ARegion *region = screen ? BKE_screen_find_region_xy(screen, RGN_TYPE_ANY, event->xy) : nullptr;
|
2018-10-27 12:21:19 +11:00
|
|
|
|
2022-07-29 23:16:58 -05:00
|
|
|
if (region == nullptr) {
|
2020-04-03 12:51:03 +02:00
|
|
|
region = region_prev;
|
2018-10-27 12:21:19 +11:00
|
|
|
}
|
|
|
|
|
|
2022-07-29 23:16:58 -05:00
|
|
|
if (region == nullptr) {
|
2019-07-01 12:10:49 +10:00
|
|
|
return OPERATOR_PASS_THROUGH;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-06 16:56:42 +01:00
|
|
|
CTX_wm_region_set(C, region);
|
2018-10-27 12:21:19 +11:00
|
|
|
uiBut *but = UI_context_active_but_get(C);
|
2020-04-03 12:51:03 +02:00
|
|
|
CTX_wm_region_set(C, region_prev);
|
2018-10-27 12:21:19 +11:00
|
|
|
|
2022-07-29 23:16:58 -05:00
|
|
|
if (but == nullptr) {
|
2018-10-27 12:21:19 +11:00
|
|
|
return OPERATOR_PASS_THROUGH;
|
|
|
|
|
}
|
|
|
|
|
if (skip_depressed && (but->flag & (UI_SELECT | UI_SELECT_DRAW))) {
|
|
|
|
|
return OPERATOR_PASS_THROUGH;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Weak, this is a workaround for 'UI_but_is_tool', which checks the operator type,
|
|
|
|
|
* having this avoids a minor drawing glitch. */
|
|
|
|
|
void *but_optype = but->optype;
|
|
|
|
|
|
2020-03-06 16:56:42 +01:00
|
|
|
UI_but_execute(C, region, but);
|
2018-10-27 12:21:19 +11:00
|
|
|
|
2022-07-29 23:16:58 -05:00
|
|
|
but->optype = static_cast<wmOperatorType *>(but_optype);
|
2018-10-27 12:21:19 +11:00
|
|
|
|
2020-03-06 16:22:28 +01:00
|
|
|
WM_event_add_mousemove(CTX_wm_window(C));
|
2018-10-27 12:21:19 +11:00
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void UI_OT_button_execute(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "Press Button";
|
|
|
|
|
ot->idname = "UI_OT_button_execute";
|
|
|
|
|
ot->description = "Presses active button";
|
|
|
|
|
|
|
|
|
|
ot->invoke = ui_button_press_invoke;
|
|
|
|
|
ot->flag = OPTYPE_INTERNAL;
|
|
|
|
|
|
2022-07-29 23:16:58 -05:00
|
|
|
RNA_def_boolean(ot->srna, "skip_depressed", false, "Skip Depressed", "");
|
2018-10-27 12:21:19 +11:00
|
|
|
}
|
|
|
|
|
|
2019-03-23 12:07:05 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
2019-09-09 16:34:16 +02:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Text Button Clear Operator
|
|
|
|
|
* \{ */
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int button_string_clear_exec(bContext *C, wmOperator * /*op*/)
|
2019-09-09 16:34:16 +02:00
|
|
|
{
|
2024-05-10 11:25:44 +10:00
|
|
|
uiBut *but = UI_context_active_but_get_respect_popup(C);
|
2019-09-09 16:34:16 +02:00
|
|
|
|
|
|
|
|
if (but) {
|
|
|
|
|
ui_but_active_string_clear_and_exit(C, but);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void UI_OT_button_string_clear(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "Clear Button String";
|
|
|
|
|
ot->idname = "UI_OT_button_string_clear";
|
|
|
|
|
ot->description = "Unsets the text of the active button";
|
|
|
|
|
|
|
|
|
|
ot->poll = ED_operator_regionactive;
|
|
|
|
|
ot->exec = button_string_clear_exec;
|
|
|
|
|
ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
2019-03-23 12:07:05 +11:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Drop Color Operator
|
|
|
|
|
* \{ */
|
|
|
|
|
|
2023-06-03 08:36:28 +10:00
|
|
|
bool UI_drop_color_poll(bContext *C, wmDrag *drag, const wmEvent * /*event*/)
|
2014-07-21 12:02:05 +02:00
|
|
|
{
|
|
|
|
|
/* should only return true for regions that include buttons, for now
|
|
|
|
|
* return true always */
|
|
|
|
|
if (drag->type == WM_DRAG_COLOR) {
|
|
|
|
|
SpaceImage *sima = CTX_wm_space_image(C);
|
2020-03-06 16:56:42 +01:00
|
|
|
ARegion *region = CTX_wm_region(C);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-25 10:15:20 +11:00
|
|
|
if (UI_but_active_drop_color(C)) {
|
2022-07-29 23:16:58 -05:00
|
|
|
return true;
|
2019-03-25 10:15:20 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-07-21 12:02:05 +02:00
|
|
|
if (sima && (sima->mode == SI_MODE_PAINT) && sima->image &&
|
2020-03-06 16:56:42 +01:00
|
|
|
(region && region->regiontype == RGN_TYPE_WINDOW))
|
|
|
|
|
{
|
2022-07-29 23:16:58 -05:00
|
|
|
return true;
|
2014-07-21 12:02:05 +02:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-07-29 23:16:58 -05:00
|
|
|
return false;
|
2014-07-21 12:02:05 +02:00
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
void UI_drop_color_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop)
|
2014-07-21 12:02:05 +02:00
|
|
|
{
|
2022-07-29 23:16:58 -05:00
|
|
|
uiDragColorHandle *drag_info = static_cast<uiDragColorHandle *>(drag->poin);
|
2014-07-21 12:02:05 +02:00
|
|
|
|
|
|
|
|
RNA_float_set_array(drop->ptr, "color", drag_info->color);
|
|
|
|
|
RNA_boolean_set(drop->ptr, "gamma", drag_info->gamma_corrected);
|
|
|
|
|
}
|
|
|
|
|
|
Add support for tiled images and the UDIM naming scheme
This patch contains the work that I did during my week at the Code Quest - adding support for tiled images to Blender.
With this patch, images now contain a list of tiles. By default, this just contains one tile, but if the source type is set to Tiled, the user can add additional tiles. When acquiring an ImBuf, the tile to be loaded is specified in the ImageUser.
Therefore, code that is not yet aware of tiles will just access the default tile as usual.
The filenames of the additional tiles are derived from the original filename according to the UDIM naming scheme - the filename contains an index that is calculated as (1001 + 10*<y coordinate of the tile> + <x coordinate of the tile>), where the x coordinate never goes above 9.
Internally, the various tiles are stored in a cache just like sequences. When acquired for the first time, the code will try to load the corresponding file from disk. Alternatively, a new operator can be used to initialize the tile similar to the New Image operator.
The following features are supported so far:
- Automatic detection and loading of all tiles when opening the first tile (1001)
- Saving all tiles
- Adding and removing tiles
- Filling tiles with generated images
- Drawing all tiles in the Image Editor
- Viewing a tiled grid even if no image is selected
- Rendering tiled images in Eevee
- Rendering tiled images in Cycles (in SVM mode)
- Automatically skipping loading of unused tiles in Cycles
- 2D texture painting (also across tiles)
- 3D texture painting (also across tiles, only limitation: individual faces can not cross tile borders)
- Assigning custom labels to individual tiles (drawn in the Image Editor instead of the ID)
- Different resolutions between tiles
There still are some missing features that will be added later (see T72390):
- Workbench engine support
- Packing/Unpacking support
- Baking support
- Cycles OSL support
- many other Blender features that rely on images
Thanks to Brecht for the review and to all who tested the intermediate versions!
Differential Revision: https://developer.blender.org/D3509
2019-12-12 16:06:08 +01:00
|
|
|
static int drop_color_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
2014-07-21 12:02:05 +02:00
|
|
|
{
|
2020-03-06 16:56:42 +01:00
|
|
|
ARegion *region = CTX_wm_region(C);
|
2022-07-29 23:16:58 -05:00
|
|
|
uiBut *but = nullptr;
|
2014-08-25 14:31:51 +10:00
|
|
|
float color[4];
|
2014-07-21 12:02:05 +02:00
|
|
|
bool gamma;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-07-21 12:02:05 +02:00
|
|
|
RNA_float_get_array(op->ptr, "color", color);
|
|
|
|
|
gamma = RNA_boolean_get(op->ptr, "gamma");
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-07-21 12:02:05 +02:00
|
|
|
/* find button under mouse, check if it has RNA color property and
|
|
|
|
|
* if it does copy the data */
|
2020-03-06 16:56:42 +01:00
|
|
|
but = ui_region_find_active_but(region);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-11-09 21:20:40 +01:00
|
|
|
if (but && but->type == UI_BTYPE_COLOR && but->rnaprop) {
|
2014-08-25 14:31:51 +10:00
|
|
|
const int color_len = RNA_property_array_length(&but->rnapoin, but->rnaprop);
|
|
|
|
|
BLI_assert(color_len <= 4);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-08-25 14:31:51 +10:00
|
|
|
/* keep alpha channel as-is */
|
|
|
|
|
if (color_len == 4) {
|
|
|
|
|
color[3] = RNA_property_float_get_index(&but->rnapoin, but->rnaprop, 3);
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-07-21 12:02:05 +02:00
|
|
|
if (RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) {
|
2019-03-25 10:15:20 +11:00
|
|
|
if (!gamma) {
|
2022-05-20 17:54:43 +02:00
|
|
|
IMB_colormanagement_scene_linear_to_srgb_v3(color, color);
|
2019-03-25 10:15:20 +11:00
|
|
|
}
|
2014-07-21 12:02:05 +02:00
|
|
|
RNA_property_float_set_array(&but->rnapoin, but->rnaprop, color);
|
|
|
|
|
RNA_property_update(C, &but->rnapoin, but->rnaprop);
|
|
|
|
|
}
|
|
|
|
|
else if (RNA_property_subtype(but->rnaprop) == PROP_COLOR) {
|
2019-03-25 10:15:20 +11:00
|
|
|
if (gamma) {
|
2022-05-20 17:54:43 +02:00
|
|
|
IMB_colormanagement_srgb_to_scene_linear_v3(color, color);
|
2019-03-25 10:15:20 +11:00
|
|
|
}
|
2014-07-21 12:02:05 +02:00
|
|
|
RNA_property_float_set_array(&but->rnapoin, but->rnaprop, color);
|
|
|
|
|
RNA_property_update(C, &but->rnapoin, but->rnaprop);
|
|
|
|
|
}
|
2023-10-19 16:02:34 +02:00
|
|
|
|
|
|
|
|
if (UI_but_flag_is_set(but, UI_BUT_UNDO)) {
|
|
|
|
|
ED_undo_push(C, RNA_property_ui_name(but->rnaprop));
|
|
|
|
|
}
|
2014-07-21 12:02:05 +02:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (gamma) {
|
|
|
|
|
srgb_to_linearrgb_v3_v3(color, color);
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
Add support for tiled images and the UDIM naming scheme
This patch contains the work that I did during my week at the Code Quest - adding support for tiled images to Blender.
With this patch, images now contain a list of tiles. By default, this just contains one tile, but if the source type is set to Tiled, the user can add additional tiles. When acquiring an ImBuf, the tile to be loaded is specified in the ImageUser.
Therefore, code that is not yet aware of tiles will just access the default tile as usual.
The filenames of the additional tiles are derived from the original filename according to the UDIM naming scheme - the filename contains an index that is calculated as (1001 + 10*<y coordinate of the tile> + <x coordinate of the tile>), where the x coordinate never goes above 9.
Internally, the various tiles are stored in a cache just like sequences. When acquired for the first time, the code will try to load the corresponding file from disk. Alternatively, a new operator can be used to initialize the tile similar to the New Image operator.
The following features are supported so far:
- Automatic detection and loading of all tiles when opening the first tile (1001)
- Saving all tiles
- Adding and removing tiles
- Filling tiles with generated images
- Drawing all tiles in the Image Editor
- Viewing a tiled grid even if no image is selected
- Rendering tiled images in Eevee
- Rendering tiled images in Cycles (in SVM mode)
- Automatically skipping loading of unused tiles in Cycles
- 2D texture painting (also across tiles)
- 3D texture painting (also across tiles, only limitation: individual faces can not cross tile borders)
- Assigning custom labels to individual tiles (drawn in the Image Editor instead of the ID)
- Different resolutions between tiles
There still are some missing features that will be added later (see T72390):
- Workbench engine support
- Packing/Unpacking support
- Baking support
- Cycles OSL support
- many other Blender features that rely on images
Thanks to Brecht for the review and to all who tested the intermediate versions!
Differential Revision: https://developer.blender.org/D3509
2019-12-12 16:06:08 +01:00
|
|
|
ED_imapaint_bucket_fill(C, color, op, event->mval);
|
2014-07-21 12:02:05 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-03-06 16:56:42 +01:00
|
|
|
ED_region_tag_redraw(region);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-07-21 12:02:05 +02:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void UI_OT_drop_color(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "Drop Color";
|
|
|
|
|
ot->idname = "UI_OT_drop_color";
|
|
|
|
|
ot->description = "Drop colors to buttons";
|
|
|
|
|
|
|
|
|
|
ot->invoke = drop_color_invoke;
|
2023-04-30 14:52:54 +10:00
|
|
|
ot->poll = ED_operator_regionactive;
|
|
|
|
|
|
2015-02-24 14:38:08 +05:00
|
|
|
ot->flag = OPTYPE_INTERNAL;
|
2014-07-21 12:02:05 +02:00
|
|
|
|
2022-07-29 23:16:58 -05:00
|
|
|
RNA_def_float_color(
|
|
|
|
|
ot->srna, "color", 3, nullptr, 0.0, FLT_MAX, "Color", "Source color", 0.0, 1.0);
|
|
|
|
|
RNA_def_boolean(
|
|
|
|
|
ot->srna, "gamma", false, "Gamma Corrected", "The source color is gamma corrected");
|
2014-07-21 12:02:05 +02:00
|
|
|
}
|
|
|
|
|
|
2019-03-23 12:07:05 +11:00
|
|
|
/** \} */
|
2014-07-21 12:02:05 +02:00
|
|
|
|
2021-10-27 15:50:14 +02:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Drop Name Operator
|
|
|
|
|
* \{ */
|
|
|
|
|
|
2022-06-14 11:53:38 +02:00
|
|
|
static bool drop_name_poll(bContext *C)
|
|
|
|
|
{
|
|
|
|
|
if (!ED_operator_regionactive(C)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const uiBut *but = UI_but_active_drop_name_button(C);
|
|
|
|
|
if (!but) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (but->flag & UI_BUT_DISABLED) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int drop_name_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
2021-10-27 15:50:14 +02:00
|
|
|
{
|
|
|
|
|
uiBut *but = UI_but_active_drop_name_button(C);
|
2022-07-29 23:16:58 -05:00
|
|
|
char *str = RNA_string_get_alloc(op->ptr, "string", nullptr, 0, nullptr);
|
2021-10-27 15:50:14 +02:00
|
|
|
|
|
|
|
|
if (str) {
|
|
|
|
|
ui_but_set_string_interactive(C, but, str);
|
|
|
|
|
MEM_freeN(str);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void UI_OT_drop_name(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "Drop Name";
|
|
|
|
|
ot->idname = "UI_OT_drop_name";
|
|
|
|
|
ot->description = "Drop name to button";
|
|
|
|
|
|
2022-06-14 11:53:38 +02:00
|
|
|
ot->poll = drop_name_poll;
|
2021-10-27 15:50:14 +02:00
|
|
|
ot->invoke = drop_name_invoke;
|
|
|
|
|
ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL;
|
|
|
|
|
|
|
|
|
|
RNA_def_string(
|
2022-07-29 23:16:58 -05:00
|
|
|
ot->srna, "string", nullptr, 0, "String", "The string value to drop into the button");
|
2021-10-27 15:50:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
2021-07-13 15:44:44 +02:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name UI List Search Operator
|
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
|
|
static bool ui_list_focused_poll(bContext *C)
|
|
|
|
|
{
|
|
|
|
|
const ARegion *region = CTX_wm_region(C);
|
2022-03-22 18:35:28 +01:00
|
|
|
if (!region) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-07-13 15:44:44 +02:00
|
|
|
const wmWindow *win = CTX_wm_window(C);
|
|
|
|
|
const uiList *list = UI_list_find_mouse_over(region, win->eventstate);
|
|
|
|
|
|
2022-07-29 23:16:58 -05:00
|
|
|
return list != nullptr;
|
2021-07-13 15:44:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Ensure the filter options are set to be visible in the UI list.
|
|
|
|
|
* \return if the visibility changed, requiring a redraw.
|
|
|
|
|
*/
|
|
|
|
|
static bool ui_list_unhide_filter_options(uiList *list)
|
|
|
|
|
{
|
|
|
|
|
if (list->filter_flag & UILST_FLT_SHOW) {
|
|
|
|
|
/* Nothing to be done. */
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
list->filter_flag |= UILST_FLT_SHOW;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int ui_list_start_filter_invoke(bContext *C, wmOperator * /*op*/, const wmEvent *event)
|
2021-07-13 15:44:44 +02:00
|
|
|
{
|
|
|
|
|
ARegion *region = CTX_wm_region(C);
|
|
|
|
|
uiList *list = UI_list_find_mouse_over(region, event);
|
|
|
|
|
/* Poll should check. */
|
2022-07-29 23:16:58 -05:00
|
|
|
BLI_assert(list != nullptr);
|
2021-07-13 15:44:44 +02:00
|
|
|
|
|
|
|
|
if (ui_list_unhide_filter_options(list)) {
|
|
|
|
|
ui_region_redraw_immediately(C, region);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!UI_textbutton_activate_rna(C, region, list, "filter_name")) {
|
|
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void UI_OT_list_start_filter(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "List Filter";
|
|
|
|
|
ot->idname = "UI_OT_list_start_filter";
|
|
|
|
|
ot->description = "Start entering filter text for the list in focus";
|
|
|
|
|
|
|
|
|
|
ot->invoke = ui_list_start_filter_invoke;
|
|
|
|
|
ot->poll = ui_list_focused_poll;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
2023-06-12 11:41:00 +02:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name UI View Start Filter Operator
|
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
|
|
static bool ui_view_focused_poll(bContext *C)
|
|
|
|
|
{
|
2023-09-19 21:19:20 +10:00
|
|
|
const wmWindow *win = CTX_wm_window(C);
|
|
|
|
|
if (!(win && win->eventstate)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-12 11:41:00 +02:00
|
|
|
const ARegion *region = CTX_wm_region(C);
|
|
|
|
|
if (!region) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2024-03-08 09:16:00 -05:00
|
|
|
const blender::ui::AbstractView *view = UI_region_view_find_at(region, win->eventstate->xy, 0);
|
2023-06-12 11:41:00 +02:00
|
|
|
return view != nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ui_view_start_filter_invoke(bContext *C, wmOperator * /*op*/, const wmEvent *event)
|
|
|
|
|
{
|
|
|
|
|
const ARegion *region = CTX_wm_region(C);
|
2024-03-08 09:16:00 -05:00
|
|
|
const blender::ui::AbstractView *hovered_view = UI_region_view_find_at(region, event->xy, 0);
|
2023-06-12 11:41:00 +02:00
|
|
|
|
2024-03-08 09:16:00 -05:00
|
|
|
if (!hovered_view->begin_filtering(*C)) {
|
2023-06-12 11:41:00 +02:00
|
|
|
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void UI_OT_view_start_filter(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "View Filter";
|
|
|
|
|
ot->idname = "UI_OT_view_start_filter";
|
|
|
|
|
ot->description = "Start entering filter text for the data-set in focus";
|
|
|
|
|
|
|
|
|
|
ot->invoke = ui_view_start_filter_invoke;
|
|
|
|
|
ot->poll = ui_view_focused_poll;
|
|
|
|
|
|
|
|
|
|
ot->flag = OPTYPE_INTERNAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
2021-09-30 16:26:56 +02:00
|
|
|
/* -------------------------------------------------------------------- */
|
2023-03-22 18:45:35 +01:00
|
|
|
/** \name UI View Drop Operator
|
2021-09-30 16:26:56 +02:00
|
|
|
* \{ */
|
|
|
|
|
|
UI: Port view item features to base class, merge view item button types
No user visible changes expected.
Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).
This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
selection).
- Simplifies APIs, there don't have to be functions for individual view
item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
two into one.
I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:14:42 +02:00
|
|
|
static bool ui_view_drop_poll(bContext *C)
|
2021-09-30 16:26:56 +02:00
|
|
|
{
|
|
|
|
|
const wmWindow *win = CTX_wm_window(C);
|
2023-09-19 21:19:20 +10:00
|
|
|
if (!(win && win->eventstate)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-09-30 16:26:56 +02:00
|
|
|
const ARegion *region = CTX_wm_region(C);
|
2022-08-28 20:19:05 +10:00
|
|
|
if (region == nullptr) {
|
2022-08-28 20:08:18 +10:00
|
|
|
return false;
|
|
|
|
|
}
|
2023-03-22 18:45:35 +01:00
|
|
|
return region_views_find_drop_target_at(region, win->eventstate->xy) != nullptr;
|
2021-09-30 16:26:56 +02:00
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int ui_view_drop_invoke(bContext *C, wmOperator * /*op*/, const wmEvent *event)
|
2021-09-30 16:26:56 +02:00
|
|
|
{
|
|
|
|
|
if (event->custom != EVT_DATA_DRAGDROP) {
|
|
|
|
|
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
|
|
|
|
|
}
|
|
|
|
|
|
UI: Basic tree-view drag & drop reordering and inserting support
No user visible changes expected, these are just the internal API preparations.
Modifies the Drop API for views so that tree-views can choose to insert items
before, after and into other items.
Note: While there is support for drag-tooltips that can explain how an item
will be inserted, there is no drawing yet like in the Outliner, that indicates
if an item is inserted before, after or into. There is some work on that but
that can be done separately.
Changes:
- Removes `AbstractViewDropTarget` that was shared between tree- and
grid-views, and adds `AbstractTreeViewDropTarget` and
`AbstractGridViewDropTarget`. The tree-view needs specialized handling now,
and although they could share some code still, it's not worth having another
level of inheritance.
- Modifies the drop-target API to use `DragInfo` which contains more info about
the dragging operation than just the `wmDrag`.
- Adds `determine_drop_location()` to the `DropTargetInterface` which drop
targets can use to determine when dropping means inserting before, after or
into.
- Store the block and region in the view. This is needed unfortunately but
shouldn't be an issue since the tree view is recreated on redraws, together
with the block.
- Various smaller tweaks and additions to views as needed.
TODO (outside scope of this change): Increase row height so there is no gap
between tree view items, but keep things visually the same otherwise. This
reduces flickering while dragging.
Pull Request: https://projects.blender.org/blender/blender/pulls/109825
2023-07-11 14:30:26 +02:00
|
|
|
ARegion *region = CTX_wm_region(C);
|
2023-03-23 14:28:50 +11:00
|
|
|
std::unique_ptr<DropTargetInterface> drop_target = region_views_find_drop_target_at(region,
|
|
|
|
|
event->xy);
|
2021-09-30 16:26:56 +02:00
|
|
|
|
UI: Basic tree-view drag & drop reordering and inserting support
No user visible changes expected, these are just the internal API preparations.
Modifies the Drop API for views so that tree-views can choose to insert items
before, after and into other items.
Note: While there is support for drag-tooltips that can explain how an item
will be inserted, there is no drawing yet like in the Outliner, that indicates
if an item is inserted before, after or into. There is some work on that but
that can be done separately.
Changes:
- Removes `AbstractViewDropTarget` that was shared between tree- and
grid-views, and adds `AbstractTreeViewDropTarget` and
`AbstractGridViewDropTarget`. The tree-view needs specialized handling now,
and although they could share some code still, it's not worth having another
level of inheritance.
- Modifies the drop-target API to use `DragInfo` which contains more info about
the dragging operation than just the `wmDrag`.
- Adds `determine_drop_location()` to the `DropTargetInterface` which drop
targets can use to determine when dropping means inserting before, after or
into.
- Store the block and region in the view. This is needed unfortunately but
shouldn't be an issue since the tree view is recreated on redraws, together
with the block.
- Various smaller tweaks and additions to views as needed.
TODO (outside scope of this change): Increase row height so there is no gap
between tree view items, but keep things visually the same otherwise. This
reduces flickering while dragging.
Pull Request: https://projects.blender.org/blender/blender/pulls/109825
2023-07-11 14:30:26 +02:00
|
|
|
if (!drop_target_apply_drop(
|
|
|
|
|
*C, *region, *event, *drop_target, *static_cast<const ListBase *>(event->customdata)))
|
2023-03-22 18:45:35 +01:00
|
|
|
{
|
2021-09-30 16:26:56 +02:00
|
|
|
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
|
|
|
|
|
}
|
|
|
|
|
|
UI: Basic tree-view drag & drop reordering and inserting support
No user visible changes expected, these are just the internal API preparations.
Modifies the Drop API for views so that tree-views can choose to insert items
before, after and into other items.
Note: While there is support for drag-tooltips that can explain how an item
will be inserted, there is no drawing yet like in the Outliner, that indicates
if an item is inserted before, after or into. There is some work on that but
that can be done separately.
Changes:
- Removes `AbstractViewDropTarget` that was shared between tree- and
grid-views, and adds `AbstractTreeViewDropTarget` and
`AbstractGridViewDropTarget`. The tree-view needs specialized handling now,
and although they could share some code still, it's not worth having another
level of inheritance.
- Modifies the drop-target API to use `DragInfo` which contains more info about
the dragging operation than just the `wmDrag`.
- Adds `determine_drop_location()` to the `DropTargetInterface` which drop
targets can use to determine when dropping means inserting before, after or
into.
- Store the block and region in the view. This is needed unfortunately but
shouldn't be an issue since the tree view is recreated on redraws, together
with the block.
- Various smaller tweaks and additions to views as needed.
TODO (outside scope of this change): Increase row height so there is no gap
between tree view items, but keep things visually the same otherwise. This
reduces flickering while dragging.
Pull Request: https://projects.blender.org/blender/blender/pulls/109825
2023-07-11 14:30:26 +02:00
|
|
|
ED_region_tag_redraw(region);
|
2021-09-30 16:26:56 +02:00
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
UI: Port view item features to base class, merge view item button types
No user visible changes expected.
Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).
This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
selection).
- Simplifies APIs, there don't have to be functions for individual view
item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
two into one.
I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:14:42 +02:00
|
|
|
static void UI_OT_view_drop(wmOperatorType *ot)
|
2021-09-30 16:26:56 +02:00
|
|
|
{
|
2023-03-22 18:45:35 +01:00
|
|
|
ot->name = "View Drop";
|
UI: Port view item features to base class, merge view item button types
No user visible changes expected.
Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).
This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
selection).
- Simplifies APIs, there don't have to be functions for individual view
item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
two into one.
I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:14:42 +02:00
|
|
|
ot->idname = "UI_OT_view_drop";
|
2023-03-22 18:45:35 +01:00
|
|
|
ot->description = "Drag and drop onto a data-set or item within the data-set";
|
2021-09-30 16:26:56 +02:00
|
|
|
|
UI: Port view item features to base class, merge view item button types
No user visible changes expected.
Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).
This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
selection).
- Simplifies APIs, there don't have to be functions for individual view
item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
two into one.
I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:14:42 +02:00
|
|
|
ot->invoke = ui_view_drop_invoke;
|
|
|
|
|
ot->poll = ui_view_drop_poll;
|
2021-09-30 16:26:56 +02:00
|
|
|
|
|
|
|
|
ot->flag = OPTYPE_INTERNAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
2021-10-08 19:56:24 +02:00
|
|
|
/* -------------------------------------------------------------------- */
|
UI: Port view item features to base class, merge view item button types
No user visible changes expected.
Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).
This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
selection).
- Simplifies APIs, there don't have to be functions for individual view
item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
two into one.
I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:14:42 +02:00
|
|
|
/** \name UI View Item Rename Operator
|
2021-10-08 19:56:24 +02:00
|
|
|
*
|
UI: Port view item features to base class, merge view item button types
No user visible changes expected.
Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).
This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
selection).
- Simplifies APIs, there don't have to be functions for individual view
item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
two into one.
I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:14:42 +02:00
|
|
|
* General purpose renaming operator for views. Thanks to this, to add a rename button to context
|
|
|
|
|
* menus for example, view API users don't have to implement their own renaming operators with the
|
|
|
|
|
* same logic as they already have for their #ui::AbstractViewItem::rename() override.
|
2021-10-08 19:56:24 +02:00
|
|
|
*
|
|
|
|
|
* \{ */
|
|
|
|
|
|
UI: Port view item features to base class, merge view item button types
No user visible changes expected.
Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).
This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
selection).
- Simplifies APIs, there don't have to be functions for individual view
item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
two into one.
I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:14:42 +02:00
|
|
|
static bool ui_view_item_rename_poll(bContext *C)
|
2021-10-08 19:56:24 +02:00
|
|
|
{
|
|
|
|
|
const ARegion *region = CTX_wm_region(C);
|
2022-08-28 20:19:05 +10:00
|
|
|
if (region == nullptr) {
|
2022-08-28 20:08:18 +10:00
|
|
|
return false;
|
|
|
|
|
}
|
2024-03-08 09:16:00 -05:00
|
|
|
const blender::ui::AbstractViewItem *active_item = UI_region_views_find_active_item(region);
|
|
|
|
|
return active_item != nullptr && UI_view_item_can_rename(*active_item);
|
2021-10-08 19:56:24 +02:00
|
|
|
}
|
|
|
|
|
|
2022-10-03 17:37:25 -05:00
|
|
|
static int ui_view_item_rename_exec(bContext *C, wmOperator * /*op*/)
|
2021-10-08 19:56:24 +02:00
|
|
|
{
|
|
|
|
|
ARegion *region = CTX_wm_region(C);
|
2024-03-08 09:16:00 -05:00
|
|
|
blender::ui::AbstractViewItem *active_item = UI_region_views_find_active_item(region);
|
2021-10-08 19:56:24 +02:00
|
|
|
|
2024-03-08 09:16:00 -05:00
|
|
|
UI_view_item_begin_rename(*active_item);
|
2021-10-08 19:56:24 +02:00
|
|
|
ED_region_tag_redraw(region);
|
|
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
UI: Port view item features to base class, merge view item button types
No user visible changes expected.
Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).
This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
selection).
- Simplifies APIs, there don't have to be functions for individual view
item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
two into one.
I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:14:42 +02:00
|
|
|
static void UI_OT_view_item_rename(wmOperatorType *ot)
|
2021-10-08 19:56:24 +02:00
|
|
|
{
|
UI: Port view item features to base class, merge view item button types
No user visible changes expected.
Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).
This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
selection).
- Simplifies APIs, there don't have to be functions for individual view
item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
two into one.
I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:14:42 +02:00
|
|
|
ot->name = "Rename View Item";
|
|
|
|
|
ot->idname = "UI_OT_view_item_rename";
|
|
|
|
|
ot->description = "Rename the active item in the data-set view";
|
2021-10-08 19:56:24 +02:00
|
|
|
|
UI: Port view item features to base class, merge view item button types
No user visible changes expected.
Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).
This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
selection).
- Simplifies APIs, there don't have to be functions for individual view
item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
two into one.
I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:14:42 +02:00
|
|
|
ot->exec = ui_view_item_rename_exec;
|
|
|
|
|
ot->poll = ui_view_item_rename_poll;
|
2021-10-08 19:56:24 +02:00
|
|
|
/* Could get a custom tooltip via the `get_description()` callback and another overridable
|
UI: Port view item features to base class, merge view item button types
No user visible changes expected.
Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).
This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
selection).
- Simplifies APIs, there don't have to be functions for individual view
item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
two into one.
I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:14:42 +02:00
|
|
|
* function of the view. */
|
2021-10-08 19:56:24 +02:00
|
|
|
|
|
|
|
|
ot->flag = OPTYPE_INTERNAL;
|
|
|
|
|
}
|
2022-03-03 15:28:48 -08:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Material Drag/Drop Operator
|
|
|
|
|
*
|
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
|
|
static bool ui_drop_material_poll(bContext *C)
|
|
|
|
|
{
|
|
|
|
|
PointerRNA ptr = CTX_data_pointer_get_type(C, "object", &RNA_Object);
|
2022-07-29 23:16:58 -05:00
|
|
|
const Object *ob = static_cast<const Object *>(ptr.data);
|
|
|
|
|
if (ob == nullptr) {
|
2022-03-03 15:28:48 -08:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PointerRNA mat_slot = CTX_data_pointer_get_type(C, "material_slot", &RNA_MaterialSlot);
|
|
|
|
|
if (RNA_pointer_is_null(&mat_slot)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ui_drop_material_exec(bContext *C, wmOperator *op)
|
|
|
|
|
{
|
|
|
|
|
Main *bmain = CTX_data_main(C);
|
|
|
|
|
|
2024-01-22 13:47:13 +01:00
|
|
|
Material *ma = (Material *)WM_operator_properties_id_lookup_from_name_or_session_uid(
|
2022-05-24 16:47:48 +02:00
|
|
|
bmain, op->ptr, ID_MA);
|
2022-07-29 23:16:58 -05:00
|
|
|
if (ma == nullptr) {
|
2022-03-03 15:28:48 -08:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PointerRNA ptr = CTX_data_pointer_get_type(C, "object", &RNA_Object);
|
2022-07-29 23:16:58 -05:00
|
|
|
Object *ob = static_cast<Object *>(ptr.data);
|
2022-03-03 15:28:48 -08:00
|
|
|
BLI_assert(ob);
|
|
|
|
|
|
|
|
|
|
PointerRNA mat_slot = CTX_data_pointer_get_type(C, "material_slot", &RNA_MaterialSlot);
|
|
|
|
|
BLI_assert(mat_slot.data);
|
|
|
|
|
const int target_slot = RNA_int_get(&mat_slot, "slot_index") + 1;
|
|
|
|
|
|
|
|
|
|
/* only drop grease pencil material on grease pencil objects */
|
2023-03-08 12:35:58 +01:00
|
|
|
if ((ma->gp_style != nullptr) && (ob->type != OB_GPENCIL_LEGACY)) {
|
2022-03-03 15:28:48 -08:00
|
|
|
return OPERATOR_CANCELLED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BKE_object_material_assign(bmain, ob, ma, target_slot, BKE_MAT_ASSIGN_USERPREF);
|
|
|
|
|
|
|
|
|
|
WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, ob);
|
2022-07-29 23:16:58 -05:00
|
|
|
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, nullptr);
|
2022-03-03 15:28:48 -08:00
|
|
|
WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_LINKS, ma);
|
|
|
|
|
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
|
|
|
|
|
|
|
|
|
|
return OPERATOR_FINISHED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void UI_OT_drop_material(wmOperatorType *ot)
|
|
|
|
|
{
|
|
|
|
|
ot->name = "Drop Material in Material slots";
|
|
|
|
|
ot->description = "Drag material to Material slots in Properties";
|
|
|
|
|
ot->idname = "UI_OT_drop_material";
|
|
|
|
|
|
|
|
|
|
ot->poll = ui_drop_material_poll;
|
|
|
|
|
ot->exec = ui_drop_material_exec;
|
|
|
|
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
|
|
|
|
|
2022-05-24 16:47:48 +02:00
|
|
|
WM_operator_properties_id_lookup(ot, false);
|
2022-03-03 15:28:48 -08:00
|
|
|
}
|
2021-10-08 19:56:24 +02:00
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
2019-03-23 12:07:05 +11:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Operator & Keymap Registration
|
|
|
|
|
* \{ */
|
2009-12-17 10:47:55 +00:00
|
|
|
|
2023-07-02 19:37:22 +10:00
|
|
|
void ED_operatortypes_ui()
|
2009-12-17 10:47:55 +00:00
|
|
|
{
|
2024-04-17 12:44:53 -04:00
|
|
|
using namespace blender::ui;
|
2009-12-17 17:15:38 +00:00
|
|
|
WM_operatortype_append(UI_OT_copy_data_path_button);
|
2019-07-31 18:42:03 +03:00
|
|
|
WM_operatortype_append(UI_OT_copy_as_driver_button);
|
2016-02-26 11:57:35 +11:00
|
|
|
WM_operatortype_append(UI_OT_copy_python_command_button);
|
2009-12-17 10:47:55 +00:00
|
|
|
WM_operatortype_append(UI_OT_reset_default_button);
|
2018-12-15 22:37:12 +03:00
|
|
|
WM_operatortype_append(UI_OT_assign_default_button);
|
2013-09-20 09:10:17 +00:00
|
|
|
WM_operatortype_append(UI_OT_unset_property_button);
|
2009-12-17 17:15:38 +00:00
|
|
|
WM_operatortype_append(UI_OT_copy_to_selected_button);
|
2024-04-29 18:34:57 +02:00
|
|
|
WM_operatortype_append(UI_OT_copy_driver_to_selected_button);
|
2018-11-20 17:34:56 +11:00
|
|
|
WM_operatortype_append(UI_OT_jump_to_target_button);
|
2014-07-21 12:02:05 +02:00
|
|
|
WM_operatortype_append(UI_OT_drop_color);
|
2021-10-27 15:50:14 +02:00
|
|
|
WM_operatortype_append(UI_OT_drop_name);
|
2022-03-03 15:28:48 -08:00
|
|
|
WM_operatortype_append(UI_OT_drop_material);
|
2011-11-03 23:20:54 +00:00
|
|
|
#ifdef WITH_PYTHON
|
2011-10-23 04:13:56 +00:00
|
|
|
WM_operatortype_append(UI_OT_editsource);
|
UI translation from inside Blender UI: first part.
This commit reshapes a bit runtime button info getter, by adding a new uiButGetStrInfo() which accepts a variable number of uiStringInfo parameters, and tries to fill them with the requested strings, for the given button (label, tip, context, RNA identifier, keymap, etc.). Currently used mostly by existing ui_tooltip_create(), and new UI_OT_edittranslation_init operator.
It also adds a few getters (to get RNA i18n context, and current language iso code).
Finally, it adds to C operators needed for the py ui_translation addon:
*UI_OT_edittranslation_init, which gathers requested data and launch the py operator.
*UI_OT_reloadtranslation, which forces a full reload of the whole UI translation (including rechecking the directory containing mo files).
For the first operator to work, it also adds a new user preferences path: i18n_branches_directory, to point to the /branch part of a bf-translation checkout.
2012-07-09 14:25:35 +00:00
|
|
|
WM_operatortype_append(UI_OT_edittranslation_init);
|
2011-11-03 23:20:54 +00:00
|
|
|
#endif
|
UI translation from inside Blender UI: first part.
This commit reshapes a bit runtime button info getter, by adding a new uiButGetStrInfo() which accepts a variable number of uiStringInfo parameters, and tries to fill them with the requested strings, for the given button (label, tip, context, RNA identifier, keymap, etc.). Currently used mostly by existing ui_tooltip_create(), and new UI_OT_edittranslation_init operator.
It also adds a few getters (to get RNA i18n context, and current language iso code).
Finally, it adds to C operators needed for the py ui_translation addon:
*UI_OT_edittranslation_init, which gathers requested data and launch the py operator.
*UI_OT_reloadtranslation, which forces a full reload of the whole UI translation (including rechecking the directory containing mo files).
For the first operator to work, it also adds a new user preferences path: i18n_branches_directory, to point to the /branch part of a bf-translation checkout.
2012-07-09 14:25:35 +00:00
|
|
|
WM_operatortype_append(UI_OT_reloadtranslation);
|
2018-10-27 12:21:19 +11:00
|
|
|
WM_operatortype_append(UI_OT_button_execute);
|
2019-09-09 16:34:16 +02:00
|
|
|
WM_operatortype_append(UI_OT_button_string_clear);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-07-13 15:44:44 +02:00
|
|
|
WM_operatortype_append(UI_OT_list_start_filter);
|
|
|
|
|
|
2023-06-12 11:41:00 +02:00
|
|
|
WM_operatortype_append(UI_OT_view_start_filter);
|
UI: Port view item features to base class, merge view item button types
No user visible changes expected.
Merges the tree row and grid tile button types, which were mostly doing
the same things. The idea is that there is a button type for
highlighting, as well as supporting general view item features (e.g.
renaming, drag/drop, etc.). So instead there is a view item button type
now. Also ports view item features like renaming, custom context menus,
drag controllers and drop controllers to `ui::AbstractViewItem` (the new
base class for all view items).
This should be quite an improvement because:
- Merges code that was duplicated over view items.
- Mentioned features (renaming, drag & drop, ...) are much easier to
implement in new view types now. Most of it comes "for free".
- Further features will immediately become availalbe to all views (e.g.
selection).
- Simplifies APIs, there don't have to be functions for individual view
item types anymore.
- View item classes are split and thus less overwhelming visually.
- View item buttons now share all code (drawing, handling, etc.)
- We're soon running out of available button types, this commit merges
two into one.
I was hoping I could do this in multiple smaller commits, but things
were quite intertwined so that would've taken quite some effort.
2022-07-19 16:14:42 +02:00
|
|
|
WM_operatortype_append(UI_OT_view_drop);
|
|
|
|
|
WM_operatortype_append(UI_OT_view_item_rename);
|
2021-09-30 16:26:56 +02:00
|
|
|
|
2022-08-17 12:10:31 +02:00
|
|
|
WM_operatortype_append(UI_OT_override_type_set_button);
|
|
|
|
|
WM_operatortype_append(UI_OT_override_remove_button);
|
2022-08-24 12:11:50 +02:00
|
|
|
WM_operatortype_append(UI_OT_override_idtemplate_make);
|
2022-08-17 12:10:31 +02:00
|
|
|
WM_operatortype_append(UI_OT_override_idtemplate_reset);
|
|
|
|
|
WM_operatortype_append(UI_OT_override_idtemplate_clear);
|
|
|
|
|
override_idtemplate_menu();
|
|
|
|
|
|
2013-10-08 15:07:52 +00:00
|
|
|
/* external */
|
|
|
|
|
WM_operatortype_append(UI_OT_eyedropper_color);
|
2019-03-18 18:20:44 +11:00
|
|
|
WM_operatortype_append(UI_OT_eyedropper_colorramp);
|
|
|
|
|
WM_operatortype_append(UI_OT_eyedropper_colorramp_point);
|
2013-10-08 15:32:54 +00:00
|
|
|
WM_operatortype_append(UI_OT_eyedropper_id);
|
2015-01-14 08:01:40 +11:00
|
|
|
WM_operatortype_append(UI_OT_eyedropper_depth);
|
Driver Setup Workflow Improvement: Property Eyedropper
This commit brings some long requested improvements to the workflow for setting up
drivers, which should make it easier and faster to set up new drivers in a more
interactive fashion.
The new workflow is as follows:
1) Hover over the property (e.g. "Lamp Energy" or "Y Location") or properties ("Rotation")
you wish to add drivers to. We'll refer to this as the "destination"
2) Ctrl-D to active the new "Add Drivers" eyedropper
3) Click on the property you want to use as the source/target. The property under the
mouse will be used to drive the property you invoked Ctrl-D on.
For example, to drive the X, Y, and Z location of the Cube using the Y Location of the Lamp,
hover over any of the X/Y/Z location buttons, hit Ctrl-D, then click on the Y-Location
button of the Lamp object. Drivers will be added to the X, Y, and Z Location properties
of the Cube; each driver will have a single variable, which uses the Y-Location Transform
Channel of the Lamp.
Tips:
- Transform properties will automatically create "Transform Channel" driver variables.
Everything else will use "Single Property" ones
- Due to the way that Blender's UI Context works, you'll need two Properties Panel instances
open (and to have pinned one of the two to show the properties for the unselected
object). It's slightly clunky, but necessary for implementing a workflow like this,
as the UI cannot be manipulated while using eyedroppers to pick data.
- The eyedropper operator implemented here actually has three modes of operation.
1) The "1-N" (one to many) mode is the default used for Ctrl-D, and "Add Driver to All"
in the RMB Menu. This is the behaviour described above.
2) There's also a "1-1" (one to one) mode that is used for the "Add Single Driver" in the
RMB Menu.
3) Finally, there's the "N-N" mode (many to many), which isn't currently exposed.
The point of this is to allow mapping XYZ to XYZ elementwise (i.e. direct copying)
which is useful for things like locations, rotations, scaling, and colours.
Implementation Notes:
- The bulk of the driver adding logic is in editors/animation/drivers.c, where most of
the Driver UI operators and tools are defined
- The property eyedropper code is in interface_eyedropper.c along with all the other
eyedroppers (even though they don't share much actual code in common). However, this
turns out to be necessary, as we can't get access to many of the low-level buttons API's
otherwise.
Todo:
- It may be necessary to restore a way to access the old behaviour (i.e. "manual setup")
in case it is not practical to immediately pick a property.
- Other things to investigate here include extra hotkeys (e.g. Ctrl-Shift-D for Add Single?),
and to expose the N-N mode.
- Other things we could try include interactively applying scaling factors, picking
multiple targets (e.g. for location difference and rotation difference drivers),
and/or other ways of using these property picking methods.
2016-03-26 17:55:42 +13:00
|
|
|
WM_operatortype_append(UI_OT_eyedropper_driver);
|
2019-10-11 13:28:22 +02:00
|
|
|
WM_operatortype_append(UI_OT_eyedropper_gpencil_color);
|
2009-12-17 10:47:55 +00:00
|
|
|
}
|
2016-02-29 17:40:19 +01:00
|
|
|
|
|
|
|
|
void ED_keymap_ui(wmKeyConfig *keyconf)
|
|
|
|
|
{
|
2023-09-14 13:32:42 +10:00
|
|
|
WM_keymap_ensure(keyconf, "User Interface", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
2016-09-21 20:05:49 +02:00
|
|
|
|
2016-02-29 18:46:20 +01:00
|
|
|
eyedropper_modal_keymap(keyconf);
|
2017-12-12 10:19:55 +11:00
|
|
|
eyedropper_colorband_modal_keymap(keyconf);
|
2016-02-29 17:40:19 +01:00
|
|
|
}
|
2019-03-23 12:07:05 +11:00
|
|
|
|
|
|
|
|
/** \} */
|