Blender V4.3
wm_operator_type.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11#include "MEM_guardedalloc.h"
12
13#include "CLG_log.h"
14
15#include "DNA_ID.h"
16#include "DNA_scene_types.h"
17#include "DNA_screen_types.h"
18#include "DNA_userdef_types.h"
20
21#include "BLT_translation.hh"
22
23#include "BLI_blenlib.h"
24#include "BLI_map.hh"
25#include "BLI_utildefines.h"
26
27#include "BKE_context.hh"
28#include "BKE_idprop.hh"
29
30#include "RNA_access.hh"
31#include "RNA_define.hh"
32#include "RNA_enum_types.hh"
33#include "RNA_prototypes.hh"
34
35#include "WM_api.hh"
36#include "WM_types.hh"
37
38#include "wm.hh"
39#include "wm_event_system.hh"
40
41#define UNDOCUMENTED_OPERATOR_TIP N_("(undocumented operator)")
42
44
45/* -------------------------------------------------------------------- */
50{
51 static wmOperatorTypeMap map = []() {
53 /* Reserve size is set based on blender default setup. */
54 map.reserve(2048);
55 return map;
56 }();
57 return map;
58}
59
64
66static int ot_prop_basic_count = -1;
67
68wmOperatorType *WM_operatortype_find(const char *idname, bool quiet)
69{
70 if (idname[0]) {
72
73 /* Needed to support python style names without the `_OT_` syntax. */
74 char idname_bl[OP_MAX_TYPENAME];
75 WM_operator_bl_idname(idname_bl, idname);
76
77 ot = get_operators_map().lookup_default_as(idname_bl, nullptr);
78 if (ot) {
79 return ot;
80 }
81
82 if (!quiet) {
84 WM_LOG_OPERATORS, 0, "search for unknown operator '%s', '%s'\n", idname_bl, idname);
85 }
86 }
87 else {
88 if (!quiet) {
89 CLOG_INFO(WM_LOG_OPERATORS, 0, "search for empty operator");
90 }
91 }
92
93 return nullptr;
94}
95
96/* -------------------------------------------------------------------- */
101{
102 wmOperatorType *ot = static_cast<wmOperatorType *>(
103 MEM_callocN(sizeof(wmOperatorType), "operatortype"));
104
106
107 ot->srna = RNA_def_struct_ptr(&BLENDER_RNA, "", &RNA_OperatorProperties);
109 /* Set the default i18n context now, so that opfunc can redefine it if needed! */
113
114 return ot;
115}
117{
118 if (ot->name == nullptr) {
119 CLOG_ERROR(WM_LOG_OPERATORS, "Operator '%s' has no name property", ot->idname);
120 }
121 BLI_assert((ot->description == nullptr) || (ot->description[0]));
122
123 /* Allow calling _begin without _end in operatortype creation. */
125
126 /* XXX All ops should have a description but for now allow them not to. */
130
133}
134
135/* All ops in 1 list (for time being... needs evaluation later). */
136
143
144void WM_operatortype_append_ptr(void (*opfunc)(wmOperatorType *, void *), void *userdata)
145{
147 opfunc(ot, userdata);
149}
150
173
174bool WM_operatortype_remove(const char *idname)
175{
176 wmOperatorType *ot = WM_operatortype_find(idname, false);
177
178 if (ot == nullptr) {
179 return false;
180 }
181
183
184 return true;
185}
186
188{
189 if (ot->last_properties) {
191 }
192
193 if (ot->macro.first) {
195 }
196
197 if (ot->rna_ext.srna) {
198 /* A Python operator, allocates its own string. */
199 MEM_freeN((void *)ot->idname);
200 }
201
202 MEM_freeN(ot);
203}
204
206{
208 for (wmOperatorType *ot : map.values()) {
210 }
211 map.clear();
212}
213
215{
216 if (ot_prop_basic_count == -1) {
217 /* Don't do anything if _begin was called before, but not _end. */
219 }
220}
221
223{
224 PointerRNA struct_ptr;
225 int counter = 0;
226
227 if (ot_prop_basic_count == -1) {
228 /* WM_operatortype_props_advanced_begin was not called. Don't do anything. */
229 return;
230 }
231
233
234 RNA_STRUCT_BEGIN (&struct_ptr, prop) {
235 counter++;
236 if (counter > ot_prop_basic_count) {
238 }
239 }
241
243}
244
254
256 const bContext * /*C*/,
257 PointerRNA * /*ptr*/,
258 PropertyRNA * /*prop*/,
259 const char * /*edit_text*/,
261{
263 char idname_py[OP_MAX_TYPENAME];
264 WM_operator_py_idname(idname_py, ot->idname);
265
266 StringPropertySearchVisitParams visit_params{};
267 visit_params.text = idname_py;
268 visit_params.info = ot->name;
269 visit_fn(visit_params);
270 }
271}
272
275/* -------------------------------------------------------------------- */
279struct MacroData {
281};
282
284{
285 if (op->customdata == nullptr) {
286 op->customdata = MEM_callocN(sizeof(MacroData), "MacroData");
287 }
288}
289
290static int wm_macro_end(wmOperator *op, int retval)
291{
292 if (retval & OPERATOR_CANCELLED) {
293 MacroData *md = static_cast<MacroData *>(op->customdata);
294
295 if (md->retval & OPERATOR_FINISHED) {
296 retval |= OPERATOR_FINISHED;
297 retval &= ~OPERATOR_CANCELLED;
298 }
299 }
300
301 /* If modal is ending, free custom data. */
302 if (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED)) {
303 if (op->customdata) {
305 op->customdata = nullptr;
306 }
307 }
308
309 return retval;
310}
311
312/* Macro exec only runs exec calls. */
314{
315 int retval = OPERATOR_FINISHED;
316 const int op_inherited_flag = op->flag & (OP_IS_REPEAT | OP_IS_REPEAT_LAST);
317
318 wm_macro_start(op);
319
320 LISTBASE_FOREACH (wmOperator *, opm, &op->macro) {
321 if (opm->type->exec == nullptr) {
322 CLOG_WARN(WM_LOG_OPERATORS, "'%s' can't exec macro", opm->type->idname);
323 continue;
324 }
325
326 opm->flag |= op_inherited_flag;
327 retval = opm->type->exec(C, opm);
328 opm->flag &= ~op_inherited_flag;
329
330 OPERATOR_RETVAL_CHECK(retval);
331
332 if (retval & OPERATOR_FINISHED) {
333 MacroData *md = static_cast<MacroData *>(op->customdata);
334 md->retval = OPERATOR_FINISHED; /* Keep in mind that at least one operator finished. */
335 }
336 else {
337 break; /* Operator didn't finish, end macro. */
338 }
339 }
340
341 return wm_macro_end(op, retval);
342}
343
345 wmOperator *op,
346 const wmEvent *event,
347 wmOperator *opm)
348{
349 int retval = OPERATOR_FINISHED;
350 const int op_inherited_flag = op->flag & (OP_IS_REPEAT | OP_IS_REPEAT_LAST);
351
352 /* Start from operator received as argument. */
353 for (; opm; opm = opm->next) {
354
355 opm->flag |= op_inherited_flag;
356 if (opm->type->invoke) {
357 retval = opm->type->invoke(C, opm, event);
358 }
359 else if (opm->type->exec) {
360 retval = opm->type->exec(C, opm);
361 }
362 opm->flag &= ~op_inherited_flag;
363
364 OPERATOR_RETVAL_CHECK(retval);
365
367
368 if (retval & OPERATOR_FINISHED) {
369 MacroData *md = static_cast<MacroData *>(op->customdata);
370 md->retval = OPERATOR_FINISHED; /* Keep in mind that at least one operator finished. */
371 }
372 else {
373 break; /* Operator didn't finish, end macro. */
374 }
375 }
376
377 return wm_macro_end(op, retval);
378}
379
380static int wm_macro_invoke(bContext *C, wmOperator *op, const wmEvent *event)
381{
382 wm_macro_start(op);
383 return wm_macro_invoke_internal(C, op, event, static_cast<wmOperator *>(op->macro.first));
384}
385
386static int wm_macro_modal(bContext *C, wmOperator *op, const wmEvent *event)
387{
388 wmOperator *opm = op->opm;
389 int retval = OPERATOR_FINISHED;
390
391 if (opm == nullptr) {
392 CLOG_ERROR(WM_LOG_OPERATORS, "macro error, calling nullptr modal()");
393 }
394 else {
395 retval = opm->type->modal(C, opm, event);
396 OPERATOR_RETVAL_CHECK(retval);
397
398 /* If we're halfway through using a tool and cancel it, clear the options, see: #37149. */
399 if (retval & OPERATOR_CANCELLED) {
401 }
402
403 /* If this one is done but it's not the last operator in the macro. */
404 if ((retval & OPERATOR_FINISHED) && opm->next) {
405 MacroData *md = static_cast<MacroData *>(op->customdata);
406
407 md->retval = OPERATOR_FINISHED; /* Keep in mind that at least one operator finished. */
408
409 retval = wm_macro_invoke_internal(C, op, event, opm->next);
410
411 /* If new operator is modal and also added its own handler. */
412 if (retval & OPERATOR_RUNNING_MODAL && op->opm != opm) {
413 wmWindow *win = CTX_wm_window(C);
414 wmEventHandler_Op *handler;
415
416 handler = static_cast<wmEventHandler_Op *>(
418 if (handler) {
419 BLI_remlink(&win->modalhandlers, handler);
420 wm_event_free_handler(&handler->head);
421 }
422
423 /* If operator is blocking, grab cursor.
424 * This may end up grabbing twice, but we don't care. */
425 if (op->opm->type->flag & OPTYPE_BLOCKING) {
427 const rcti *wrap_region = nullptr;
428
429 if ((op->opm->flag & OP_IS_MODAL_GRAB_CURSOR) ||
431 {
433 }
434 else if (op->opm->type->flag & OPTYPE_GRAB_CURSOR_X) {
436 }
437 else if (op->opm->type->flag & OPTYPE_GRAB_CURSOR_Y) {
439 }
440
441 if (wrap) {
442 ARegion *region = CTX_wm_region(C);
443 if (region) {
444 wrap_region = &region->winrct;
445 }
446 }
447
448 WM_cursor_grab_enable(win, eWM_CursorWrapAxis(wrap), wrap_region, false);
449 }
450 }
451 }
452 }
453
454 return wm_macro_end(op, retval);
455}
456
458{
459 /* Call cancel on the current modal operator, if any. */
460 if (op->opm && op->opm->type->cancel) {
461 op->opm->type->cancel(C, op->opm);
462 }
463
465}
466
468 const char *name,
469 const char *description,
470 int flag)
471{
473 const char *i18n_context;
474
475 if (WM_operatortype_find(idname, true)) {
476 CLOG_ERROR(WM_LOG_OPERATORS, "operator %s exists, cannot create macro", idname);
477 return nullptr;
478 }
479
480 ot = static_cast<wmOperatorType *>(MEM_callocN(sizeof(wmOperatorType), "operatortype"));
481 ot->srna = RNA_def_struct_ptr(&BLENDER_RNA, "", &RNA_OperatorProperties);
482
483 ot->idname = idname;
484 ot->name = name;
485 ot->description = description;
487
492 ot->poll = nullptr;
493
494 /* XXX All ops should have a description but for now allow them not to. */
495 BLI_assert((ot->description == nullptr) || (ot->description[0]));
496
500 /* Use i18n context from rna_ext.srna if possible (py operators). */
504 ot->translation_context = i18n_context;
505
508
509 return ot;
510}
511
512void WM_operatortype_append_macro_ptr(void (*opfunc)(wmOperatorType *ot, void *userdata),
513 void *userdata)
514{
516
517 ot = static_cast<wmOperatorType *>(MEM_callocN(sizeof(wmOperatorType), "operatortype"));
518 ot->srna = RNA_def_struct_ptr(&BLENDER_RNA, "", &RNA_OperatorProperties);
519
525 ot->poll = nullptr;
526
527 /* XXX All ops should have a description but for now allow them not to. */
528 BLI_assert((ot->description == nullptr) || (ot->description[0]));
529
530 /* Set the default i18n context now, so that opfunc can redefine it if needed! */
533 opfunc(ot, userdata);
534
538
541}
542
544{
545 wmOperatorTypeMacro *otmacro = static_cast<wmOperatorTypeMacro *>(
546 MEM_callocN(sizeof(wmOperatorTypeMacro), "wmOperatorTypeMacro"));
547
548 STRNCPY(otmacro->idname, idname);
549
550 /* Do this on first use, since operator definitions might have been not done yet. */
551 WM_operator_properties_alloc(&(otmacro->ptr), &(otmacro->properties), idname);
552 WM_operator_properties_sanitize(otmacro->ptr, true);
553
554 BLI_addtail(&ot->macro, otmacro);
555
556 /* Operator should always be found but in the event its not. don't segfault. */
557 if (wmOperatorType *otsub = WM_operatortype_find(idname, false)) {
558 RNA_def_pointer_runtime(ot->srna, otsub->idname, otsub->srna, otsub->name, otsub->description);
559 }
560
561 return otmacro;
562}
563
565{
567 if (otmacro->ptr) {
568 WM_operator_properties_free(otmacro->ptr);
569 MEM_delete(otmacro->ptr);
570 }
571 }
573}
574
576{
577 std::string name;
578 if (ot->get_name && properties) {
579 name = ot->get_name(ot, properties);
580 }
581
582 return name.empty() ? std::string(RNA_struct_ui_name(ot->srna)) : name;
583}
584
586{
587 if (ot->get_description && properties) {
588 std::string description = ot->get_description(C, ot, properties);
589 if (!description.empty()) {
590 return description;
591 }
592 }
593
594 const char *info = RNA_struct_ui_description(ot->srna);
595 if (info && info[0]) {
596 return info;
597 }
598 return "";
599}
600
603 PointerRNA *properties)
604{
605 std::string text = WM_operatortype_description(C, ot, properties);
606 if (text.empty()) {
607 std::string text_orig = WM_operatortype_name(ot, properties);
608 if (!text_orig.empty()) {
609 return text_orig;
610 }
611 }
612 return text;
613}
614
616{
618 return true;
619 }
620 if (ot.depends_on_cursor) {
621 return ot.depends_on_cursor(C, ot, properties);
622 }
623 return false;
624}
625
wmWindow * CTX_wm_window(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
void IDP_FreeProperty(IDProperty *prop)
Definition idprop.cc:1227
#define BLI_assert(a)
Definition BLI_assert.h:50
#define LISTBASE_FOREACH(type, var, list)
void void void BLI_movelisttolist(struct ListBase *dst, struct ListBase *src) ATTR_NONNULL(1
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:496
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:130
void * BLI_findptr(const struct ListBase *listbase, const void *ptr, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
#define STRNCPY(dst, src)
Definition BLI_string.h:593
#define BLT_I18NCONTEXT_OPERATOR_DEFAULT
#define CLOG_ERROR(clg_ref,...)
Definition CLG_log.h:182
#define CLOG_WARN(clg_ref,...)
Definition CLG_log.h:181
#define CLOG_INFO(clg_ref, level,...)
Definition CLG_log.h:179
ID and Library types, which are fundamental for SDNA.
#define OP_MAX_TYPENAME
@ OP_IS_MODAL_GRAB_CURSOR
@ OPERATOR_RUNNING_MODAL
#define OPERATOR_RETVAL_CHECK(ret)
Read Guarded memory(de)allocation.
#define RNA_STRUCT_BEGIN(sptr, prop)
#define RNA_STRUCT_END
#define WM_operatortype_prop_tag(property, tags)
Definition WM_api.hh:1197
@ OPTYPE_MACRO
Definition WM_types.hh:165
@ OPTYPE_BLOCKING
Definition WM_types.hh:164
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:198
@ OPTYPE_GRAB_CURSOR_XY
Definition WM_types.hh:168
@ OPTYPE_GRAB_CURSOR_X
Definition WM_types.hh:170
@ OPTYPE_GRAB_CURSOR_Y
Definition WM_types.hh:172
CLG_LogRef * WM_LOG_OPERATORS
#define OP_PROP_TAG_ADVANCED
Definition WM_types.hh:243
eWM_CursorWrapAxis
Definition WM_types.hh:205
@ WM_CURSOR_WRAP_X
Definition WM_types.hh:207
@ WM_CURSOR_WRAP_XY
Definition WM_types.hh:209
@ WM_CURSOR_WRAP_Y
Definition WM_types.hh:208
@ WM_CURSOR_WRAP_NONE
Definition WM_types.hh:206
ValueIterator values() const
Definition BLI_map.hh:846
void add_new(const Key &key, const Value &value)
Definition BLI_map.hh:241
bool remove(const Key &key)
Definition BLI_map.hh:344
void reserve(int64_t n)
Definition BLI_map.hh:979
Value lookup_default_as(const ForwardKey &key, ForwardValue &&...default_value) const
Definition BLI_map.hh:536
#define offsetof(t, d)
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
float wrap(float value, float max, float min)
Definition node_math.h:71
const char * RNA_struct_ui_description(const StructRNA *type)
const char * RNA_struct_ui_name(const StructRNA *type)
uint RNA_struct_count_properties(StructRNA *srna)
const char * RNA_struct_translation_context(const StructRNA *type)
PropertyRNA * RNA_def_pointer_runtime(StructOrFunctionRNA *cont_, const char *identifier, StructRNA *type, const char *ui_name, const char *ui_description)
void RNA_def_struct_property_tags(StructRNA *srna, const EnumPropertyItem *prop_tag_defines)
void RNA_def_struct_ui_text(StructRNA *srna, const char *name, const char *description)
void RNA_def_struct_identifier(BlenderRNA *brna, StructRNA *srna, const char *identifier)
StructRNA * RNA_def_struct_ptr(BlenderRNA *brna, const char *identifier, StructRNA *srnafrom)
void RNA_struct_free(BlenderRNA *brna, StructRNA *srna)
void RNA_def_struct_translation_context(StructRNA *srna, const char *context)
BlenderRNA BLENDER_RNA
const EnumPropertyItem rna_enum_operator_property_tag_items[]
Definition rna_wm.cc:584
CLG_LogType * type
Definition CLG_log.h:108
StructRNA * srna
Definition RNA_types.hh:780
void * first
const char * name
wmEventHandler head
struct IDProperty * properties
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
std::string(* get_description)(bContext *C, wmOperatorType *ot, PointerRNA *ptr)
Definition WM_types.hh:1074
bool(* depends_on_cursor)(bContext &C, wmOperatorType &ot, PointerRNA *ptr)
Definition WM_types.hh:1077
const char * idname
Definition WM_types.hh:992
int(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1036
std::string(* get_name)(wmOperatorType *ot, PointerRNA *ptr)
Definition WM_types.hh:1068
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
ExtensionRNA rna_ext
Definition WM_types.hh:1104
const char * translation_context
Definition WM_types.hh:994
const char * description
Definition WM_types.hh:996
IDProperty * last_properties
Definition WM_types.hh:1083
ListBase macro
Definition WM_types.hh:1095
StructRNA * srna
Definition WM_types.hh:1080
void(* cancel)(bContext *C, wmOperator *op)
Definition WM_types.hh:1028
struct ReportList * reports
struct wmOperator * next
struct wmOperatorType * type
struct PointerRNA * ptr
struct wmOperator * opm
void WM_cursor_grab_enable(wmWindow *win, const eWM_CursorWrapAxis wrap, const rcti *wrap_region, const bool hide)
@ WM_CURSOR_PICK_AREA
Definition wm_cursors.hh:61
void wm_event_free_handler(wmEventHandler *handler)
wmOperatorType * ot
Definition wm_files.cc:4125
void WM_keyconfig_update_operatortype()
static int wm_macro_end(wmOperator *op, int retval)
void WM_operatortype_append_macro_ptr(void(*opfunc)(wmOperatorType *ot, void *userdata), void *userdata)
void WM_operatortype_idname_visit_for_search(const bContext *, PointerRNA *, PropertyRNA *, const char *, blender::FunctionRef< void(StringPropertySearchVisitParams)> visit_fn)
static wmOperatorType * wm_operatortype_append__begin()
static void wm_operatortype_free_macro(wmOperatorType *ot)
static wmOperatorTypeMap & get_operators_map()
static int ot_prop_basic_count
void WM_operatortype_props_advanced_begin(wmOperatorType *ot)
static void wm_macro_start(wmOperator *op)
wmOperatorTypeMacro * WM_operatortype_macro_define(wmOperatorType *ot, const char *idname)
void wm_operatortype_free()
std::string WM_operatortype_name(wmOperatorType *ot, PointerRNA *properties)
bool WM_operator_depends_on_cursor(bContext &C, wmOperatorType &ot, PointerRNA *properties)
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))
static int wm_macro_modal(bContext *C, wmOperator *op, const wmEvent *event)
std::string WM_operatortype_description_or_name(bContext *C, wmOperatorType *ot, PointerRNA *properties)
std::string WM_operatortype_description(bContext *C, wmOperatorType *ot, PointerRNA *properties)
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
static int wm_macro_exec(bContext *C, wmOperator *op)
const wmOperatorTypeMap & WM_operatortype_map()
void WM_operatortype_last_properties_clear_all()
void WM_operatortype_append_ptr(void(*opfunc)(wmOperatorType *, void *), void *userdata)
static void wm_operatortype_append__end(wmOperatorType *ot)
#define UNDOCUMENTED_OPERATOR_TIP
wmOperatorType * WM_operatortype_append_macro(const char *idname, const char *name, const char *description, int flag)
static void operatortype_ghash_free_cb(wmOperatorType *ot)
void WM_operatortype_remove_ptr(wmOperatorType *ot)
static int wm_macro_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void WM_operatortype_props_advanced_end(wmOperatorType *ot)
static int wm_macro_invoke_internal(bContext *C, wmOperator *op, const wmEvent *event, wmOperator *opm)
bool WM_operatortype_remove(const char *idname)
static void wm_macro_cancel(bContext *C, wmOperator *op)
size_t WM_operator_py_idname(char *dst, const char *src)
size_t WM_operator_bl_idname(char *dst, const char *src)
void WM_operator_properties_alloc(PointerRNA **ptr, IDProperty **properties, const char *opstring)
bool WM_operator_bl_idname_is_valid(const char *idname)
void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
void WM_operator_properties_clear(PointerRNA *ptr)
void WM_operator_properties_free(PointerRNA *ptr)
void WM_operator_properties_sanitize(PointerRNA *ptr, const bool no_context)
uint8_t flag
Definition wm_window.cc:138