Blender V4.5
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.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
30#include "UI_interface.hh"
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,
99 wmOperatorCallContext opcontext)
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) && (opcontext == umi_op->opcontext) && ok_idprop &&
109 ok_prop_enum)
110 {
111 return umi_op;
112 }
113 }
114 }
115 return nullptr;
116}
117
119{
120 LISTBASE_FOREACH (bUserMenuItem *, umi, lb) {
121 if (umi->type == USER_MENU_TYPE_MENU) {
122 bUserMenuItem_Menu *umi_mt = (bUserMenuItem_Menu *)umi;
123 if (STREQ(mt->idname, umi_mt->mt_idname)) {
124 return umi_mt;
125 }
126 }
127 }
128 return nullptr;
129}
130
132 const char *context_data_path,
133 const char *prop_id,
134 int prop_index)
135{
136 LISTBASE_FOREACH (bUserMenuItem *, umi, lb) {
137 if (umi->type == USER_MENU_TYPE_PROP) {
138 bUserMenuItem_Prop *umi_pr = (bUserMenuItem_Prop *)umi;
139 if (STREQ(context_data_path, umi_pr->context_data_path) && STREQ(prop_id, umi_pr->prop_id) &&
140 (prop_index == umi_pr->prop_index))
141 {
142 return umi_pr;
143 }
144 }
145 }
146 return nullptr;
147}
148
150 const char *ui_name,
151 const wmOperatorType *ot,
152 const IDProperty *prop,
153 const char *op_prop_enum,
154 wmOperatorCallContext opcontext)
155{
158 umi_op->opcontext = opcontext;
159 if (!STREQ(ui_name, ot->name)) {
160 STRNCPY(umi_op->item.ui_name, ui_name);
161 }
162 STRNCPY(umi_op->op_idname, ot->idname);
163 STRNCPY(umi_op->op_prop_enum, op_prop_enum);
164 umi_op->prop = prop ? IDP_CopyProperty(prop) : nullptr;
165}
166
167void ED_screen_user_menu_item_add_menu(ListBase *lb, const char *ui_name, const MenuType *mt)
168{
171 if (!STREQ(ui_name, mt->label)) {
172 STRNCPY(umi_mt->item.ui_name, ui_name);
173 }
174 STRNCPY(umi_mt->mt_idname, mt->idname);
175}
176
178 const char *ui_name,
179 const char *context_data_path,
180 const char *prop_id,
181 int prop_index)
182{
185 STRNCPY(umi_pr->item.ui_name, ui_name);
186 STRNCPY(umi_pr->context_data_path, context_data_path);
187 STRNCPY(umi_pr->prop_id, prop_id);
188 umi_pr->prop_index = prop_index;
189}
190
196
198
199/* -------------------------------------------------------------------- */
202
203static void screen_user_menu_draw(const bContext *C, Menu *menu)
204{
205 using namespace blender;
206 /* Enable when we have the ability to edit menus. */
207 const bool show_missing = false;
208 char label[512];
209
210 uint um_array_len;
211 bUserMenu **um_array = ED_screen_user_menus_find(C, &um_array_len);
212 bool is_empty = true;
213 for (int um_index = 0; um_index < um_array_len; um_index++) {
214 bUserMenu *um = um_array[um_index];
215 if (um == nullptr) {
216 continue;
217 }
218 LISTBASE_FOREACH (bUserMenuItem *, umi, &um->items) {
219 std::optional<StringRefNull> ui_name = umi->ui_name[0] ?
220 std::make_optional<StringRefNull>(umi->ui_name) :
221 std::nullopt;
222 if (umi->type == USER_MENU_TYPE_OPERATOR) {
223 bUserMenuItem_Op *umi_op = (bUserMenuItem_Op *)umi;
224 if (wmOperatorType *ot = WM_operatortype_find(umi_op->op_idname, false)) {
225 if (ui_name) {
226 ui_name = CTX_IFACE_(ot->translation_context, ui_name->c_str());
227 }
228 if (umi_op->op_prop_enum[0] == '\0') {
229 PointerRNA ptr = menu->layout->op(
230 ot, ui_name, ICON_NONE, wmOperatorCallContext(umi_op->opcontext), UI_ITEM_NONE);
231 if (umi_op->prop) {
232 IDP_CopyPropertyContent(ptr.data_as<IDProperty>(), umi_op->prop);
233 }
234 }
235 else {
236 /* umi_op->prop could be used to set other properties but it's currently unsupported.
237 */
239 menu->layout, C, ot, umi_op->op_prop_enum, ui_name, ICON_NONE, nullptr);
240 }
241 is_empty = false;
242 }
243 else {
244 if (show_missing) {
245 SNPRINTF(label, RPT_("Missing: %s"), umi_op->op_idname);
246 menu->layout->label(label, ICON_NONE);
247 }
248 }
249 }
250 else if (umi->type == USER_MENU_TYPE_MENU) {
251 bUserMenuItem_Menu *umi_mt = (bUserMenuItem_Menu *)umi;
252 MenuType *mt = WM_menutype_find(umi_mt->mt_idname, false);
253 if (mt != nullptr) {
254 menu->layout->menu(mt, ui_name, ICON_NONE);
255 is_empty = false;
256 }
257 else {
258 if (show_missing) {
259 SNPRINTF(label, RPT_("Missing: %s"), umi_mt->mt_idname);
260 menu->layout->label(label, ICON_NONE);
261 }
262 }
263 }
264 else if (umi->type == USER_MENU_TYPE_PROP) {
265 bUserMenuItem_Prop *umi_pr = (bUserMenuItem_Prop *)umi;
266
267 char *data_path = strchr(umi_pr->context_data_path, '.');
268 if (data_path) {
269 *data_path = '\0';
270 }
272 if (ptr.type == nullptr) {
273 PointerRNA ctx_ptr = RNA_pointer_create_discrete(nullptr, &RNA_Context, (void *)C);
274 if (!RNA_path_resolve_full(&ctx_ptr, umi_pr->context_data_path, &ptr, nullptr, nullptr))
275 {
276 ptr.type = nullptr;
277 }
278 }
279 if (data_path) {
280 *data_path = '.';
281 data_path += 1;
282 }
283
284 bool ok = false;
285 if (ptr.type != nullptr) {
286 PropertyRNA *prop = nullptr;
287 PointerRNA prop_ptr = ptr;
288 if ((data_path == nullptr) ||
289 RNA_path_resolve_full(&ptr, data_path, &prop_ptr, nullptr, nullptr))
290 {
291 prop = RNA_struct_find_property(&prop_ptr, umi_pr->prop_id);
292 if (prop) {
293 ok = true;
294 menu->layout->prop(
295 &prop_ptr, prop, umi_pr->prop_index, 0, UI_ITEM_NONE, ui_name, ICON_NONE);
296 is_empty = false;
297 }
298 }
299 }
300 if (!ok) {
301 if (show_missing) {
302 SNPRINTF(label, RPT_("Missing: %s.%s"), umi_pr->context_data_path, umi_pr->prop_id);
303 menu->layout->label(label, ICON_NONE);
304 }
305 }
306 }
307 else if (umi->type == USER_MENU_TYPE_SEP) {
308 menu->layout->separator();
309 }
310 }
311 }
312 if (um_array) {
313 MEM_freeN(um_array);
314 }
315
316 if (is_empty) {
317 menu->layout->label(RPT_("No menu items found"), ICON_NONE);
318 menu->layout->label(RPT_("Right click on buttons to add them to this menu"), ICON_NONE);
319 }
320}
321
323{
324 MenuType *mt = MEM_callocN<MenuType>(__func__);
325 STRNCPY(mt->idname, "SCREEN_MT_user_menu");
326 STRNCPY(mt->label, N_("Quick Favorites"));
329 WM_menutype_add(mt);
330}
331
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:878
IDProperty * IDP_CopyProperty(const IDProperty *prop) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition idprop.cc:873
bool IDP_EqualsProperties(const IDProperty *prop1, const IDProperty *prop2) ATTR_WARN_UNUSED_RESULT
Definition idprop.cc:996
#define LISTBASE_FOREACH(type, var, list)
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:599
char * STRNCPY(char(&dst)[N], const char *src)
Definition BLI_string.h:688
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
void uiItemMenuEnumFullO_ptr(uiLayout *layout, const bContext *C, wmOperatorType *ot, blender::StringRefNull propname, std::optional< blender::StringRefNull > name, int icon, PointerRNA *r_opptr)
#define UI_ITEM_NONE
wmOperatorCallContext
Definition WM_types.hh:236
#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
bUserMenu * ED_screen_user_menu_ensure(bContext *C)
static void screen_user_menu_draw(const bContext *C, Menu *menu)
bUserMenu ** ED_screen_user_menus_find(const bContext *C, uint *r_len)
bUserMenuItem_Op * ED_screen_user_menu_item_find_operator(ListBase *lb, const wmOperatorType *ot, IDProperty *prop, const char *op_prop_enum, wmOperatorCallContext opcontext)
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_operator(ListBase *lb, const char *ui_name, const wmOperatorType *ot, const IDProperty *prop, const char *op_prop_enum, wmOperatorCallContext opcontext)
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(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, wmOperatorCallContext context, eUI_Item_Flag flag)
void label(blender::StringRef name, int icon)
void separator(float factor=1.0f, LayoutSeparatorType type=LayoutSeparatorType::Auto)
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:4227
wmOperatorType * ot
Definition wm_files.cc:4226
MenuType * WM_menutype_find(const StringRef idname, bool quiet)
bool WM_menutype_add(MenuType *mt)
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)