Blender V5.0
screen_user_menu.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2009 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cfloat>
10#include <cstring>
11
12#include "MEM_guardedalloc.h"
13
14#include "BLI_listbase.h"
15#include "BLI_string_utf8.h"
16#include "BLI_utildefines.h"
17
18#include "BLT_translation.hh"
19
21#include "BKE_context.hh"
22#include "BKE_idprop.hh"
23#include "BKE_screen.hh"
24
25#include "WM_api.hh"
26#include "WM_types.hh"
27
28#include "ED_screen.hh"
29
31#include "UI_resources.hh"
32
33#include "RNA_access.hh"
34#include "RNA_path.hh"
35#include "RNA_prototypes.hh"
36
37/* -------------------------------------------------------------------- */
40
41static const char *screen_menu_context_string(const bContext *C, const SpaceLink *sl)
42{
43 if (sl->spacetype == SPACE_NODE) {
44 const SpaceNode *snode = (const SpaceNode *)sl;
45 return snode->tree_idname;
46 }
47 return CTX_data_mode_string(C);
48}
49
51
52/* -------------------------------------------------------------------- */
55
57{
59
60 if (sl == nullptr) {
61 *r_len = 0;
62 return nullptr;
63 }
64
65 const char *context_mode = CTX_data_mode_string(C);
66 const char *context = screen_menu_context_string(C, sl);
67 uint array_len = 3;
68 bUserMenu **um_array = static_cast<bUserMenu **>(
69 MEM_calloc_arrayN(array_len, sizeof(*um_array), __func__));
70 um_array[0] = BKE_blender_user_menu_find(&U.user_menus, sl->spacetype, context);
71 um_array[1] = (sl->spacetype != SPACE_TOPBAR) ?
72 BKE_blender_user_menu_find(&U.user_menus, SPACE_TOPBAR, context_mode) :
73 nullptr;
74 um_array[2] = (sl->spacetype == SPACE_VIEW3D) ?
75 BKE_blender_user_menu_find(&U.user_menus, SPACE_PROPERTIES, context_mode) :
76 nullptr;
77
78 *r_len = array_len;
79 return um_array;
80}
81
83{
85 const char *context = screen_menu_context_string(C, sl);
86 return BKE_blender_user_menu_ensure(&U.user_menus, sl->spacetype, context);
87}
88
90
91/* -------------------------------------------------------------------- */
94
96 const wmOperatorType *ot,
97 IDProperty *prop,
98 const char *op_prop_enum,
100{
101 LISTBASE_FOREACH (bUserMenuItem *, umi, lb) {
102 if (umi->type == USER_MENU_TYPE_OPERATOR) {
103 bUserMenuItem_Op *umi_op = (bUserMenuItem_Op *)umi;
104 const bool ok_idprop = prop ? IDP_EqualsProperties(prop, umi_op->prop) : true;
105 const bool ok_prop_enum = (umi_op->op_prop_enum[0] != '\0') ?
106 STREQ(umi_op->op_prop_enum, op_prop_enum) :
107 true;
108 if (STREQ(ot->idname, umi_op->op_idname) &&
109 (opcontext == blender::wm::OpCallContext(umi_op->opcontext)) && ok_idprop &&
110 ok_prop_enum)
111 {
112 return umi_op;
113 }
114 }
115 }
116 return nullptr;
117}
118
120{
121 LISTBASE_FOREACH (bUserMenuItem *, umi, lb) {
122 if (umi->type == USER_MENU_TYPE_MENU) {
123 bUserMenuItem_Menu *umi_mt = (bUserMenuItem_Menu *)umi;
124 if (STREQ(mt->idname, umi_mt->mt_idname)) {
125 return umi_mt;
126 }
127 }
128 }
129 return nullptr;
130}
131
133 const char *context_data_path,
134 const char *prop_id,
135 int prop_index)
136{
137 LISTBASE_FOREACH (bUserMenuItem *, umi, lb) {
138 if (umi->type == USER_MENU_TYPE_PROP) {
139 bUserMenuItem_Prop *umi_pr = (bUserMenuItem_Prop *)umi;
140 if (STREQ(context_data_path, umi_pr->context_data_path) && STREQ(prop_id, umi_pr->prop_id) &&
141 (prop_index == umi_pr->prop_index))
142 {
143 return umi_pr;
144 }
145 }
146 }
147 return nullptr;
148}
149
151 const char *ui_name,
152 const wmOperatorType *ot,
153 const IDProperty *prop,
154 const char *op_prop_enum,
156{
159 umi_op->opcontext = int8_t(opcontext);
160 if (!STREQ(ui_name, ot->name)) {
161 STRNCPY_UTF8(umi_op->item.ui_name, ui_name);
162 }
163 STRNCPY_UTF8(umi_op->op_idname, ot->idname);
164 STRNCPY_UTF8(umi_op->op_prop_enum, op_prop_enum);
165 umi_op->prop = prop ? IDP_CopyProperty(prop) : nullptr;
166}
167
168void ED_screen_user_menu_item_add_menu(ListBase *lb, const char *ui_name, const MenuType *mt)
169{
172 if (!STREQ(ui_name, mt->label)) {
173 STRNCPY_UTF8(umi_mt->item.ui_name, ui_name);
174 }
175 STRNCPY_UTF8(umi_mt->mt_idname, mt->idname);
176}
177
179 const char *ui_name,
180 const char *context_data_path,
181 const char *prop_id,
182 int prop_index)
183{
186 STRNCPY_UTF8(umi_pr->item.ui_name, ui_name);
187 STRNCPY_UTF8(umi_pr->context_data_path, context_data_path);
188 STRNCPY_UTF8(umi_pr->prop_id, prop_id);
189 umi_pr->prop_index = prop_index;
190}
191
197
199
200/* -------------------------------------------------------------------- */
203
204static void screen_user_menu_draw(const bContext *C, Menu *menu)
205{
206 using namespace blender;
207 /* Enable when we have the ability to edit menus. */
208 const bool show_missing = false;
209 char label[512];
210
211 uint um_array_len;
212 bUserMenu **um_array = ED_screen_user_menus_find(C, &um_array_len);
213 bool is_empty = true;
214 for (int um_index = 0; um_index < um_array_len; um_index++) {
215 bUserMenu *um = um_array[um_index];
216 if (um == nullptr) {
217 continue;
218 }
219 LISTBASE_FOREACH (bUserMenuItem *, umi, &um->items) {
220 std::optional<StringRefNull> ui_name = umi->ui_name[0] ?
221 std::make_optional<StringRefNull>(umi->ui_name) :
222 std::nullopt;
223 if (umi->type == USER_MENU_TYPE_OPERATOR) {
224 bUserMenuItem_Op *umi_op = (bUserMenuItem_Op *)umi;
225 if (wmOperatorType *ot = WM_operatortype_find(umi_op->op_idname, false)) {
226 if (ui_name) {
227 ui_name = CTX_IFACE_(ot->translation_context, ui_name->c_str());
228 }
229 if (umi_op->op_prop_enum[0] == '\0') {
230 PointerRNA ptr = menu->layout->op(ot,
231 ui_name,
232 ICON_NONE,
235 if (umi_op->prop) {
236 IDP_CopyPropertyContent(ptr.data_as<IDProperty>(), umi_op->prop);
237 }
238 }
239 else {
240 /* umi_op->prop could be used to set other properties but it's currently unsupported.
241 */
242 menu->layout->op_menu_enum(C, ot, umi_op->op_prop_enum, ui_name, ICON_NONE);
243 }
244 is_empty = false;
245 }
246 else {
247 if (show_missing) {
248 SNPRINTF_UTF8(label, RPT_("Missing: %s"), umi_op->op_idname);
249 menu->layout->label(label, ICON_NONE);
250 }
251 }
252 }
253 else if (umi->type == USER_MENU_TYPE_MENU) {
254 bUserMenuItem_Menu *umi_mt = (bUserMenuItem_Menu *)umi;
255 MenuType *mt = WM_menutype_find(umi_mt->mt_idname, false);
256 if (mt != nullptr) {
257 menu->layout->menu(mt, ui_name, ICON_NONE);
258 is_empty = false;
259 }
260 else {
261 if (show_missing) {
262 SNPRINTF_UTF8(label, RPT_("Missing: %s"), umi_mt->mt_idname);
263 menu->layout->label(label, ICON_NONE);
264 }
265 }
266 }
267 else if (umi->type == USER_MENU_TYPE_PROP) {
268 bUserMenuItem_Prop *umi_pr = (bUserMenuItem_Prop *)umi;
269
270 char *data_path = strchr(umi_pr->context_data_path, '.');
271 if (data_path) {
272 *data_path = '\0';
273 }
275 if (ptr.type == nullptr) {
276 PointerRNA ctx_ptr = RNA_pointer_create_discrete(nullptr, &RNA_Context, (void *)C);
277 if (!RNA_path_resolve_full(&ctx_ptr, umi_pr->context_data_path, &ptr, nullptr, nullptr))
278 {
279 ptr.type = nullptr;
280 }
281 }
282 if (data_path) {
283 *data_path = '.';
284 data_path += 1;
285 }
286
287 bool ok = false;
288 if (ptr.type != nullptr) {
289 PropertyRNA *prop = nullptr;
290 PointerRNA prop_ptr = ptr;
291 if ((data_path == nullptr) ||
292 RNA_path_resolve_full(&ptr, data_path, &prop_ptr, nullptr, nullptr))
293 {
294 prop = RNA_struct_find_property(&prop_ptr, umi_pr->prop_id);
295 if (prop) {
296 ok = true;
297 menu->layout->prop(
298 &prop_ptr, prop, umi_pr->prop_index, 0, UI_ITEM_NONE, ui_name, ICON_NONE);
299 is_empty = false;
300 }
301 }
302 }
303 if (!ok) {
304 if (show_missing) {
306 label, RPT_("Missing: %s.%s"), umi_pr->context_data_path, umi_pr->prop_id);
307 menu->layout->label(label, ICON_NONE);
308 }
309 }
310 }
311 else if (umi->type == USER_MENU_TYPE_SEP) {
312 menu->layout->separator();
313 }
314 }
315 }
316 if (um_array) {
317 MEM_freeN(um_array);
318 }
319
320 if (is_empty) {
321 menu->layout->label(RPT_("No menu items found"), ICON_NONE);
322 menu->layout->label(RPT_("Right click on buttons to add them to this menu"), ICON_NONE);
323 }
324}
325
327{
328 MenuType *mt = MEM_callocN<MenuType>(__func__);
329 STRNCPY_UTF8(mt->idname, "SCREEN_MT_user_menu");
330 STRNCPY_UTF8(mt->label, N_("Quick Favorites"));
333 WM_menutype_add(mt);
334}
335
bUserMenu * BKE_blender_user_menu_find(ListBase *lb, char space_type, const char *context)
bUserMenu * BKE_blender_user_menu_ensure(ListBase *lb, char space_type, const char *context)
void BKE_blender_user_menu_item_free(bUserMenuItem *umi)
bUserMenuItem * BKE_blender_user_menu_item_add(ListBase *lb, int type)
PointerRNA CTX_data_pointer_get(const bContext *C, const char *member)
const char * CTX_data_mode_string(const bContext *C)
SpaceLink * CTX_wm_space_data(const bContext *C)
void IDP_CopyPropertyContent(IDProperty *dst, const IDProperty *src) ATTR_NONNULL()
Definition idprop.cc:868
IDProperty * IDP_CopyProperty(const IDProperty *prop) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition idprop.cc:863
bool IDP_EqualsProperties(const IDProperty *prop1, const IDProperty *prop2) ATTR_WARN_UNUSED_RESULT
Definition idprop.cc:1004
#define LISTBASE_FOREACH(type, var, list)
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
#define SNPRINTF_UTF8(dst, format,...)
#define STRNCPY_UTF8(dst, src)
unsigned int uint
#define STREQ(a, b)
#define RPT_(msgid)
#define CTX_IFACE_(context, msgid)
#define BLT_I18NCONTEXT_DEFAULT_BPYRNA
@ SPACE_TOPBAR
@ SPACE_NODE
@ SPACE_PROPERTIES
@ SPACE_VIEW3D
@ USER_MENU_TYPE_OPERATOR
@ USER_MENU_TYPE_SEP
@ USER_MENU_TYPE_PROP
@ USER_MENU_TYPE_MENU
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
#define UI_ITEM_NONE
#define U
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
bool RNA_path_resolve_full(const PointerRNA *ptr, const char *path, PointerRNA *r_ptr, PropertyRNA **r_prop, int *r_index)
Definition rna_path.cc:544
void ED_screen_user_menu_item_add_operator(ListBase *lb, const char *ui_name, const wmOperatorType *ot, const IDProperty *prop, const char *op_prop_enum, blender::wm::OpCallContext opcontext)
bUserMenu * ED_screen_user_menu_ensure(bContext *C)
bUserMenuItem_Op * ED_screen_user_menu_item_find_operator(ListBase *lb, const wmOperatorType *ot, IDProperty *prop, const char *op_prop_enum, blender::wm::OpCallContext opcontext)
static void screen_user_menu_draw(const bContext *C, Menu *menu)
bUserMenu ** ED_screen_user_menus_find(const bContext *C, uint *r_len)
bUserMenuItem_Prop * ED_screen_user_menu_item_find_prop(ListBase *lb, const char *context_data_path, const char *prop_id, int prop_index)
static const char * screen_menu_context_string(const bContext *C, const SpaceLink *sl)
void ED_screen_user_menu_register()
void ED_screen_user_menu_item_add_prop(ListBase *lb, const char *ui_name, const char *context_data_path, const char *prop_id, int prop_index)
void ED_screen_user_menu_item_add_menu(ListBase *lb, const char *ui_name, const MenuType *mt)
void ED_screen_user_menu_item_remove(ListBase *lb, bUserMenuItem *umi)
bUserMenuItem_Menu * ED_screen_user_menu_item_find_menu(ListBase *lb, const MenuType *mt)
char label[BKE_ST_MAXNAME]
char idname[BKE_ST_MAXNAME]
void(* draw)(const bContext *C, Menu *menu)
char translation_context[BKE_ST_MAXNAME]
uiLayout * layout
char tree_idname[64]
struct IDProperty * prop
PointerRNA op_menu_enum(const bContext *C, wmOperatorType *ot, blender::StringRefNull propname, std::optional< blender::StringRefNull > name, int icon)
void label(blender::StringRef name, int icon)
void separator(float factor=1.0f, LayoutSeparatorType type=LayoutSeparatorType::Auto)
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, blender::wm::OpCallContext context, eUI_Item_Flag flag)
void menu(MenuType *mt, std::optional< blender::StringRef > name, int icon)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
#define N_(msgid)
PointerRNA * ptr
Definition wm_files.cc:4238
wmOperatorType * ot
Definition wm_files.cc:4237
MenuType * WM_menutype_find(const StringRef idname, bool quiet)
bool WM_menutype_add(MenuType *mt)
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)