Merge branch 'master' into blender2.8
[blender.git] / source / blender / windowmanager / intern / wm_operators.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2007 Blender Foundation.
19  * All rights reserved.
20  *
21  *
22  * Contributor(s): Blender Foundation
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/windowmanager/intern/wm_operators.c
28  *  \ingroup wm
29  *
30  * Functions for dealing with wmOperator, adding, removing, calling
31  * as well as some generic operators and shared operator properties.
32  */
33
34
35 #include <float.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include <stdio.h>
39 #include <stddef.h>
40 #include <assert.h>
41 #include <errno.h>
42
43 #ifdef WIN32
44 #  include "GHOST_C-api.h"
45 #endif
46
47 #include "MEM_guardedalloc.h"
48
49 #include "CLG_log.h"
50
51 #include "DNA_ID.h"
52 #include "DNA_object_types.h"
53 #include "DNA_screen_types.h"
54 #include "DNA_scene_types.h"
55 #include "DNA_userdef_types.h"
56 #include "DNA_windowmanager_types.h"
57 #include "DNA_workspace_types.h"
58
59 #include "BLT_translation.h"
60
61 #include "PIL_time.h"
62
63 #include "BLI_blenlib.h"
64 #include "BLI_dial_2d.h"
65 #include "BLI_dynstr.h" /*for WM_operator_pystring */
66 #include "BLI_math.h"
67 #include "BLI_utildefines.h"
68 #include "BLI_ghash.h"
69
70 #include "BLO_readfile.h"
71
72 #include "BKE_appdir.h"
73 #include "BKE_blender_version.h"
74 #include "BKE_brush.h"
75 #include "BKE_context.h"
76 #include "BKE_icons.h"
77 #include "BKE_idprop.h"
78 #include "BKE_image.h"
79 #include "BKE_library.h"
80 #include "BKE_library_query.h"
81 #include "BKE_global.h"
82 #include "BKE_main.h"
83 #include "BKE_material.h"
84 #include "BKE_report.h"
85 #include "BKE_scene.h"
86 #include "BKE_screen.h" /* BKE_ST_MAXNAME */
87 #include "BKE_unit.h"
88
89 #include "BKE_idcode.h"
90
91 #include "BLF_api.h"
92
93 #include "GPU_immediate.h"
94 #include "GPU_immediate_util.h"
95 #include "GPU_matrix.h"
96
97 #include "IMB_imbuf_types.h"
98 #include "IMB_imbuf.h"
99
100 #include "ED_numinput.h"
101 #include "ED_screen.h"
102 #include "ED_undo.h"
103 #include "ED_view3d.h"
104
105 #include "RNA_access.h"
106 #include "RNA_define.h"
107 #include "RNA_enum_types.h"
108
109 #include "UI_interface.h"
110 #include "UI_interface_icons.h"
111 #include "UI_resources.h"
112
113 #include "WM_api.h"
114 #include "WM_types.h"
115
116 #include "wm.h"
117 #include "wm_draw.h"
118 #include "wm_event_system.h"
119 #include "wm_event_types.h"
120 #include "wm_files.h"
121 #include "wm_window.h"
122
123 static GHash *global_ops_hash = NULL;
124 /** Counter for operator-properties that should not be tagged with #OP_PROP_TAG_ADVANCED. */
125 static int ot_prop_basic_count = -1;
126
127 #define UNDOCUMENTED_OPERATOR_TIP N_("(undocumented operator)")
128
129 /* ************ operator API, exported ********** */
130
131
132 wmOperatorType *WM_operatortype_find(const char *idname, bool quiet)
133 {
134         if (idname[0]) {
135                 wmOperatorType *ot;
136
137                 /* needed to support python style names without the _OT_ syntax */
138                 char idname_bl[OP_MAX_TYPENAME];
139                 WM_operator_bl_idname(idname_bl, idname);
140
141                 ot = BLI_ghash_lookup(global_ops_hash, idname_bl);
142                 if (ot) {
143                         return ot;
144                 }
145
146                 if (!quiet) {
147                         CLOG_INFO(WM_LOG_OPERATORS, 0, "search for unknown operator '%s', '%s'\n", idname_bl, idname);
148                 }
149         }
150         else {
151                 if (!quiet) {
152                         CLOG_INFO(WM_LOG_OPERATORS, 0, "search for empty operator");
153                 }
154         }
155
156         return NULL;
157 }
158
159 /* caller must free */
160 void WM_operatortype_iter(GHashIterator *ghi)
161 {
162         BLI_ghashIterator_init(ghi, global_ops_hash);
163 }
164
165 /** \name Operator Type Append
166  * \{ */
167
168 static wmOperatorType *wm_operatortype_append__begin(void)
169 {
170         wmOperatorType *ot = MEM_callocN(sizeof(wmOperatorType), "operatortype");
171
172         BLI_assert(ot_prop_basic_count == -1);
173
174         ot->srna = RNA_def_struct_ptr(&BLENDER_RNA, "", &RNA_OperatorProperties);
175         RNA_def_struct_property_tags(ot->srna, rna_enum_operator_property_tags);
176         /* Set the default i18n context now, so that opfunc can redefine it if needed! */
177         RNA_def_struct_translation_context(ot->srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT);
178         ot->translation_context = BLT_I18NCONTEXT_OPERATOR_DEFAULT;
179
180         return ot;
181 }
182 static void wm_operatortype_append__end(wmOperatorType *ot)
183 {
184         if (ot->name == NULL) {
185                 CLOG_ERROR(WM_LOG_OPERATORS, "Operator '%s' has no name property", ot->idname);
186         }
187
188         /* Allow calling _begin without _end in operatortype creation. */
189         WM_operatortype_props_advanced_end(ot);
190
191         /* XXX All ops should have a description but for now allow them not to. */
192         RNA_def_struct_ui_text(ot->srna, ot->name, ot->description ? ot->description : UNDOCUMENTED_OPERATOR_TIP);
193         RNA_def_struct_identifier(&BLENDER_RNA, ot->srna, ot->idname);
194
195         BLI_ghash_insert(global_ops_hash, (void *)ot->idname, ot);
196 }
197
198 /* all ops in 1 list (for time being... needs evaluation later) */
199 void WM_operatortype_append(void (*opfunc)(wmOperatorType *))
200 {
201         wmOperatorType *ot = wm_operatortype_append__begin();
202         opfunc(ot);
203         wm_operatortype_append__end(ot);
204 }
205
206 void WM_operatortype_append_ptr(void (*opfunc)(wmOperatorType *, void *), void *userdata)
207 {
208         wmOperatorType *ot = wm_operatortype_append__begin();
209         opfunc(ot, userdata);
210         wm_operatortype_append__end(ot);
211 }
212
213 /** \} */
214
215 /* ********************* macro operator ******************** */
216
217 typedef struct {
218         int retval;
219 } MacroData;
220
221 static void wm_macro_start(wmOperator *op)
222 {
223         if (op->customdata == NULL) {
224                 op->customdata = MEM_callocN(sizeof(MacroData), "MacroData");
225         }
226 }
227
228 static int wm_macro_end(wmOperator *op, int retval)
229 {
230         if (retval & OPERATOR_CANCELLED) {
231                 MacroData *md = op->customdata;
232
233                 if (md->retval & OPERATOR_FINISHED) {
234                         retval |= OPERATOR_FINISHED;
235                         retval &= ~OPERATOR_CANCELLED;
236                 }
237         }
238
239         /* if modal is ending, free custom data */
240         if (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED)) {
241                 if (op->customdata) {
242                         MEM_freeN(op->customdata);
243                         op->customdata = NULL;
244                 }
245         }
246
247         return retval;
248 }
249
250 /* macro exec only runs exec calls */
251 static int wm_macro_exec(bContext *C, wmOperator *op)
252 {
253         wmOperator *opm;
254         int retval = OPERATOR_FINISHED;
255
256         wm_macro_start(op);
257
258         for (opm = op->macro.first; opm; opm = opm->next) {
259
260                 if (opm->type->exec) {
261                         retval = opm->type->exec(C, opm);
262                         OPERATOR_RETVAL_CHECK(retval);
263
264                         if (retval & OPERATOR_FINISHED) {
265                                 MacroData *md = op->customdata;
266                                 md->retval = OPERATOR_FINISHED; /* keep in mind that at least one operator finished */
267                         }
268                         else {
269                                 break; /* operator didn't finish, end macro */
270                         }
271                 }
272                 else {
273                         CLOG_WARN(WM_LOG_OPERATORS, "'%s' cant exec macro", opm->type->idname);
274                 }
275         }
276
277         return wm_macro_end(op, retval);
278 }
279
280 static int wm_macro_invoke_internal(bContext *C, wmOperator *op, const wmEvent *event, wmOperator *opm)
281 {
282         int retval = OPERATOR_FINISHED;
283
284         /* start from operator received as argument */
285         for (; opm; opm = opm->next) {
286                 if (opm->type->invoke)
287                         retval = opm->type->invoke(C, opm, event);
288                 else if (opm->type->exec)
289                         retval = opm->type->exec(C, opm);
290
291                 OPERATOR_RETVAL_CHECK(retval);
292
293                 BLI_movelisttolist(&op->reports->list, &opm->reports->list);
294
295                 if (retval & OPERATOR_FINISHED) {
296                         MacroData *md = op->customdata;
297                         md->retval = OPERATOR_FINISHED; /* keep in mind that at least one operator finished */
298                 }
299                 else {
300                         break; /* operator didn't finish, end macro */
301                 }
302         }
303
304         return wm_macro_end(op, retval);
305 }
306
307 static int wm_macro_invoke(bContext *C, wmOperator *op, const wmEvent *event)
308 {
309         wm_macro_start(op);
310         return wm_macro_invoke_internal(C, op, event, op->macro.first);
311 }
312
313 static int wm_macro_modal(bContext *C, wmOperator *op, const wmEvent *event)
314 {
315         wmOperator *opm = op->opm;
316         int retval = OPERATOR_FINISHED;
317
318         if (opm == NULL) {
319                 CLOG_ERROR(WM_LOG_OPERATORS, "macro error, calling NULL modal()");
320         }
321         else {
322                 retval = opm->type->modal(C, opm, event);
323                 OPERATOR_RETVAL_CHECK(retval);
324
325                 /* if we're halfway through using a tool and cancel it, clear the options [#37149] */
326                 if (retval & OPERATOR_CANCELLED) {
327                         WM_operator_properties_clear(opm->ptr);
328                 }
329
330                 /* if this one is done but it's not the last operator in the macro */
331                 if ((retval & OPERATOR_FINISHED) && opm->next) {
332                         MacroData *md = op->customdata;
333
334                         md->retval = OPERATOR_FINISHED; /* keep in mind that at least one operator finished */
335
336                         retval = wm_macro_invoke_internal(C, op, event, opm->next);
337
338                         /* if new operator is modal and also added its own handler */
339                         if (retval & OPERATOR_RUNNING_MODAL && op->opm != opm) {
340                                 wmWindow *win = CTX_wm_window(C);
341                                 wmEventHandler *handler;
342
343                                 handler = BLI_findptr(&win->modalhandlers, op, offsetof(wmEventHandler, op));
344                                 if (handler) {
345                                         BLI_remlink(&win->modalhandlers, handler);
346                                         wm_event_free_handler(handler);
347                                 }
348
349                                 /* if operator is blocking, grab cursor
350                                  * This may end up grabbing twice, but we don't care.
351                                  * */
352                                 if (op->opm->type->flag & OPTYPE_BLOCKING) {
353                                         int bounds[4] = {-1, -1, -1, -1};
354                                         const bool wrap = (
355                                                 (U.uiflag & USER_CONTINUOUS_MOUSE) &&
356                                                 ((op->opm->flag & OP_IS_MODAL_GRAB_CURSOR) || (op->opm->type->flag & OPTYPE_GRAB_CURSOR)));
357
358                                         if (wrap) {
359                                                 ARegion *ar = CTX_wm_region(C);
360                                                 if (ar) {
361                                                         bounds[0] = ar->winrct.xmin;
362                                                         bounds[1] = ar->winrct.ymax;
363                                                         bounds[2] = ar->winrct.xmax;
364                                                         bounds[3] = ar->winrct.ymin;
365                                                 }
366                                         }
367
368                                         WM_cursor_grab_enable(win, wrap, false, bounds);
369                                 }
370                         }
371                 }
372         }
373
374         return wm_macro_end(op, retval);
375 }
376
377 static void wm_macro_cancel(bContext *C, wmOperator *op)
378 {
379         /* call cancel on the current modal operator, if any */
380         if (op->opm && op->opm->type->cancel) {
381                 op->opm->type->cancel(C, op->opm);
382         }
383
384         wm_macro_end(op, OPERATOR_CANCELLED);
385 }
386
387 /* Names have to be static for now */
388 wmOperatorType *WM_operatortype_append_macro(const char *idname, const char *name, const char *description, int flag)
389 {
390         wmOperatorType *ot;
391         const char *i18n_context;
392
393         if (WM_operatortype_find(idname, true)) {
394                 CLOG_ERROR(WM_LOG_OPERATORS, "operator %s exists, cannot create macro", idname);
395                 return NULL;
396         }
397
398         ot = MEM_callocN(sizeof(wmOperatorType), "operatortype");
399         ot->srna = RNA_def_struct_ptr(&BLENDER_RNA, "", &RNA_OperatorProperties);
400
401         ot->idname = idname;
402         ot->name = name;
403         ot->description = description;
404         ot->flag = OPTYPE_MACRO | flag;
405
406         ot->exec = wm_macro_exec;
407         ot->invoke = wm_macro_invoke;
408         ot->modal = wm_macro_modal;
409         ot->cancel = wm_macro_cancel;
410         ot->poll = NULL;
411
412         if (!ot->description) /* XXX All ops should have a description but for now allow them not to. */
413                 ot->description = UNDOCUMENTED_OPERATOR_TIP;
414
415         RNA_def_struct_ui_text(ot->srna, ot->name, ot->description);
416         RNA_def_struct_identifier(&BLENDER_RNA, ot->srna, ot->idname);
417         /* Use i18n context from ext.srna if possible (py operators). */
418         i18n_context = ot->ext.srna ? RNA_struct_translation_context(ot->ext.srna) : BLT_I18NCONTEXT_OPERATOR_DEFAULT;
419         RNA_def_struct_translation_context(ot->srna, i18n_context);
420         ot->translation_context = i18n_context;
421
422         BLI_ghash_insert(global_ops_hash, (void *)ot->idname, ot);
423
424         return ot;
425 }
426
427 void WM_operatortype_append_macro_ptr(void (*opfunc)(wmOperatorType *, void *), void *userdata)
428 {
429         wmOperatorType *ot;
430
431         ot = MEM_callocN(sizeof(wmOperatorType), "operatortype");
432         ot->srna = RNA_def_struct_ptr(&BLENDER_RNA, "", &RNA_OperatorProperties);
433
434         ot->flag = OPTYPE_MACRO;
435         ot->exec = wm_macro_exec;
436         ot->invoke = wm_macro_invoke;
437         ot->modal = wm_macro_modal;
438         ot->cancel = wm_macro_cancel;
439         ot->poll = NULL;
440
441         if (!ot->description)
442                 ot->description = UNDOCUMENTED_OPERATOR_TIP;
443
444         /* Set the default i18n context now, so that opfunc can redefine it if needed! */
445         RNA_def_struct_translation_context(ot->srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT);
446         ot->translation_context = BLT_I18NCONTEXT_OPERATOR_DEFAULT;
447         opfunc(ot, userdata);
448
449         RNA_def_struct_ui_text(ot->srna, ot->name, ot->description);
450         RNA_def_struct_identifier(&BLENDER_RNA, ot->srna, ot->idname);
451
452         BLI_ghash_insert(global_ops_hash, (void *)ot->idname, ot);
453 }
454
455 wmOperatorTypeMacro *WM_operatortype_macro_define(wmOperatorType *ot, const char *idname)
456 {
457         wmOperatorTypeMacro *otmacro = MEM_callocN(sizeof(wmOperatorTypeMacro), "wmOperatorTypeMacro");
458
459         BLI_strncpy(otmacro->idname, idname, OP_MAX_TYPENAME);
460
461         /* do this on first use, since operatordefinitions might have been not done yet */
462         WM_operator_properties_alloc(&(otmacro->ptr), &(otmacro->properties), idname);
463         WM_operator_properties_sanitize(otmacro->ptr, 1);
464
465         BLI_addtail(&ot->macro, otmacro);
466
467         {
468                 /* operator should always be found but in the event its not. don't segfault */
469                 wmOperatorType *otsub = WM_operatortype_find(idname, 0);
470                 if (otsub) {
471                         RNA_def_pointer_runtime(ot->srna, otsub->idname, otsub->srna,
472                                                 otsub->name, otsub->description);
473                 }
474         }
475
476         return otmacro;
477 }
478
479 static void wm_operatortype_free_macro(wmOperatorType *ot)
480 {
481         wmOperatorTypeMacro *otmacro;
482
483         for (otmacro = ot->macro.first; otmacro; otmacro = otmacro->next) {
484                 if (otmacro->ptr) {
485                         WM_operator_properties_free(otmacro->ptr);
486                         MEM_freeN(otmacro->ptr);
487                 }
488         }
489         BLI_freelistN(&ot->macro);
490 }
491
492 void WM_operatortype_remove_ptr(wmOperatorType *ot)
493 {
494         BLI_assert(ot == WM_operatortype_find(ot->idname, false));
495
496         RNA_struct_free(&BLENDER_RNA, ot->srna);
497
498         if (ot->last_properties) {
499                 IDP_FreeProperty(ot->last_properties);
500                 MEM_freeN(ot->last_properties);
501         }
502
503         if (ot->macro.first)
504                 wm_operatortype_free_macro(ot);
505
506         BLI_ghash_remove(global_ops_hash, ot->idname, NULL, NULL);
507
508         WM_keyconfig_update_operatortype();
509
510         MEM_freeN(ot);
511 }
512
513 bool WM_operatortype_remove(const char *idname)
514 {
515         wmOperatorType *ot = WM_operatortype_find(idname, 0);
516
517         if (ot == NULL)
518                 return false;
519
520         WM_operatortype_remove_ptr(ot);
521
522         return true;
523 }
524
525 /**
526  * Remove memory of all previously executed tools.
527  */
528 void WM_operatortype_last_properties_clear_all(void)
529 {
530         GHashIterator iter;
531
532         for (WM_operatortype_iter(&iter);
533              (!BLI_ghashIterator_done(&iter));
534              (BLI_ghashIterator_step(&iter)))
535         {
536                 wmOperatorType *ot = BLI_ghashIterator_getValue(&iter);
537
538                 if (ot->last_properties) {
539                         IDP_FreeProperty(ot->last_properties);
540                         MEM_freeN(ot->last_properties);
541                         ot->last_properties = NULL;
542                 }
543         }
544 }
545
546 /**
547  * Tag all operator-properties of \a ot defined after calling this, until
548  * the next #WM_operatortype_props_advanced_end call (if available), with
549  * #OP_PROP_TAG_ADVANCED. Previously defined ones properties not touched.
550  *
551  * Calling this multiple times without a call to #WM_operatortype_props_advanced_end,
552  * all calls after the first one are ignored. Meaning all propereties defined after the
553  * first call are tagged as advanced.
554  *
555  * This doesn't do the actual tagging, #WM_operatortype_props_advanced_end does which is
556  * called for all operators during registration (see #wm_operatortype_append__end).
557  */
558 void WM_operatortype_props_advanced_begin(wmOperatorType *ot)
559 {
560         if (ot_prop_basic_count == -1) { /* Don't do anything if _begin was called before, but not _end  */
561                 ot_prop_basic_count = RNA_struct_count_properties(ot->srna);
562         }
563 }
564
565 /**
566  * Tags all operator-properties of \ot defined since the first #WM_operatortype_props_advanced_begin
567  * call, or the last #WM_operatortype_props_advanced_end call, with #OP_PROP_TAG_ADVANCED.
568  * Note that this is called for all operators during registration (see #wm_operatortype_append__end).
569  * So it does not need to be explicitly called in operator-type definition.
570  */
571 void WM_operatortype_props_advanced_end(wmOperatorType *ot)
572 {
573         PointerRNA struct_ptr;
574         int counter = 0;
575
576         if (ot_prop_basic_count == -1) {
577                 /* WM_operatortype_props_advanced_begin was not called. Don't do anything. */
578                 return;
579         }
580
581         RNA_pointer_create(NULL, ot->srna, NULL, &struct_ptr);
582
583         RNA_STRUCT_BEGIN (&struct_ptr, prop)
584         {
585                 counter++;
586                 if (counter > ot_prop_basic_count) {
587                         WM_operatortype_prop_tag(prop, OP_PROP_TAG_ADVANCED);
588                 }
589         }
590         RNA_STRUCT_END;
591
592         ot_prop_basic_count = -1;
593 }
594
595 /* SOME_OT_op -> some.op */
596 void WM_operator_py_idname(char *to, const char *from)
597 {
598         const char *sep = strstr(from, "_OT_");
599         if (sep) {
600                 int ofs = (sep - from);
601
602                 /* note, we use ascii tolower instead of system tolower, because the
603                  * latter depends on the locale, and can lead to idname mismatch */
604                 memcpy(to, from, sizeof(char) * ofs);
605                 BLI_str_tolower_ascii(to, ofs);
606
607                 to[ofs] = '.';
608                 BLI_strncpy(to + (ofs + 1), sep + 4, OP_MAX_TYPENAME - (ofs + 1));
609         }
610         else {
611                 /* should not happen but support just in case */
612                 BLI_strncpy(to, from, OP_MAX_TYPENAME);
613         }
614 }
615
616 /* some.op -> SOME_OT_op */
617 void WM_operator_bl_idname(char *to, const char *from)
618 {
619         if (from) {
620                 const char *sep = strchr(from, '.');
621
622                 if (sep) {
623                         int ofs = (sep - from);
624
625                         memcpy(to, from, sizeof(char) * ofs);
626                         BLI_str_toupper_ascii(to, ofs);
627                         strcpy(to + ofs, "_OT_");
628                         strcpy(to + (ofs + 4), sep + 1);
629                 }
630                 else {
631                         /* should not happen but support just in case */
632                         BLI_strncpy(to, from, OP_MAX_TYPENAME);
633                 }
634         }
635         else
636                 to[0] = 0;
637 }
638
639 /**
640  * Sanity check to ensure #WM_operator_bl_idname won't fail.
641  * \returns true when there are no problems with \a idname, otherwise report an error.
642  */
643 bool WM_operator_py_idname_ok_or_report(ReportList *reports, const char *classname, const char *idname)
644 {
645         const char *ch = idname;
646         int dot = 0;
647         int i;
648         for (i = 0; *ch; i++, ch++) {
649                 if ((*ch >= 'a' && *ch <= 'z') || (*ch >= '0' && *ch <= '9') || *ch == '_') {
650                         /* pass */
651                 }
652                 else if (*ch == '.') {
653                         dot++;
654                 }
655                 else {
656                         BKE_reportf(reports, RPT_ERROR,
657                                     "Registering operator class: '%s', invalid bl_idname '%s', at position %d",
658                                     classname, idname, i);
659                         return false;
660                 }
661         }
662
663         if (i > (MAX_NAME - 3)) {
664                 BKE_reportf(reports, RPT_ERROR, "Registering operator class: '%s', invalid bl_idname '%s', "
665                             "is too long, maximum length is %d", classname, idname,
666                             MAX_NAME - 3);
667                 return false;
668         }
669
670         if (dot != 1) {
671                 BKE_reportf(reports, RPT_ERROR,
672                             "Registering operator class: '%s', invalid bl_idname '%s', must contain 1 '.' character",
673                             classname, idname);
674                 return false;
675         }
676         return true;
677 }
678
679 /**
680  * Print a string representation of the operator, with the args that it runs so python can run it again.
681  *
682  * When calling from an existing wmOperator, better to use simple version:
683  * `WM_operator_pystring(C, op);`
684  *
685  * \note Both \a op and \a opptr may be `NULL` (\a op is only used for macro operators).
686  */
687 char *WM_operator_pystring_ex(bContext *C, wmOperator *op, const bool all_args, const bool macro_args,
688                               wmOperatorType *ot, PointerRNA *opptr)
689 {
690         char idname_py[OP_MAX_TYPENAME];
691
692         /* for building the string */
693         DynStr *dynstr = BLI_dynstr_new();
694         char *cstring;
695         char *cstring_args;
696
697         /* arbitrary, but can get huge string with stroke painting otherwise */
698         int max_prop_length = 10;
699
700         WM_operator_py_idname(idname_py, ot->idname);
701         BLI_dynstr_appendf(dynstr, "bpy.ops.%s(", idname_py);
702
703         if (op && op->macro.first) {
704                 /* Special handling for macros, else we only get default values in this case... */
705                 wmOperator *opm;
706                 bool first_op = true;
707
708                 opm = macro_args ? op->macro.first : NULL;
709
710                 for (; opm; opm = opm->next) {
711                         PointerRNA *opmptr = opm->ptr;
712                         PointerRNA opmptr_default;
713                         if (opmptr == NULL) {
714                                 WM_operator_properties_create_ptr(&opmptr_default, opm->type);
715                                 opmptr = &opmptr_default;
716                         }
717
718                         cstring_args = RNA_pointer_as_string_id(C, opmptr);
719                         if (first_op) {
720                                 BLI_dynstr_appendf(dynstr, "%s=%s", opm->type->idname, cstring_args);
721                                 first_op = false;
722                         }
723                         else {
724                                 BLI_dynstr_appendf(dynstr, ", %s=%s", opm->type->idname, cstring_args);
725                         }
726                         MEM_freeN(cstring_args);
727
728                         if (opmptr == &opmptr_default) {
729                                 WM_operator_properties_free(&opmptr_default);
730                         }
731                 }
732         }
733         else {
734                 /* only to get the orginal props for comparisons */
735                 PointerRNA opptr_default;
736                 const bool macro_args_test = ot->macro.first ? macro_args : true;
737
738                 if (opptr == NULL) {
739                         WM_operator_properties_create_ptr(&opptr_default, ot);
740                         opptr = &opptr_default;
741                 }
742
743                 cstring_args = RNA_pointer_as_string_keywords(C, opptr, false, all_args, macro_args_test, max_prop_length);
744                 BLI_dynstr_append(dynstr, cstring_args);
745                 MEM_freeN(cstring_args);
746
747                 if (opptr == &opptr_default) {
748                         WM_operator_properties_free(&opptr_default);
749                 }
750         }
751
752         BLI_dynstr_append(dynstr, ")");
753
754         cstring = BLI_dynstr_get_cstring(dynstr);
755         BLI_dynstr_free(dynstr);
756         return cstring;
757 }
758
759 char *WM_operator_pystring(bContext *C, wmOperator *op,
760                            const bool all_args, const bool macro_args)
761 {
762         return WM_operator_pystring_ex(C, op, all_args, macro_args, op->type, op->ptr);
763 }
764
765
766 /**
767  * \return true if the string was shortened
768  */
769 bool WM_operator_pystring_abbreviate(char *str, int str_len_max)
770 {
771         const int str_len = strlen(str);
772         const char *parens_start = strchr(str, '(');
773
774         if (parens_start) {
775                 const int parens_start_pos = parens_start - str;
776                 const char *parens_end = strrchr(parens_start + 1, ')');
777
778                 if (parens_end) {
779                         const int parens_len = parens_end - parens_start;
780
781                         if (parens_len > str_len_max) {
782                                 const char *comma_first = strchr(parens_start, ',');
783
784                                 /* truncate after the first comma */
785                                 if (comma_first) {
786                                         const char end_str[] = " ... )";
787                                         const int end_str_len = sizeof(end_str) - 1;
788
789                                         /* leave a place for the first argument*/
790                                         const int new_str_len = (comma_first - parens_start) + 1;
791
792                                         if (str_len >= new_str_len + parens_start_pos + end_str_len + 1) {
793                                                 /* append " ... )" to the string after the comma */
794                                                 memcpy(str + new_str_len + parens_start_pos, end_str, end_str_len + 1);
795
796                                                 return true;
797                                         }
798                                 }
799                         }
800                 }
801         }
802
803         return false;
804 }
805
806 /* return NULL if no match is found */
807 #if 0
808 static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr)
809 {
810         /* loop over all context items and do 2 checks
811          *
812          * - see if the pointer is in the context.
813          * - see if the pointers ID is in the context.
814          */
815
816         /* don't get from the context store since this is normally set only for the UI and not usable elsewhere */
817         ListBase lb = CTX_data_dir_get_ex(C, false, true, true);
818         LinkData *link;
819
820         const char *member_found = NULL;
821         const char *member_id = NULL;
822
823         for (link = lb.first; link; link = link->next) {
824                 const char *identifier = link->data;
825                 PointerRNA ctx_item_ptr = {{0}}; // CTX_data_pointer_get(C, identifier); // XXX, this isnt working
826
827                 if (ctx_item_ptr.type == NULL) {
828                         continue;
829                 }
830
831                 if (ptr->id.data == ctx_item_ptr.id.data) {
832                         if ((ptr->data == ctx_item_ptr.data) &&
833                             (ptr->type == ctx_item_ptr.type))
834                         {
835                                 /* found! */
836                                 member_found = identifier;
837                                 break;
838                         }
839                         else if (RNA_struct_is_ID(ctx_item_ptr.type)) {
840                                 /* we found a reference to this ID,
841                                  * so fallback to it if there is no direct reference */
842                                 member_id = identifier;
843                         }
844                 }
845         }
846         BLI_freelistN(&lb);
847
848         if (member_found) {
849                 return member_found;
850         }
851         else if (member_id) {
852                 return member_id;
853         }
854         else {
855                 return NULL;
856         }
857 }
858
859 #else
860
861 /* use hard coded checks for now */
862
863 static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr)
864 {
865         const char *member_id = NULL;
866
867         if (ptr->id.data) {
868
869 #define CTX_TEST_PTR_ID(C, member, idptr) \
870                 { \
871                         const char *ctx_member = member; \
872                         PointerRNA ctx_item_ptr = CTX_data_pointer_get(C, ctx_member); \
873                         if (ctx_item_ptr.id.data == idptr) { \
874                                 member_id = ctx_member; \
875                                 break; \
876                         } \
877                 } (void)0
878
879 #define CTX_TEST_PTR_ID_CAST(C, member, member_full, cast, idptr) \
880                 { \
881                         const char *ctx_member = member; \
882                         const char *ctx_member_full = member_full; \
883                         PointerRNA ctx_item_ptr = CTX_data_pointer_get(C, ctx_member); \
884                         if (ctx_item_ptr.id.data && cast(ctx_item_ptr.id.data) == idptr) { \
885                                 member_id = ctx_member_full; \
886                                 break; \
887                         } \
888                 } (void)0
889
890 #define CTX_TEST_PTR_DATA_TYPE(C, member, rna_type, rna_ptr, dataptr_cmp) \
891                 { \
892                         const char *ctx_member = member; \
893                         if (RNA_struct_is_a((ptr)->type, &(rna_type)) && (ptr)->data == (dataptr_cmp)) { \
894                                 member_id = ctx_member; \
895                                 break; \
896                         } \
897                 } (void)0
898
899 #define CTX_TEST_SPACE_TYPE(space_data_type, member_full, dataptr_cmp) \
900                 { \
901                         const char *ctx_member_full = member_full; \
902                         if (space_data->spacetype == space_data_type && ptr->data == dataptr_cmp) { \
903                                 member_id = ctx_member_full; \
904                                 break; \
905                         } \
906                 } (void)0
907
908                 switch (GS(((ID *)ptr->id.data)->name)) {
909                         case ID_SCE:
910                         {
911                                 CTX_TEST_PTR_DATA_TYPE(C, "active_gpencil_brush", RNA_GPencilBrush, ptr, CTX_data_active_gpencil_brush(C));
912                                 CTX_TEST_PTR_ID(C, "scene", ptr->id.data);
913                                 break;
914                         }
915                         case ID_OB:
916                         {
917                                 CTX_TEST_PTR_ID(C, "object", ptr->id.data);
918                                 break;
919                         }
920                         /* from rna_Main_objects_new */
921                         case OB_DATA_SUPPORT_ID_CASE:
922                         {
923 #define ID_CAST_OBDATA(id_pt) (((Object *)(id_pt))->data)
924                                 CTX_TEST_PTR_ID_CAST(C, "object", "object.data", ID_CAST_OBDATA, ptr->id.data);
925                                 break;
926 #undef ID_CAST_OBDATA
927                         }
928                         case ID_MA:
929                         {
930 #define ID_CAST_OBMATACT(id_pt) (give_current_material(((Object *)id_pt), ((Object *)id_pt)->actcol))
931                                 CTX_TEST_PTR_ID_CAST(C, "object", "object.active_material", ID_CAST_OBMATACT, ptr->id.data);
932                                 break;
933 #undef ID_CAST_OBMATACT
934                         }
935                         case ID_WO:
936                         {
937 #define ID_CAST_SCENEWORLD(id_pt) (((Scene *)(id_pt))->world)
938                                 CTX_TEST_PTR_ID_CAST(C, "scene", "scene.world", ID_CAST_SCENEWORLD, ptr->id.data);
939                                 break;
940 #undef ID_CAST_SCENEWORLD
941                         }
942                         case ID_SCR:
943                         {
944                                 CTX_TEST_PTR_ID(C, "screen", ptr->id.data);
945
946                                 SpaceLink *space_data = CTX_wm_space_data(C);
947
948                                 CTX_TEST_PTR_DATA_TYPE(C, "space_data", RNA_Space, ptr, space_data);
949                                 CTX_TEST_PTR_DATA_TYPE(C, "space_data", RNA_View3DOverlay, ptr, space_data);
950                                 CTX_TEST_PTR_DATA_TYPE(C, "space_data", RNA_View3DShading, ptr, space_data);
951                                 CTX_TEST_PTR_DATA_TYPE(C, "area", RNA_Area, ptr, CTX_wm_area(C));
952                                 CTX_TEST_PTR_DATA_TYPE(C, "region", RNA_Region, ptr, CTX_wm_region(C));
953
954                                 CTX_TEST_SPACE_TYPE(SPACE_IMAGE, "space_data.uv_editor", space_data);
955                                 CTX_TEST_SPACE_TYPE(SPACE_VIEW3D, "space_data.fx_settings", &(CTX_wm_view3d(C)->fx_settings));
956                                 CTX_TEST_SPACE_TYPE(SPACE_NLA, "space_data.dopesheet", CTX_wm_space_nla(C)->ads);
957                                 CTX_TEST_SPACE_TYPE(SPACE_IPO, "space_data.dopesheet", CTX_wm_space_graph(C)->ads);
958                                 CTX_TEST_SPACE_TYPE(SPACE_ACTION, "space_data.dopesheet", &(CTX_wm_space_action(C)->ads));
959                                 CTX_TEST_SPACE_TYPE(SPACE_FILE, "space_data.params", CTX_wm_space_file(C)->params);
960                                 break;
961                         }
962                         default:
963                                 break;
964                 }
965 #undef CTX_TEST_PTR_ID
966 #undef CTX_TEST_PTR_ID_CAST
967 #undef CTX_TEST_SPACE_TYPE
968         }
969
970         return member_id;
971 }
972 #endif
973
974 static char *wm_prop_pystring_from_context(bContext *C, PointerRNA *ptr, PropertyRNA *prop, int index)
975 {
976         const char *member_id = wm_context_member_from_ptr(C, ptr);
977         char *ret = NULL;
978         if (member_id != NULL) {
979                 char *prop_str = RNA_path_struct_property_py(ptr, prop, index);
980                 if (prop_str) {
981                         ret = BLI_sprintfN("bpy.context.%s.%s", member_id, prop_str);
982                         MEM_freeN(prop_str);
983                 }
984         }
985         return ret;
986 }
987
988 const char *WM_context_member_from_ptr(bContext *C, const PointerRNA *ptr)
989 {
990         return wm_context_member_from_ptr(C, ptr);
991 }
992
993 char *WM_prop_pystring_assign(bContext *C, PointerRNA *ptr, PropertyRNA *prop, int index)
994 {
995         char *lhs, *rhs, *ret;
996
997         lhs = C ? wm_prop_pystring_from_context(C, ptr, prop, index) : NULL;
998
999         if (lhs == NULL) {
1000                 /* fallback to bpy.data.foo[id] if we dont find in the context */
1001                 lhs = RNA_path_full_property_py(ptr, prop, index);
1002         }
1003
1004         if (!lhs) {
1005                 return NULL;
1006         }
1007
1008         rhs = RNA_property_as_string(C, ptr, prop, index, INT_MAX);
1009         if (!rhs) {
1010                 MEM_freeN(lhs);
1011                 return NULL;
1012         }
1013
1014         ret = BLI_sprintfN("%s = %s", lhs, rhs);
1015         MEM_freeN(lhs);
1016         MEM_freeN(rhs);
1017         return ret;
1018 }
1019
1020 void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
1021 {
1022         RNA_pointer_create(NULL, ot->srna, NULL, ptr);
1023 }
1024
1025 void WM_operator_properties_create(PointerRNA *ptr, const char *opstring)
1026 {
1027         wmOperatorType *ot = WM_operatortype_find(opstring, false);
1028
1029         if (ot)
1030                 WM_operator_properties_create_ptr(ptr, ot);
1031         else
1032                 RNA_pointer_create(NULL, &RNA_OperatorProperties, NULL, ptr);
1033 }
1034
1035 /* similar to the function above except its uses ID properties
1036  * used for keymaps and macros */
1037 void WM_operator_properties_alloc(PointerRNA **ptr, IDProperty **properties, const char *opstring)
1038 {
1039         if (*properties == NULL) {
1040                 IDPropertyTemplate val = {0};
1041                 *properties = IDP_New(IDP_GROUP, &val, "wmOpItemProp");
1042         }
1043
1044         if (*ptr == NULL) {
1045                 *ptr = MEM_callocN(sizeof(PointerRNA), "wmOpItemPtr");
1046                 WM_operator_properties_create(*ptr, opstring);
1047         }
1048
1049         (*ptr)->data = *properties;
1050
1051 }
1052
1053 void WM_operator_properties_sanitize(PointerRNA *ptr, const bool no_context)
1054 {
1055         RNA_STRUCT_BEGIN (ptr, prop)
1056         {
1057                 switch (RNA_property_type(prop)) {
1058                         case PROP_ENUM:
1059                                 if (no_context)
1060                                         RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT);
1061                                 else
1062                                         RNA_def_property_clear_flag(prop, PROP_ENUM_NO_CONTEXT);
1063                                 break;
1064                         case PROP_POINTER:
1065                         {
1066                                 StructRNA *ptype = RNA_property_pointer_type(ptr, prop);
1067
1068                                 /* recurse into operator properties */
1069                                 if (RNA_struct_is_a(ptype, &RNA_OperatorProperties)) {
1070                                         PointerRNA opptr = RNA_property_pointer_get(ptr, prop);
1071                                         WM_operator_properties_sanitize(&opptr, no_context);
1072                                 }
1073                                 break;
1074                         }
1075                         default:
1076                                 break;
1077                 }
1078         }
1079         RNA_STRUCT_END;
1080 }
1081
1082
1083 /** set all props to their default,
1084  * \param do_update Only update un-initialized props.
1085  *
1086  * \note, theres nothing specific to operators here.
1087  * this could be made a general function.
1088  */
1089 bool WM_operator_properties_default(PointerRNA *ptr, const bool do_update)
1090 {
1091         bool changed = false;
1092         RNA_STRUCT_BEGIN (ptr, prop)
1093         {
1094                 switch (RNA_property_type(prop)) {
1095                         case PROP_POINTER:
1096                         {
1097                                 StructRNA *ptype = RNA_property_pointer_type(ptr, prop);
1098                                 if (ptype != &RNA_Struct) {
1099                                         PointerRNA opptr = RNA_property_pointer_get(ptr, prop);
1100                                         changed |= WM_operator_properties_default(&opptr, do_update);
1101                                 }
1102                                 break;
1103                         }
1104                         default:
1105                                 if ((do_update == false) || (RNA_property_is_set(ptr, prop) == false)) {
1106                                         if (RNA_property_reset(ptr, prop, -1)) {
1107                                                 changed = true;
1108                                         }
1109                                 }
1110                                 break;
1111                 }
1112         }
1113         RNA_STRUCT_END;
1114
1115         return changed;
1116 }
1117
1118 /* remove all props without PROP_SKIP_SAVE */
1119 void WM_operator_properties_reset(wmOperator *op)
1120 {
1121         if (op->ptr->data) {
1122                 PropertyRNA *iterprop;
1123                 iterprop = RNA_struct_iterator_property(op->type->srna);
1124
1125                 RNA_PROP_BEGIN (op->ptr, itemptr, iterprop)
1126                 {
1127                         PropertyRNA *prop = itemptr.data;
1128
1129                         if ((RNA_property_flag(prop) & PROP_SKIP_SAVE) == 0) {
1130                                 const char *identifier = RNA_property_identifier(prop);
1131                                 RNA_struct_idprops_unset(op->ptr, identifier);
1132                         }
1133                 }
1134                 RNA_PROP_END;
1135         }
1136 }
1137
1138 void WM_operator_properties_clear(PointerRNA *ptr)
1139 {
1140         IDProperty *properties = ptr->data;
1141
1142         if (properties) {
1143                 IDP_ClearProperty(properties);
1144         }
1145 }
1146
1147 void WM_operator_properties_free(PointerRNA *ptr)
1148 {
1149         IDProperty *properties = ptr->data;
1150
1151         if (properties) {
1152                 IDP_FreeProperty(properties);
1153                 MEM_freeN(properties);
1154                 ptr->data = NULL; /* just in case */
1155         }
1156 }
1157
1158 /* ************ default op callbacks, exported *********** */
1159
1160 void WM_operator_view3d_unit_defaults(struct bContext *C, struct wmOperator *op)
1161 {
1162         if (op->flag & OP_IS_INVOKE) {
1163                 Scene *scene = CTX_data_scene(C);
1164                 View3D *v3d = CTX_wm_view3d(C);
1165
1166                 const float dia = v3d ? ED_view3d_grid_scale(scene, v3d, NULL) : ED_scene_grid_scale(scene, NULL);
1167
1168                 /* always run, so the values are initialized,
1169                  * otherwise we may get differ behavior when (dia != 1.0) */
1170                 RNA_STRUCT_BEGIN (op->ptr, prop)
1171                 {
1172                         if (RNA_property_type(prop) == PROP_FLOAT) {
1173                                 PropertySubType pstype = RNA_property_subtype(prop);
1174                                 if (pstype == PROP_DISTANCE) {
1175                                         /* we don't support arrays yet */
1176                                         BLI_assert(RNA_property_array_check(prop) == false);
1177                                         /* initialize */
1178                                         if (!RNA_property_is_set_ex(op->ptr, prop, false)) {
1179                                                 const float value = RNA_property_float_get_default(op->ptr, prop) * dia;
1180                                                 RNA_property_float_set(op->ptr, prop, value);
1181                                         }
1182                                 }
1183                         }
1184                 }
1185                 RNA_STRUCT_END;
1186         }
1187 }
1188
1189 int WM_operator_smooth_viewtx_get(const wmOperator *op)
1190 {
1191         return (op->flag & OP_IS_INVOKE) ? U.smooth_viewtx : 0;
1192 }
1193
1194 /* invoke callback, uses enum property named "type" */
1195 int WM_menu_invoke_ex(bContext *C, wmOperator *op, int opcontext)
1196 {
1197         PropertyRNA *prop = op->type->prop;
1198         uiPopupMenu *pup;
1199         uiLayout *layout;
1200
1201         if (prop == NULL) {
1202                 CLOG_ERROR(WM_LOG_OPERATORS,
1203                            "'%s' has no enum property set",
1204                            op->type->idname);
1205         }
1206         else if (RNA_property_type(prop) != PROP_ENUM) {
1207                 CLOG_ERROR(WM_LOG_OPERATORS,
1208                            "'%s', '%s' is not an enum property",
1209                            op->type->idname, RNA_property_identifier(prop));
1210         }
1211         else if (RNA_property_is_set(op->ptr, prop)) {
1212                 const int retval = op->type->exec(C, op);
1213                 OPERATOR_RETVAL_CHECK(retval);
1214                 return retval;
1215         }
1216         else {
1217                 pup = UI_popup_menu_begin(C, RNA_struct_ui_name(op->type->srna), ICON_NONE);
1218                 layout = UI_popup_menu_layout(pup);
1219                 /* set this so the default execution context is the same as submenus */
1220                 uiLayoutSetOperatorContext(layout, opcontext);
1221                 uiItemsFullEnumO(layout, op->type->idname, RNA_property_identifier(prop), op->ptr->data, opcontext, 0);
1222                 UI_popup_menu_end(C, pup);
1223                 return OPERATOR_INTERFACE;
1224         }
1225
1226         return OPERATOR_CANCELLED;
1227 }
1228
1229 int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1230 {
1231         return WM_menu_invoke_ex(C, op, WM_OP_INVOKE_REGION_WIN);
1232 }
1233
1234 struct EnumSearchMenu {
1235         wmOperator *op; /* the operator that will be executed when selecting an item */
1236
1237         bool use_previews;
1238         short prv_cols, prv_rows;
1239 };
1240
1241 /* generic enum search invoke popup */
1242 static uiBlock *wm_enum_search_menu(bContext *C, ARegion *ar, void *arg)
1243 {
1244         struct EnumSearchMenu *search_menu = arg;
1245         wmWindow *win = CTX_wm_window(C);
1246         wmOperator *op = search_menu->op;
1247         /* template_ID uses 4 * widget_unit for width, we use a bit more, some items may have a suffix to show */
1248         const int width = search_menu->use_previews ? 5 * U.widget_unit * search_menu->prv_cols : UI_searchbox_size_x();
1249         const int height = search_menu->use_previews ? 5 * U.widget_unit * search_menu->prv_rows : UI_searchbox_size_y();
1250         static char search[256] = "";
1251         uiBlock *block;
1252         uiBut *but;
1253
1254         block = UI_block_begin(C, ar, "_popup", UI_EMBOSS);
1255         UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_SEARCH_MENU);
1256
1257         search[0] = '\0';
1258         BLI_assert(search_menu->use_previews || (search_menu->prv_cols == 0 && search_menu->prv_rows == 0));
1259 #if 0 /* ok, this isn't so easy... */
1260         uiDefBut(block, UI_BTYPE_LABEL, 0, RNA_struct_ui_name(op->type->srna), 10, 10, UI_searchbox_size_x(), UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
1261 #endif
1262         but = uiDefSearchButO_ptr(block, op->type, op->ptr->data, search, 0, ICON_VIEWZOOM, sizeof(search),
1263                                   10, 10, width, UI_UNIT_Y, search_menu->prv_rows, search_menu->prv_cols, "");
1264
1265         /* fake button, it holds space for search items */
1266         uiDefBut(block, UI_BTYPE_LABEL, 0, "", 10, 10 - UI_searchbox_size_y(), width, height, NULL, 0, 0, 0, 0, NULL);
1267
1268         UI_block_bounds_set_popup(block, 6, 0, -UI_UNIT_Y); /* move it downwards, mouse over button */
1269         UI_but_focus_on_enter_event(win, but);
1270
1271         return block;
1272 }
1273
1274 /**
1275  * Similar to #WM_enum_search_invoke, but draws previews. Also, this can't
1276  * be used as invoke callback directly since it needs additional info.
1277  */
1278 int WM_enum_search_invoke_previews(
1279         bContext *C, wmOperator *op, short prv_cols, short prv_rows)
1280 {
1281         static struct EnumSearchMenu search_menu;
1282
1283         search_menu.op = op;
1284         search_menu.use_previews = true;
1285         search_menu.prv_cols = prv_cols;
1286         search_menu.prv_rows = prv_rows;
1287
1288         UI_popup_block_invoke(C, wm_enum_search_menu, &search_menu);
1289
1290         return OPERATOR_INTERFACE;
1291 }
1292
1293 int WM_enum_search_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1294 {
1295         static struct EnumSearchMenu search_menu;
1296         search_menu.op = op;
1297         UI_popup_block_invoke(C, wm_enum_search_menu, &search_menu);
1298         return OPERATOR_INTERFACE;
1299 }
1300
1301 /* Can't be used as an invoke directly, needs message arg (can be NULL) */
1302 int WM_operator_confirm_message_ex(bContext *C, wmOperator *op,
1303                                    const char *title, const int icon,
1304                                    const char *message)
1305 {
1306         uiPopupMenu *pup;
1307         uiLayout *layout;
1308         IDProperty *properties = op->ptr->data;
1309
1310         if (properties && properties->len)
1311                 properties = IDP_CopyProperty(op->ptr->data);
1312         else
1313                 properties = NULL;
1314
1315         pup = UI_popup_menu_begin(C, title, icon);
1316         layout = UI_popup_menu_layout(pup);
1317         uiItemFullO_ptr(layout, op->type, message, ICON_NONE, properties, WM_OP_EXEC_REGION_WIN, 0, NULL);
1318         UI_popup_menu_end(C, pup);
1319
1320         return OPERATOR_INTERFACE;
1321 }
1322
1323 int WM_operator_confirm_message(bContext *C, wmOperator *op, const char *message)
1324 {
1325         return WM_operator_confirm_message_ex(C, op, IFACE_("OK?"), ICON_QUESTION, message);
1326 }
1327
1328 int WM_operator_confirm(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1329 {
1330         return WM_operator_confirm_message(C, op, NULL);
1331 }
1332
1333 /* op->invoke, opens fileselect if path property not set, otherwise executes */
1334 int WM_operator_filesel(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1335 {
1336         if (RNA_struct_property_is_set(op->ptr, "filepath")) {
1337                 return WM_operator_call_notest(C, op); /* call exec direct */
1338         }
1339         else {
1340                 WM_event_add_fileselect(C, op);
1341                 return OPERATOR_RUNNING_MODAL;
1342         }
1343 }
1344
1345 bool WM_operator_filesel_ensure_ext_imtype(wmOperator *op, const struct ImageFormatData *im_format)
1346 {
1347         PropertyRNA *prop;
1348         char filepath[FILE_MAX];
1349         /* dont NULL check prop, this can only run on ops with a 'filepath' */
1350         prop = RNA_struct_find_property(op->ptr, "filepath");
1351         RNA_property_string_get(op->ptr, prop, filepath);
1352         if (BKE_image_path_ensure_ext_from_imformat(filepath, im_format)) {
1353                 RNA_property_string_set(op->ptr, prop, filepath);
1354                 /* note, we could check for and update 'filename' here,
1355                  * but so far nothing needs this. */
1356                 return true;
1357         }
1358         return false;
1359 }
1360
1361 /* op->poll */
1362 bool WM_operator_winactive(bContext *C)
1363 {
1364         if (CTX_wm_window(C) == NULL) return 0;
1365         return 1;
1366 }
1367
1368 /* return false, if the UI should be disabled */
1369 bool WM_operator_check_ui_enabled(const bContext *C, const char *idname)
1370 {
1371         wmWindowManager *wm = CTX_wm_manager(C);
1372         Scene *scene = CTX_data_scene(C);
1373
1374         return !((ED_undo_is_valid(C, idname) == false) || WM_jobs_test(wm, scene, WM_JOB_TYPE_ANY));
1375 }
1376
1377 wmOperator *WM_operator_last_redo(const bContext *C)
1378 {
1379         wmWindowManager *wm = CTX_wm_manager(C);
1380         wmOperator *op;
1381
1382         /* only for operators that are registered and did an undo push */
1383         for (op = wm->operators.last; op; op = op->prev)
1384                 if ((op->type->flag & OPTYPE_REGISTER) && (op->type->flag & OPTYPE_UNDO))
1385                         break;
1386
1387         return op;
1388 }
1389
1390 IDProperty *WM_operator_last_properties_ensure_idprops(wmOperatorType *ot)
1391 {
1392         if (ot->last_properties == NULL) {
1393                 IDPropertyTemplate val = {0};
1394                 ot->last_properties = IDP_New(IDP_GROUP, &val, "wmOperatorProperties");
1395         }
1396         return ot->last_properties;
1397 }
1398
1399 void WM_operator_last_properties_ensure(wmOperatorType *ot, PointerRNA *ptr)
1400 {
1401         IDProperty *props = WM_operator_last_properties_ensure_idprops(ot);
1402         RNA_pointer_create(NULL, ot->srna, props, ptr);
1403 }
1404
1405 /**
1406  * Use for drag & drop a path or name with operators invoke() function.
1407  */
1408 ID *WM_operator_drop_load_path(struct bContext *C, wmOperator *op, const short idcode)
1409 {
1410         Main *bmain = CTX_data_main(C);
1411         ID *id = NULL;
1412         /* check input variables */
1413         if (RNA_struct_property_is_set(op->ptr, "filepath")) {
1414                 const bool is_relative_path = RNA_boolean_get(op->ptr, "relative_path");
1415                 char path[FILE_MAX];
1416                 bool exists = false;
1417
1418                 RNA_string_get(op->ptr, "filepath", path);
1419
1420                 errno = 0;
1421
1422                 if (idcode == ID_IM) {
1423                         id = (ID *)BKE_image_load_exists_ex(bmain, path, &exists);
1424                 }
1425                 else {
1426                         BLI_assert(0);
1427                 }
1428
1429                 if (!id) {
1430                         BKE_reportf(op->reports, RPT_ERROR, "Cannot read %s '%s': %s",
1431                                     BKE_idcode_to_name(idcode), path,
1432                                     errno ? strerror(errno) : TIP_("unsupported format"));
1433                         return NULL;
1434                 }
1435
1436                 if (is_relative_path ) {
1437                         if (exists == false) {
1438                                 if (idcode == ID_IM) {
1439                                         BLI_path_rel(((Image *)id)->name, BKE_main_blendfile_path(bmain));
1440                                 }
1441                                 else {
1442                                         BLI_assert(0);
1443                                 }
1444                         }
1445                 }
1446         }
1447         else if (RNA_struct_property_is_set(op->ptr, "name")) {
1448                 char name[MAX_ID_NAME - 2];
1449                 RNA_string_get(op->ptr, "name", name);
1450                 id = BKE_libblock_find_name(bmain, idcode, name);
1451                 if (!id) {
1452                         BKE_reportf(op->reports, RPT_ERROR, "%s '%s' not found",
1453                                     BKE_idcode_to_name(idcode), name);
1454                         return NULL;
1455                 }
1456                 id_us_plus(id);
1457         }
1458
1459         return id;
1460 }
1461
1462 static void wm_block_redo_cb(bContext *C, void *arg_op, int UNUSED(arg_event))
1463 {
1464         wmOperator *op = arg_op;
1465
1466         if (op == WM_operator_last_redo(C)) {
1467                 /* operator was already executed once? undo & repeat */
1468                 ED_undo_operator_repeat(C, op);
1469         }
1470         else {
1471                 /* operator not executed yet, call it */
1472                 ED_undo_push_op(C, op);
1473                 wm_operator_register(C, op);
1474
1475                 WM_operator_repeat(C, op);
1476         }
1477 }
1478
1479 static void wm_block_redo_cancel_cb(bContext *C, void *arg_op)
1480 {
1481         wmOperator *op = arg_op;
1482
1483         /* if operator never got executed, free it */
1484         if (op != WM_operator_last_redo(C))
1485                 WM_operator_free(op);
1486 }
1487
1488 static uiBlock *wm_block_create_redo(bContext *C, ARegion *ar, void *arg_op)
1489 {
1490         wmOperator *op = arg_op;
1491         uiBlock *block;
1492         uiLayout *layout;
1493         uiStyle *style = UI_style_get();
1494         int width = 15 * UI_UNIT_X;
1495
1496         block = UI_block_begin(C, ar, __func__, UI_EMBOSS);
1497         UI_block_flag_disable(block, UI_BLOCK_LOOP);
1498         /* UI_BLOCK_NUMSELECT for layer buttons */
1499         UI_block_flag_enable(block, UI_BLOCK_NUMSELECT | UI_BLOCK_KEEP_OPEN | UI_BLOCK_MOVEMOUSE_QUIT);
1500
1501         /* if register is not enabled, the operator gets freed on OPERATOR_FINISHED
1502          * ui_apply_but_funcs_after calls ED_undo_operator_repeate_cb and crashes */
1503         assert(op->type->flag & OPTYPE_REGISTER);
1504
1505         UI_block_func_handle_set(block, wm_block_redo_cb, arg_op);
1506         layout = UI_block_layout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, width, UI_UNIT_Y, 0, style);
1507
1508         if (op == WM_operator_last_redo(C))
1509                 if (!WM_operator_check_ui_enabled(C, op->type->name))
1510                         uiLayoutSetEnabled(layout, false);
1511
1512         if (op->type->flag & OPTYPE_MACRO) {
1513                 for (op = op->macro.first; op; op = op->next) {
1514                         uiTemplateOperatorPropertyButs(C, layout, op, NULL, UI_BUT_LABEL_ALIGN_SPLIT_COLUMN,
1515                                                        UI_TEMPLATE_OP_PROPS_SHOW_TITLE);
1516                         if (op->next)
1517                                 uiItemS(layout);
1518                 }
1519         }
1520         else {
1521                 uiTemplateOperatorPropertyButs(C, layout, op, NULL, UI_BUT_LABEL_ALIGN_SPLIT_COLUMN,
1522                                                UI_TEMPLATE_OP_PROPS_SHOW_TITLE);
1523         }
1524
1525         UI_block_bounds_set_popup(block, 4, 0, 0);
1526
1527         return block;
1528 }
1529
1530 typedef struct wmOpPopUp {
1531         wmOperator *op;
1532         int width;
1533         int height;
1534         int free_op;
1535 } wmOpPopUp;
1536
1537 /* Only invoked by OK button in popups created with wm_block_dialog_create() */
1538 static void dialog_exec_cb(bContext *C, void *arg1, void *arg2)
1539 {
1540         wmOpPopUp *data = arg1;
1541         uiBlock *block = arg2;
1542
1543         /* Explicitly set UI_RETURN_OK flag, otherwise the menu might be canceled
1544          * in case WM_operator_call_ex exits/reloads the current file (T49199). */
1545         UI_popup_menu_retval_set(block, UI_RETURN_OK, true);
1546
1547         WM_operator_call_ex(C, data->op, true);
1548
1549         /* let execute handle freeing it */
1550         //data->free_op = false;
1551         //data->op = NULL;
1552
1553         /* in this case, wm_operator_ui_popup_cancel wont run */
1554         MEM_freeN(data);
1555
1556         /* get context data *after* WM_operator_call_ex which might have closed the current file and changed context */
1557         wmWindowManager *wm = CTX_wm_manager(C);
1558         wmWindow *win = CTX_wm_window(C);
1559
1560         /* check window before 'block->handle' incase the
1561          * popup execution closed the window and freed the block. see T44688.
1562          */
1563         /* Post 2.78 TODO: Check if this fix and others related to T44688 are still
1564          * needed or can be improved now that requesting context data has been corrected
1565          * (see above). We're close to release so not a good time for experiments.
1566          * -- Julian
1567          */
1568         if (BLI_findindex(&wm->windows, win) != -1) {
1569                 UI_popup_block_close(C, win, block);
1570         }
1571 }
1572
1573 /* Dialogs are popups that require user verification (click OK) before exec */
1574 static uiBlock *wm_block_dialog_create(bContext *C, ARegion *ar, void *userData)
1575 {
1576         wmOpPopUp *data = userData;
1577         wmOperator *op = data->op;
1578         uiBlock *block;
1579         uiLayout *layout;
1580         uiStyle *style = UI_style_get();
1581
1582         block = UI_block_begin(C, ar, __func__, UI_EMBOSS);
1583         UI_block_flag_disable(block, UI_BLOCK_LOOP);
1584
1585         /* intentionally don't use 'UI_BLOCK_MOVEMOUSE_QUIT', some dialogues have many items
1586          * where quitting by accident is very annoying */
1587         UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_NUMSELECT);
1588
1589         layout = UI_block_layout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, data->width, data->height, 0, style);
1590
1591         uiTemplateOperatorPropertyButs(C, layout, op, NULL, UI_BUT_LABEL_ALIGN_SPLIT_COLUMN,
1592                                        UI_TEMPLATE_OP_PROPS_SHOW_TITLE);
1593
1594         /* clear so the OK button is left alone */
1595         UI_block_func_set(block, NULL, NULL, NULL);
1596
1597         /* new column so as not to interfere with custom layouts [#26436] */
1598         {
1599                 uiBlock *col_block;
1600                 uiLayout *col;
1601                 uiBut *btn;
1602
1603                 col = uiLayoutColumn(layout, false);
1604                 col_block = uiLayoutGetBlock(col);
1605                 /* Create OK button, the callback of which will execute op */
1606                 btn = uiDefBut(col_block, UI_BTYPE_BUT, 0, IFACE_("OK"), 0, -30, 0, UI_UNIT_Y, NULL, 0, 0, 0, 0, "");
1607                 UI_but_func_set(btn, dialog_exec_cb, data, col_block);
1608         }
1609
1610         /* center around the mouse */
1611         UI_block_bounds_set_popup(block, 4, data->width / -2, data->height / 2);
1612
1613         return block;
1614 }
1615
1616 static uiBlock *wm_operator_ui_create(bContext *C, ARegion *ar, void *userData)
1617 {
1618         wmOpPopUp *data = userData;
1619         wmOperator *op = data->op;
1620         uiBlock *block;
1621         uiLayout *layout;
1622         uiStyle *style = UI_style_get();
1623
1624         block = UI_block_begin(C, ar, __func__, UI_EMBOSS);
1625         UI_block_flag_disable(block, UI_BLOCK_LOOP);
1626         UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_MOVEMOUSE_QUIT);
1627
1628         layout = UI_block_layout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, data->width, data->height, 0, style);
1629
1630         /* since ui is defined the auto-layout args are not used */
1631         uiTemplateOperatorPropertyButs(C, layout, op, NULL, UI_BUT_LABEL_ALIGN_COLUMN, 0);
1632
1633         UI_block_func_set(block, NULL, NULL, NULL);
1634
1635         UI_block_bounds_set_popup(block, 4, 0, 0);
1636
1637         return block;
1638 }
1639
1640 static void wm_operator_ui_popup_cancel(struct bContext *C, void *userData)
1641 {
1642         wmOpPopUp *data = userData;
1643         wmOperator *op = data->op;
1644
1645         if (op) {
1646                 if (op->type->cancel) {
1647                         op->type->cancel(C, op);
1648                 }
1649
1650                 if (data->free_op) {
1651                         WM_operator_free(op);
1652                 }
1653         }
1654
1655         MEM_freeN(data);
1656 }
1657
1658 static void wm_operator_ui_popup_ok(struct bContext *C, void *arg, int retval)
1659 {
1660         wmOpPopUp *data = arg;
1661         wmOperator *op = data->op;
1662
1663         if (op && retval > 0)
1664                 WM_operator_call_ex(C, op, true);
1665
1666         MEM_freeN(data);
1667 }
1668
1669 int WM_operator_ui_popup(bContext *C, wmOperator *op, int width, int height)
1670 {
1671         wmOpPopUp *data = MEM_callocN(sizeof(wmOpPopUp), "WM_operator_ui_popup");
1672         data->op = op;
1673         data->width = width;
1674         data->height = height;
1675         data->free_op = true; /* if this runs and gets registered we may want not to free it */
1676         UI_popup_block_ex(C, wm_operator_ui_create, NULL, wm_operator_ui_popup_cancel, data, op);
1677         return OPERATOR_RUNNING_MODAL;
1678 }
1679
1680 /**
1681  * For use by #WM_operator_props_popup_call, #WM_operator_props_popup only.
1682  *
1683  * \note operator menu needs undo flag enabled, for redo callback */
1684 static int wm_operator_props_popup_ex(bContext *C, wmOperator *op,
1685                                       const bool do_call, const bool do_redo)
1686 {
1687         if ((op->type->flag & OPTYPE_REGISTER) == 0) {
1688                 BKE_reportf(op->reports, RPT_ERROR,
1689                             "Operator '%s' does not have register enabled, incorrect invoke function", op->type->idname);
1690                 return OPERATOR_CANCELLED;
1691         }
1692
1693         if (do_redo) {
1694                 if ((op->type->flag & OPTYPE_UNDO) == 0) {
1695                         BKE_reportf(op->reports, RPT_ERROR,
1696                                     "Operator '%s' does not have undo enabled, incorrect invoke function", op->type->idname);
1697                         return OPERATOR_CANCELLED;
1698                 }
1699         }
1700
1701         /* if we don't have global undo, we can't do undo push for automatic redo,
1702          * so we require manual OK clicking in this popup */
1703         if (!do_redo || !(U.uiflag & USER_GLOBALUNDO))
1704                 return WM_operator_props_dialog_popup(C, op, 15 * UI_UNIT_X, UI_UNIT_Y);
1705
1706         UI_popup_block_ex(C, wm_block_create_redo, NULL, wm_block_redo_cancel_cb, op, op);
1707
1708         if (do_call)
1709                 wm_block_redo_cb(C, op, 0);
1710
1711         return OPERATOR_RUNNING_MODAL;
1712 }
1713
1714 /**
1715  * Same as #WM_operator_props_popup but don't use operator redo.
1716  * just wraps #WM_operator_props_dialog_popup.
1717  */
1718 int WM_operator_props_popup_confirm(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1719 {
1720         return wm_operator_props_popup_ex(C, op, false, false);
1721 }
1722
1723 /**
1724  * Same as #WM_operator_props_popup but call the operator first,
1725  * This way - the button values correspond to the result of the operator.
1726  * Without this, first access to a button will make the result jump, see T32452.
1727  */
1728 int WM_operator_props_popup_call(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1729 {
1730         return wm_operator_props_popup_ex(C, op, true, true);
1731 }
1732
1733 int WM_operator_props_popup(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1734 {
1735         return wm_operator_props_popup_ex(C, op, false, true);
1736 }
1737
1738 int WM_operator_props_dialog_popup(bContext *C, wmOperator *op, int width, int height)
1739 {
1740         wmOpPopUp *data = MEM_callocN(sizeof(wmOpPopUp), "WM_operator_props_dialog_popup");
1741
1742         data->op = op;
1743         data->width = width;
1744         data->height = height;
1745         data->free_op = true; /* if this runs and gets registered we may want not to free it */
1746
1747         /* op is not executed until popup OK but is clicked */
1748         UI_popup_block_ex(C, wm_block_dialog_create, wm_operator_ui_popup_ok, wm_operator_ui_popup_cancel, data, op);
1749
1750         return OPERATOR_RUNNING_MODAL;
1751 }
1752
1753 int WM_operator_redo_popup(bContext *C, wmOperator *op)
1754 {
1755         /* CTX_wm_reports(C) because operator is on stack, not active in event system */
1756         if ((op->type->flag & OPTYPE_REGISTER) == 0) {
1757                 BKE_reportf(CTX_wm_reports(C), RPT_ERROR,
1758                             "Operator redo '%s' does not have register enabled, incorrect invoke function", op->type->idname);
1759                 return OPERATOR_CANCELLED;
1760         }
1761         if (op->type->poll && op->type->poll(C) == 0) {
1762                 BKE_reportf(CTX_wm_reports(C), RPT_ERROR, "Operator redo '%s': wrong context", op->type->idname);
1763                 return OPERATOR_CANCELLED;
1764         }
1765
1766         UI_popup_block_invoke(C, wm_block_create_redo, op);
1767
1768         return OPERATOR_CANCELLED;
1769 }
1770
1771 /* ***************** Debug menu ************************* */
1772
1773 static int wm_debug_menu_exec(bContext *C, wmOperator *op)
1774 {
1775         G.debug_value = RNA_int_get(op->ptr, "debug_value");
1776         ED_screen_refresh(CTX_wm_manager(C), CTX_wm_window(C));
1777         WM_event_add_notifier(C, NC_WINDOW, NULL);
1778
1779         return OPERATOR_FINISHED;
1780 }
1781
1782 static int wm_debug_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1783 {
1784         RNA_int_set(op->ptr, "debug_value", G.debug_value);
1785         return WM_operator_props_dialog_popup(C, op, 9 * UI_UNIT_X, UI_UNIT_Y);
1786 }
1787
1788 static void WM_OT_debug_menu(wmOperatorType *ot)
1789 {
1790         ot->name = "Debug Menu";
1791         ot->idname = "WM_OT_debug_menu";
1792         ot->description = "Open a popup to set the debug level";
1793
1794         ot->invoke = wm_debug_menu_invoke;
1795         ot->exec = wm_debug_menu_exec;
1796         ot->poll = WM_operator_winactive;
1797
1798         RNA_def_int(ot->srna, "debug_value", 0, SHRT_MIN, SHRT_MAX, "Debug Value", "", -10000, 10000);
1799 }
1800
1801 /* ***************** Operator defaults ************************* */
1802 static int wm_operator_defaults_exec(bContext *C, wmOperator *op)
1803 {
1804         PointerRNA ptr = CTX_data_pointer_get_type(C, "active_operator", &RNA_Operator);
1805
1806         if (!ptr.data) {
1807                 BKE_report(op->reports, RPT_ERROR, "No operator in context");
1808                 return OPERATOR_CANCELLED;
1809         }
1810
1811         WM_operator_properties_reset((wmOperator *)ptr.data);
1812         return OPERATOR_FINISHED;
1813 }
1814
1815 /* used by operator preset menu. pre-2.65 this was a 'Reset' button */
1816 static void WM_OT_operator_defaults(wmOperatorType *ot)
1817 {
1818         ot->name = "Restore Defaults";
1819         ot->idname = "WM_OT_operator_defaults";
1820         ot->description = "Set the active operator to its default values";
1821
1822         ot->exec = wm_operator_defaults_exec;
1823
1824         ot->flag = OPTYPE_INTERNAL;
1825 }
1826
1827 /* ***************** Splash Screen ************************* */
1828
1829 static void wm_block_splash_close(bContext *C, void *arg_block, void *UNUSED(arg))
1830 {
1831         wmWindow *win = CTX_wm_window(C);
1832         UI_popup_block_close(C, win, arg_block);
1833 }
1834
1835 static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *arg_unused);
1836
1837 static void wm_block_splash_refreshmenu(bContext *C, void *UNUSED(arg_block), void *UNUSED(arg))
1838 {
1839         ARegion *ar_menu = CTX_wm_menu(C);
1840         ED_region_tag_refresh_ui(ar_menu);
1841 }
1842
1843 static int wm_resource_check_prev(void)
1844 {
1845
1846         const char *res = BKE_appdir_folder_id_version(BLENDER_RESOURCE_PATH_USER, BLENDER_VERSION, true);
1847
1848         // if (res) printf("USER: %s\n", res);
1849
1850 #if 0 /* ignore the local folder */
1851         if (res == NULL) {
1852                 /* with a local dir, copying old files isn't useful since local dir get priority for config */
1853                 res = BKE_appdir_folder_id_version(BLENDER_RESOURCE_PATH_LOCAL, BLENDER_VERSION, true);
1854         }
1855 #endif
1856
1857         // if (res) printf("LOCAL: %s\n", res);
1858         if (res) {
1859                 return false;
1860         }
1861         else {
1862                 return (BKE_appdir_folder_id_version(BLENDER_RESOURCE_PATH_USER, BLENDER_VERSION - 1, true) != NULL);
1863         }
1864 }
1865
1866 static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(arg))
1867 {
1868         uiBlock *block;
1869         uiBut *but;
1870         uiLayout *layout, *split, *col;
1871         uiStyle *style = UI_style_get();
1872         const struct RecentFile *recent;
1873         int i;
1874         MenuType *mt = WM_menutype_find("USERPREF_MT_splash", true);
1875         char url[96];
1876         const char *version_suffix = NULL;
1877
1878 #ifndef WITH_HEADLESS
1879         extern char datatoc_splash_png[];
1880         extern int datatoc_splash_png_size;
1881
1882         extern char datatoc_splash_2x_png[];
1883         extern int datatoc_splash_2x_png_size;
1884         ImBuf *ibuf;
1885 #else
1886         ImBuf *ibuf = NULL;
1887 #endif
1888
1889 #ifdef WITH_BUILDINFO
1890         int label_delta = 0;
1891         int hash_width, date_width;
1892         char date_buf[128] = "\0";
1893         char hash_buf[128] = "\0";
1894         extern unsigned long build_commit_timestamp;
1895         extern char build_hash[], build_commit_date[], build_commit_time[], build_branch[];
1896
1897         /* Builds made from tag only shows tag sha */
1898         BLI_snprintf(hash_buf, sizeof(hash_buf), "Hash: %s", build_hash);
1899         BLI_snprintf(date_buf, sizeof(date_buf), "Date: %s %s", build_commit_date, build_commit_time);
1900
1901         BLF_size(style->widgetlabel.uifont_id, style->widgetlabel.points, U.pixelsize * U.dpi);
1902         hash_width = (int)BLF_width(style->widgetlabel.uifont_id, hash_buf, sizeof(hash_buf)) + U.widget_unit;
1903         date_width = (int)BLF_width(style->widgetlabel.uifont_id, date_buf, sizeof(date_buf)) + U.widget_unit;
1904 #endif  /* WITH_BUILDINFO */
1905
1906 #ifndef WITH_HEADLESS
1907         if (U.pixelsize == 2) {
1908                 ibuf = IMB_ibImageFromMemory((unsigned char *)datatoc_splash_2x_png,
1909                                              datatoc_splash_2x_png_size, IB_rect, NULL, "<splash screen>");
1910         }
1911         else {
1912                 ibuf = IMB_ibImageFromMemory((unsigned char *)datatoc_splash_png,
1913                                              datatoc_splash_png_size, IB_rect, NULL, "<splash screen>");
1914         }
1915
1916         /* overwrite splash with template image */
1917         if (U.app_template[0] != '\0') {
1918                 ImBuf *ibuf_template = NULL;
1919                 char splash_filepath[FILE_MAX];
1920                 char template_directory[FILE_MAX];
1921
1922                 if (BKE_appdir_app_template_id_search(
1923                         U.app_template,
1924                         template_directory, sizeof(template_directory)))
1925                 {
1926                         BLI_join_dirfile(
1927                                 splash_filepath, sizeof(splash_filepath), template_directory,
1928                                 (U.pixelsize == 2) ? "splash_2x.png" : "splash.png");
1929                         ibuf_template = IMB_loadiffname(splash_filepath, IB_rect, NULL);
1930                         if (ibuf_template) {
1931                                 const int x_expect = ibuf->x;
1932                                 const int y_expect = 282 * (int)U.pixelsize;
1933                                 /* don't cover the header text */
1934                                 if (ibuf_template->x == x_expect && ibuf_template->y == y_expect) {
1935                                         memcpy(ibuf->rect, ibuf_template->rect, ibuf_template->x * ibuf_template->y * sizeof(char[4]));
1936                                 }
1937                                 else {
1938                                         CLOG_ERROR(WM_LOG_OPERATORS,
1939                                                    "Splash expected %dx%d found %dx%d, ignoring: %s\n",
1940                                                    x_expect, y_expect, ibuf_template->x, ibuf_template->y, splash_filepath);
1941                                 }
1942                                 IMB_freeImBuf(ibuf_template);
1943                         }
1944                 }
1945         }
1946 #endif
1947
1948         block = UI_block_begin(C, ar, "_popup", UI_EMBOSS);
1949
1950         /* note on UI_BLOCK_NO_WIN_CLIP, the window size is not always synchronized
1951          * with the OS when the splash shows, window clipping in this case gives
1952          * ugly results and clipping the splash isn't useful anyway, just disable it [#32938] */
1953         UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_KEEP_OPEN | UI_BLOCK_NO_WIN_CLIP);
1954
1955         /* XXX splash scales with pixelsize, should become widget-units */
1956         but = uiDefBut(block, UI_BTYPE_IMAGE, 0, "", 0, 0.5f * U.widget_unit, U.pixelsize * 501, U.pixelsize * 282, ibuf, 0.0, 0.0, 0, 0, ""); /* button owns the imbuf now */
1957         UI_but_func_set(but, wm_block_splash_close, block, NULL);
1958         UI_block_func_set(block, wm_block_splash_refreshmenu, block, NULL);
1959
1960         /* label for 'a' bugfix releases, or 'Release Candidate 1'...
1961          *  avoids recreating splash for version updates */
1962         if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "alpha")) {
1963                 version_suffix = "Alpha";
1964         }
1965         else if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "beta")) {
1966                 version_suffix = "Beta";
1967         }
1968         else if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "rc")) {
1969                 version_suffix = "Release Candidate";
1970         }
1971         else if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "release")) {
1972                 version_suffix = STRINGIFY(BLENDER_VERSION_CHAR);
1973         }
1974         if (version_suffix != NULL && version_suffix[0]) {
1975                 /* placed after the version number in the image,
1976                  * placing y is tricky to match baseline */
1977                 int x = 236 * U.pixelsize - (2 * UI_DPI_FAC);
1978                 int y = 231 * U.pixelsize + (4 * UI_DPI_FAC);
1979                 int w = 240 * U.pixelsize;
1980
1981                 /* hack to have text draw 'text_sel' */
1982                 UI_block_emboss_set(block, UI_EMBOSS_NONE);
1983                 but = uiDefBut(block, UI_BTYPE_LABEL, 0, version_suffix, x, y, w, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL);
1984                 /* XXX, set internal flag - UI_SELECT */
1985                 UI_but_flag_enable(but, 1);
1986                 UI_block_emboss_set(block, UI_EMBOSS);
1987         }
1988
1989 #ifdef WITH_BUILDINFO
1990         if (build_commit_timestamp != 0) {
1991                 but = uiDefBut(
1992                           block, UI_BTYPE_LABEL, 0, date_buf,
1993                           U.pixelsize * 502 - date_width, U.pixelsize * 267,
1994                           date_width, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL);
1995                 /* XXX, set internal flag - UI_SELECT */
1996                 UI_but_flag_enable(but, 1);
1997                 label_delta = 12;
1998         }
1999         but = uiDefBut(
2000                   block, UI_BTYPE_LABEL, 0, hash_buf,
2001                   U.pixelsize * 502 - hash_width, U.pixelsize * (267 - label_delta),
2002                   hash_width, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL);
2003         /* XXX, set internal flag - UI_SELECT */
2004         UI_but_flag_enable(but, 1);
2005
2006         if (!STREQ(build_branch, "master")) {
2007                 char branch_buf[128] = "\0";
2008                 int branch_width;
2009                 BLI_snprintf(branch_buf, sizeof(branch_buf), "Branch: %s", build_branch);
2010                 branch_width = (int)BLF_width(style->widgetlabel.uifont_id, branch_buf, sizeof(branch_buf)) + U.widget_unit;
2011                 but = uiDefBut(
2012                           block, UI_BTYPE_LABEL, 0, branch_buf,
2013                           U.pixelsize * 502 - branch_width, U.pixelsize * (255 - label_delta),
2014                           branch_width, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL);
2015                 /* XXX, set internal flag - UI_SELECT */
2016                 UI_but_flag_enable(but, 1);
2017         }
2018 #endif  /* WITH_BUILDINFO */
2019
2020         layout = UI_block_layout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 10, 2, U.pixelsize * 480, U.pixelsize * 110, 0, style);
2021
2022         UI_block_emboss_set(block, UI_EMBOSS);
2023         /* show the splash menu (containing interaction presets), using python */
2024         if (mt) {
2025                 UI_menutype_draw(C, mt, layout);
2026
2027 //              uiItemM(layout, "USERPREF_MT_keyconfigs", U.keyconfigstr, ICON_NONE);
2028         }
2029
2030         UI_block_emboss_set(block, UI_EMBOSS_PULLDOWN);
2031         uiLayoutSetOperatorContext(layout, WM_OP_EXEC_REGION_WIN);
2032
2033         split = uiLayoutSplit(layout, 0.0f, false);
2034         col = uiLayoutColumn(split, false);
2035         uiItemL(col, IFACE_("Links"), ICON_NONE);
2036         uiItemStringO(col, IFACE_("Join the Development Fund"), ICON_URL, "WM_OT_url_open", "url",
2037                       "https://www.blender.org/foundation/development-fund/");
2038         uiItemStringO(col, IFACE_("Donations"), ICON_URL, "WM_OT_url_open", "url",
2039                       "http://www.blender.org/foundation/donation-payment/");
2040         uiItemStringO(col, IFACE_("Credits"), ICON_URL, "WM_OT_url_open", "url",
2041                       "http://www.blender.org/about/credits/");
2042         BLI_snprintf(url, sizeof(url), "http://wiki.blender.org/index.php/Dev:Ref/Release_Notes/%d.%d",
2043                      BLENDER_VERSION / 100, BLENDER_VERSION % 100);
2044         uiItemStringO(col, IFACE_("Release Log"), ICON_URL, "WM_OT_url_open", "url", url);
2045         uiItemStringO(col, IFACE_("Manual"), ICON_URL, "WM_OT_url_open", "url",
2046                       "https://docs.blender.org/manual/en/dev/");
2047         uiItemStringO(col, IFACE_("Blender Website"), ICON_URL, "WM_OT_url_open", "url", "http://www.blender.org");
2048         if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "release")) {
2049                 BLI_snprintf(url, sizeof(url), "https://docs.blender.org/api/%d.%d"STRINGIFY(BLENDER_VERSION_CHAR),
2050                              BLENDER_VERSION / 100, BLENDER_VERSION % 100);
2051         }
2052         else {
2053                 BLI_snprintf(url, sizeof(url), "https://docs.blender.org/api/master");
2054         }
2055         uiItemStringO(col, IFACE_("Python API Reference"), ICON_URL, "WM_OT_url_open", "url", url);
2056         uiItemL(col, "", ICON_NONE);
2057
2058         col = uiLayoutColumn(split, false);
2059
2060         if (wm_resource_check_prev()) {
2061                 uiItemO(col, NULL, ICON_NEW, "WM_OT_copy_prev_settings");
2062                 uiItemS(col);
2063         }
2064
2065         uiItemL(col, IFACE_("Recent"), ICON_NONE);
2066         for (recent = G.recent_files.first, i = 0; (i < 5) && (recent); recent = recent->next, i++) {
2067                 const char *filename = BLI_path_basename(recent->filepath);
2068                 uiItemStringO(col, filename,
2069                               BLO_has_bfile_extension(filename) ? ICON_FILE_BLEND : ICON_FILE_BACKUP,
2070                               "WM_OT_open_mainfile", "filepath", recent->filepath);
2071         }
2072
2073         uiItemS(col);
2074         uiItemO(col, NULL, ICON_RECOVER_LAST, "WM_OT_recover_last_session");
2075         uiItemL(col, "", ICON_NONE);
2076
2077         mt = WM_menutype_find("USERPREF_MT_splash_footer", false);
2078         if (mt) {
2079                 UI_menutype_draw(C, mt, uiLayoutColumn(layout, false));
2080         }
2081
2082         UI_block_bounds_set_centered(block, 0);
2083
2084         return block;
2085 }
2086
2087 static int wm_splash_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event))
2088 {
2089         UI_popup_block_invoke(C, wm_block_create_splash, NULL);
2090
2091         return OPERATOR_FINISHED;
2092 }
2093
2094 static void WM_OT_splash(wmOperatorType *ot)
2095 {
2096         ot->name = "Splash Screen";
2097         ot->idname = "WM_OT_splash";
2098         ot->description = "Open the splash screen with release info";
2099
2100         ot->invoke = wm_splash_invoke;
2101         ot->poll = WM_operator_winactive;
2102 }
2103
2104
2105 /* ***************** Search menu ************************* */
2106
2107 struct SearchPopupInit_Data {
2108         int size[2];
2109 };
2110
2111 static uiBlock *wm_block_search_menu(bContext *C, ARegion *ar, void *userdata)
2112 {
2113         const struct SearchPopupInit_Data *init_data = userdata;
2114         static char search[256] = "";
2115         wmEvent event;
2116         wmWindow *win = CTX_wm_window(C);
2117         uiBlock *block;
2118         uiBut *but;
2119
2120         block = UI_block_begin(C, ar, "_popup", UI_EMBOSS);
2121         UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_SEARCH_MENU);
2122
2123         but = uiDefSearchBut(block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 10, init_data->size[0], UI_UNIT_Y, 0, 0, "");
2124         UI_but_func_operator_search(but);
2125
2126         /* fake button, it holds space for search items */
2127         uiDefBut(block, UI_BTYPE_LABEL, 0, "", 10, 10 - init_data->size[1],
2128                  init_data->size[0], init_data->size[1], NULL, 0, 0, 0, 0, NULL);
2129
2130         UI_block_bounds_set_popup(block, 6, 0, -UI_UNIT_Y); /* move it downwards, mouse over button */
2131
2132         wm_event_init_from_window(win, &event);
2133         event.type = EVT_BUT_OPEN;
2134         event.val = KM_PRESS;
2135         event.customdata = but;
2136         event.customdatafree = false;
2137         wm_event_add(win, &event);
2138
2139         return block;
2140 }
2141
2142 static int wm_search_menu_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
2143 {
2144         return OPERATOR_FINISHED;
2145 }
2146
2147 static int wm_search_menu_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event))
2148 {
2149         struct SearchPopupInit_Data data = {
2150                 .size = {
2151                     UI_searchbox_size_x() * 2,
2152                     UI_searchbox_size_y(),
2153                 },
2154         };
2155
2156         UI_popup_block_invoke(C, wm_block_search_menu, &data);
2157
2158         return OPERATOR_INTERFACE;
2159 }
2160
2161 /* op->poll */
2162 static bool wm_search_menu_poll(bContext *C)
2163 {
2164         if (CTX_wm_window(C) == NULL) {
2165                 return 0;
2166         }
2167         else {
2168                 ScrArea *sa = CTX_wm_area(C);
2169                 if (sa) {
2170                         if (sa->spacetype == SPACE_CONSOLE) return 0;  /* XXX - so we can use the shortcut in the console */
2171                         if (sa->spacetype == SPACE_TEXT) return 0;     /* XXX - so we can use the spacebar in the text editor */
2172                 }
2173                 else {
2174                         Object *editob = CTX_data_edit_object(C);
2175                         if (editob && editob->type == OB_FONT) return 0;  /* XXX - so we can use the spacebar for entering text */
2176                 }
2177         }
2178         return 1;
2179 }
2180
2181 static void WM_OT_search_menu(wmOperatorType *ot)
2182 {
2183         ot->name = "Search Menu";
2184         ot->idname = "WM_OT_search_menu";
2185         ot->description = "Pop-up a search menu over all available operators in current context";
2186
2187         ot->invoke = wm_search_menu_invoke;
2188         ot->exec = wm_search_menu_exec;
2189         ot->poll = wm_search_menu_poll;
2190 }
2191
2192 static int wm_call_menu_exec(bContext *C, wmOperator *op)
2193 {
2194         char idname[BKE_ST_MAXNAME];
2195         RNA_string_get(op->ptr, "name", idname);
2196
2197         return UI_popup_menu_invoke(C, idname, op->reports);
2198 }
2199
2200 static void WM_OT_call_menu(wmOperatorType *ot)
2201 {
2202         ot->name = "Call Menu";
2203         ot->idname = "WM_OT_call_menu";
2204         ot->description = "Call (draw) a pre-defined menu";
2205
2206         ot->exec = wm_call_menu_exec;
2207         ot->poll = WM_operator_winactive;
2208
2209         ot->flag = OPTYPE_INTERNAL;
2210
2211         RNA_def_string(ot->srna, "name", NULL, BKE_ST_MAXNAME, "Name", "Name of the menu");
2212 }
2213
2214 static int wm_call_pie_menu_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2215 {
2216         char idname[BKE_ST_MAXNAME];
2217         RNA_string_get(op->ptr, "name", idname);
2218
2219         return UI_pie_menu_invoke(C, idname, event);
2220 }
2221
2222 static int wm_call_pie_menu_exec(bContext *C, wmOperator *op)
2223 {
2224         char idname[BKE_ST_MAXNAME];
2225         RNA_string_get(op->ptr, "name", idname);
2226
2227         return UI_pie_menu_invoke(C, idname, CTX_wm_window(C)->eventstate);
2228 }
2229
2230 static void WM_OT_call_menu_pie(wmOperatorType *ot)
2231 {
2232         ot->name = "Call Pie Menu";
2233         ot->idname = "WM_OT_call_menu_pie";
2234         ot->description = "Call (draw) a pre-defined pie menu";
2235
2236         ot->invoke = wm_call_pie_menu_invoke;
2237         ot->exec = wm_call_pie_menu_exec;
2238         ot->poll = WM_operator_winactive;
2239
2240         ot->flag = OPTYPE_INTERNAL;
2241
2242         RNA_def_string(ot->srna, "name", NULL, BKE_ST_MAXNAME, "Name", "Name of the pie menu");
2243 }
2244
2245 static int wm_call_panel_exec(bContext *C, wmOperator *op)
2246 {
2247         char idname[BKE_ST_MAXNAME];
2248         RNA_string_get(op->ptr, "name", idname);
2249         const int space_type = RNA_enum_get(op->ptr, "space_type");
2250         const int region_type = RNA_enum_get(op->ptr, "region_type");
2251         const bool keep_open = RNA_boolean_get(op->ptr, "keep_open");
2252
2253         return UI_popover_panel_invoke(C, space_type, region_type, idname, keep_open, op->reports);
2254 }
2255
2256 static void WM_OT_call_panel(wmOperatorType *ot)
2257 {
2258         ot->name = "Call Panel";
2259         ot->idname = "WM_OT_call_panel";
2260         ot->description = "Call (draw) a pre-defined panel";
2261
2262         ot->exec = wm_call_panel_exec;
2263         ot->poll = WM_operator_winactive;
2264
2265         ot->flag = OPTYPE_INTERNAL;
2266
2267         PropertyRNA *prop;
2268
2269         prop = RNA_def_string(ot->srna, "name", NULL, BKE_ST_MAXNAME, "Name", "Name of the menu");
2270         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2271         prop = RNA_def_enum(ot->srna, "space_type", rna_enum_space_type_items, SPACE_EMPTY, "Space Type", "");
2272         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2273         prop = RNA_def_enum(ot->srna, "region_type", rna_enum_region_type_items, RGN_TYPE_WINDOW, "Region Type", "");
2274         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2275         prop = RNA_def_boolean(ot->srna, "keep_open", true, "Keep Open", "");
2276         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2277 }
2278
2279 /* ************ window / screen operator definitions ************** */
2280
2281 /* this poll functions is needed in place of WM_operator_winactive
2282  * while it crashes on full screen */
2283 static bool wm_operator_winactive_normal(bContext *C)
2284 {
2285         wmWindow *win = CTX_wm_window(C);
2286         bScreen *screen;
2287
2288         if (win == NULL)
2289                 return 0;
2290         if (!((screen = WM_window_get_active_screen(win)) && (screen->state == SCREENNORMAL)))
2291                 return 0;
2292
2293         return 1;
2294 }
2295
2296 /* included for script-access */
2297 static void WM_OT_window_close(wmOperatorType *ot)
2298 {
2299         ot->name = "Close Window";
2300         ot->idname = "WM_OT_window_close";
2301         ot->description = "Close the current window";
2302
2303         ot->exec = wm_window_close_exec;
2304         ot->poll = WM_operator_winactive;
2305 }
2306
2307 static void WM_OT_window_new(wmOperatorType *ot)
2308 {
2309         ot->name = "New Window";
2310         ot->idname = "WM_OT_window_new";
2311         ot->description = "Create a new window";
2312
2313         ot->exec = wm_window_new_exec;
2314         ot->poll = wm_operator_winactive_normal;
2315 }
2316
2317 static void WM_OT_window_new_main(wmOperatorType *ot)
2318 {
2319         ot->name = "New Main Window";
2320         ot->idname = "WM_OT_window_new_main";
2321         ot->description = "Create a new main window with its own workspace and scene selection";
2322
2323         ot->exec = wm_window_new_main_exec;
2324         ot->poll = wm_operator_winactive_normal;
2325 }
2326
2327 static void WM_OT_window_fullscreen_toggle(wmOperatorType *ot)
2328 {
2329         ot->name = "Toggle Window Fullscreen";
2330         ot->idname = "WM_OT_window_fullscreen_toggle";
2331         ot->description = "Toggle the current window fullscreen";
2332
2333         ot->exec = wm_window_fullscreen_toggle_exec;
2334         ot->poll = WM_operator_winactive;
2335 }
2336
2337 static int wm_exit_blender_exec(bContext *C, wmOperator *UNUSED(op))
2338 {
2339         wm_quit_with_optional_confirmation_prompt(C, CTX_wm_window(C));
2340         return OPERATOR_FINISHED;
2341 }
2342
2343 static int wm_exit_blender_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2344 {
2345         if (U.uiflag & USER_QUIT_PROMPT) {
2346                 return wm_exit_blender_exec(C, op);
2347         }
2348         else {
2349                 return WM_operator_confirm(C, op, event);
2350         }
2351 }
2352
2353 static void WM_OT_quit_blender(wmOperatorType *ot)
2354 {
2355         ot->name = "Quit Blender";
2356         ot->idname = "WM_OT_quit_blender";
2357         ot->description = "Quit Blender";
2358
2359         ot->invoke = wm_exit_blender_invoke;
2360         ot->exec = wm_exit_blender_exec;
2361 }
2362
2363 /* *********************** */
2364
2365 #if defined(WIN32)
2366
2367 static int wm_console_toggle_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
2368 {
2369         GHOST_toggleConsole(2);
2370         return OPERATOR_FINISHED;
2371 }
2372
2373 static void WM_OT_console_toggle(wmOperatorType *ot)
2374 {
2375         /* XXX Have to mark these for xgettext, as under linux they do not exists... */
2376         ot->name = CTX_N_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Toggle System Console");
2377         ot->idname = "WM_OT_console_toggle";
2378         ot->description = N_("Toggle System Console");
2379
2380         ot->exec = wm_console_toggle_exec;
2381         ot->poll = WM_operator_winactive;
2382 }
2383
2384 #endif
2385
2386 /* ************ default paint cursors, draw always around cursor *********** */
2387 /*
2388  * - returns handler to free
2389  * - poll(bContext): returns 1 if draw should happen
2390  * - draw(bContext): drawing callback for paint cursor
2391  */
2392
2393 void *WM_paint_cursor_activate(
2394         wmWindowManager *wm, bool (*poll)(bContext *C),
2395         wmPaintCursorDraw draw, void *customdata)
2396 {
2397         wmPaintCursor *pc = MEM_callocN(sizeof(wmPaintCursor), "paint cursor");
2398
2399         BLI_addtail(&wm->paintcursors, pc);
2400
2401         pc->customdata = customdata;
2402         pc->poll = poll;
2403         pc->draw = draw;
2404
2405         return pc;
2406 }
2407
2408 void WM_paint_cursor_end(wmWindowManager *wm, void *handle)
2409 {
2410         wmPaintCursor *pc;
2411
2412         for (pc = wm->paintcursors.first; pc; pc = pc->next) {
2413                 if (pc == (wmPaintCursor *)handle) {
2414                         BLI_remlink(&wm->paintcursors, pc);
2415                         MEM_freeN(pc);
2416                         return;
2417                 }
2418         }
2419 }
2420
2421 /* *********************** radial control ****************** */
2422
2423 #define WM_RADIAL_CONTROL_DISPLAY_SIZE (200 * UI_DPI_FAC)
2424 #define WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE (35 * UI_DPI_FAC)
2425 #define WM_RADIAL_CONTROL_DISPLAY_WIDTH (WM_RADIAL_CONTROL_DISPLAY_SIZE - WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE)
2426 #define WM_RADIAL_MAX_STR 10
2427
2428 typedef struct {
2429         PropertyType type;
2430         PropertySubType subtype;
2431         PointerRNA ptr, col_ptr, fill_col_ptr, rot_ptr, zoom_ptr, image_id_ptr;
2432         PointerRNA fill_col_override_ptr, fill_col_override_test_ptr;
2433         PropertyRNA *prop, *col_prop, *fill_col_prop, *rot_prop, *zoom_prop;
2434         PropertyRNA *fill_col_override_prop, *fill_col_override_test_prop;
2435         StructRNA *image_id_srna;
2436         float initial_value, current_value, min_value, max_value;
2437         int initial_mouse[2];
2438         int slow_mouse[2];
2439         bool slow_mode;
2440         Dial *dial;
2441         unsigned int gltex;
2442         ListBase orig_paintcursors;
2443         bool use_secondary_tex;
2444         void *cursor;
2445         NumInput num_input;
2446 } RadialControl;
2447
2448 static void radial_control_update_header(wmOperator *op, bContext *C)
2449 {
2450         RadialControl *rc = op->customdata;
2451         char msg[UI_MAX_DRAW_STR];
2452         ScrArea *sa = CTX_wm_area(C);
2453         Scene *scene = CTX_data_scene(C);
2454
2455         if (hasNumInput(&rc->num_input)) {
2456                 char num_str[NUM_STR_REP_LEN];
2457                 outputNumInput(&rc->num_input, num_str, &scene->unit);
2458                 BLI_snprintf(msg, sizeof(msg), "%s: %s", RNA_property_ui_name(rc->prop), num_str);
2459         }
2460         else {
2461                 const char *ui_name = RNA_property_ui_name(rc->prop);
2462                 switch (rc->subtype) {
2463                         case PROP_NONE:
2464                         case PROP_DISTANCE:
2465                                 BLI_snprintf(msg, sizeof(msg), "%s: %0.4f", ui_name, rc->current_value);
2466                                 break;
2467                         case PROP_PIXEL:
2468                                 BLI_snprintf(msg, sizeof(msg), "%s: %d", ui_name, (int)rc->current_value); /* XXX: round to nearest? */
2469                                 break;
2470                         case PROP_PERCENTAGE:
2471                                 BLI_snprintf(msg, sizeof(msg), "%s: %3.1f%%", ui_name, rc->current_value);
2472                                 break;
2473                         case PROP_FACTOR:
2474                                 BLI_snprintf(msg, sizeof(msg), "%s: %1.3f", ui_name, rc->current_value);
2475                                 break;
2476                         case PROP_ANGLE:
2477                                 BLI_snprintf(msg, sizeof(msg), "%s: %3.2f", ui_name, RAD2DEGF(rc->current_value));
2478                                 break;
2479                         default:
2480                                 BLI_snprintf(msg, sizeof(msg), "%s", ui_name); /* XXX: No value? */
2481                                 break;
2482                 }
2483         }
2484
2485         ED_area_status_text(sa, msg);
2486 }
2487
2488 static void radial_control_set_initial_mouse(RadialControl *rc, const wmEvent *event)
2489 {
2490         float d[2] = {0, 0};
2491         float zoom[2] = {1, 1};
2492
2493         rc->initial_mouse[0] = event->x;
2494         rc->initial_mouse[1] = event->y;
2495
2496         switch (rc->subtype) {
2497                 case PROP_NONE:
2498                 case PROP_DISTANCE:
2499                 case PROP_PIXEL:
2500                         d[0] = rc->initial_value;
2501                         break;
2502                 case PROP_PERCENTAGE:
2503                         d[0] = (rc->initial_value) / 100.0f * WM_RADIAL_CONTROL_DISPLAY_WIDTH + WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE;
2504                         break;
2505                 case PROP_FACTOR:
2506                         d[0] = (1 - rc->initial_value) * WM_RADIAL_CONTROL_DISPLAY_WIDTH + WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE;
2507                         break;
2508                 case PROP_ANGLE:
2509                         d[0] = WM_RADIAL_CONTROL_DISPLAY_SIZE * cosf(rc->initial_value);
2510                         d[1] = WM_RADIAL_CONTROL_DISPLAY_SIZE * sinf(rc->initial_value);
2511                         break;
2512                 default:
2513                         return;
2514         }
2515
2516         if (rc->zoom_prop) {
2517                 RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom);
2518                 d[0] *= zoom[0];
2519                 d[1] *= zoom[1];
2520         }
2521
2522         rc->initial_mouse[0] -= d[0];
2523         rc->initial_mouse[1] -= d[1];
2524 }
2525
2526 static void radial_control_set_tex(RadialControl *rc)
2527 {
2528         ImBuf *ibuf;
2529
2530         switch (RNA_type_to_ID_code(rc->image_id_ptr.type)) {
2531                 case ID_BR:
2532                         if ((ibuf = BKE_brush_gen_radial_control_imbuf(rc->image_id_ptr.data, rc->use_secondary_tex))) {
2533                                 glGenTextures(1, &rc->gltex);
2534                                 glBindTexture(GL_TEXTURE_2D, rc->gltex);
2535                                 glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, ibuf->x, ibuf->y, 0,
2536                                              GL_RED, GL_FLOAT, ibuf->rect_float);
2537                                 glBindTexture(GL_TEXTURE_2D, 0);
2538                                 MEM_freeN(ibuf->rect_float);
2539                                 MEM_freeN(ibuf);
2540                         }
2541                         break;
2542                 default:
2543                         break;
2544         }
2545 }
2546
2547 static void radial_control_paint_tex(RadialControl *rc, float radius, float alpha)
2548 {
2549         float col[3] = {0, 0, 0};
2550         float rot;
2551
2552         /* set fill color */
2553         if (rc->fill_col_prop) {
2554                 PointerRNA *fill_ptr;
2555                 PropertyRNA *fill_prop;
2556
2557                 if (rc->fill_col_override_prop &&
2558                     RNA_property_boolean_get(&rc->fill_col_override_test_ptr, rc->fill_col_override_test_prop))
2559                 {
2560                         fill_ptr = &rc->fill_col_override_ptr;
2561                         fill_prop = rc->fill_col_override_prop;
2562                 }
2563                 else {
2564                         fill_ptr = &rc->fill_col_ptr;
2565                         fill_prop = rc->fill_col_prop;
2566                 }
2567
2568                 RNA_property_float_get_array(fill_ptr, fill_prop, col);
2569         }
2570
2571         Gwn_VertFormat *format = immVertexFormat();
2572         uint pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
2573
2574         if (rc->gltex) {
2575
2576                 uint texCoord = GWN_vertformat_attr_add(format, "texCoord", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
2577
2578                 glActiveTexture(GL_TEXTURE0);
2579                 glBindTexture(GL_TEXTURE_2D, rc->gltex);
2580
2581                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
2582                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
2583
2584                 GLint swizzleMask[] = {GL_ZERO, GL_ZERO, GL_ZERO, GL_RED};
2585                 glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
2586
2587                 immBindBuiltinProgram(GPU_SHADER_2D_IMAGE_MASK_UNIFORM_COLOR);
2588
2589                 immUniformColor3fvAlpha(col, alpha);
2590                 immUniform1i("image", 0);
2591
2592                 /* set up rotation if available */
2593                 if (rc->rot_prop) {
2594                         rot = RNA_property_float_get(&rc->rot_ptr, rc->rot_prop);
2595                         gpuPushMatrix();
2596                         gpuRotate2D(RAD2DEGF(rot));
2597                 }
2598
2599                 /* draw textured quad */
2600                 immBegin(GWN_PRIM_TRI_FAN, 4);
2601
2602                 immAttrib2f(texCoord, 0, 0);
2603                 immVertex2f(pos, -radius, -radius);
2604
2605                 immAttrib2f(texCoord, 1, 0);
2606                 immVertex2f(pos, radius, -radius);
2607
2608                 immAttrib2f(texCoord, 1, 1);
2609                 immVertex2f(pos, radius, radius);
2610
2611                 immAttrib2f(texCoord, 0, 1);
2612                 immVertex2f(pos, -radius, radius);
2613
2614                 immEnd();
2615
2616                 /* undo rotation */
2617                 if (rc->rot_prop)
2618                         gpuPopMatrix();
2619         }
2620         else {
2621                 /* flat color if no texture available */
2622                 immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2623                 immUniformColor3fvAlpha(col, alpha);
2624                 imm_draw_circle_fill_2d(pos, 0.0f, 0.0f, radius, 40);
2625         }
2626
2627         immUnbindProgram();
2628 }
2629
2630 static void radial_control_paint_cursor(bContext *UNUSED(C), int x, int y, void *customdata)
2631 {
2632         RadialControl *rc = customdata;
2633         uiStyle *style = UI_style_get();
2634         const uiFontStyle *fstyle = &style->widget;
2635         const int fontid = fstyle->uifont_id;
2636         short fstyle_points = fstyle->points;
2637         char str[WM_RADIAL_MAX_STR];
2638         short strdrawlen = 0;
2639         float strwidth, strheight;
2640         float r1 = 0.0f, r2 = 0.0f, rmin = 0.0, tex_radius, alpha;
2641         float zoom[2], col[3] = {1, 1, 1};
2642
2643         switch (rc->subtype) {
2644                 case PROP_NONE:
2645                 case PROP_DISTANCE:
2646                 case PROP_PIXEL:
2647                         r1 = rc->current_value;
2648                         r2 = rc->initial_value;
2649                         tex_radius = r1;
2650                         alpha = 0.75;
2651                         break;
2652                 case PROP_PERCENTAGE:
2653                         r1 = rc->current_value / 100.0f * WM_RADIAL_CONTROL_DISPLAY_WIDTH + WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE;
2654                         r2 = tex_radius = WM_RADIAL_CONTROL_DISPLAY_SIZE;
2655                         rmin = WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE;
2656                         BLI_snprintf(str, WM_RADIAL_MAX_STR, "%3.1f%%", rc->current_value);
2657                         strdrawlen = BLI_strlen_utf8(str);
2658                         tex_radius = r1;
2659                         alpha = 0.75;
2660                         break;
2661                 case PROP_FACTOR:
2662                         r1 = (1 - rc->current_value) * WM_RADIAL_CONTROL_DISPLAY_WIDTH + WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE;
2663                         r2 = tex_radius = WM_RADIAL_CONTROL_DISPLAY_SIZE;
2664                         rmin = WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE;
2665                         alpha = rc->current_value / 2.0f + 0.5f;
2666                         BLI_snprintf(str, WM_RADIAL_MAX_STR, "%1.3f", rc->current_value);
2667                         strdrawlen = BLI_strlen_utf8(str);
2668                         break;
2669                 case PROP_ANGLE:
2670                         r1 = r2 = tex_radius = WM_RADIAL_CONTROL_DISPLAY_SIZE;
2671                         alpha = 0.75;
2672                         rmin = WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE;
2673                         BLI_snprintf(str, WM_RADIAL_MAX_STR, "%3.2f", RAD2DEGF(rc->current_value));
2674                         strdrawlen = BLI_strlen_utf8(str);
2675                         break;
2676                 default:
2677                         tex_radius = WM_RADIAL_CONTROL_DISPLAY_SIZE; /* note, this is a dummy value */
2678                         alpha = 0.75;
2679                         break;
2680         }
2681
2682         /* Keep cursor in the original place */
2683         x = rc->initial_mouse[0];
2684         y = rc->initial_mouse[1];
2685         gpuTranslate2f((float)x, (float)y);
2686
2687         glEnable(GL_BLEND);
2688         glEnable(GL_LINE_SMOOTH);
2689
2690         /* apply zoom if available */
2691         if (rc->zoom_prop) {
2692                 RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom);
2693                 gpuScale2fv(zoom);
2694         }
2695
2696         /* draw rotated texture */
2697         radial_control_paint_tex(rc, tex_radius, alpha);
2698
2699         /* set line color */
2700         if (rc->col_prop)
2701                 RNA_property_float_get_array(&rc->col_ptr, rc->col_prop, col);
2702
2703         Gwn_VertFormat *format = immVertexFormat();
2704         uint pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
2705
2706         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
2707         immUniformColor3fvAlpha(col, 0.5f);
2708
2709         if (rc->subtype == PROP_ANGLE) {
2710                 gpuPushMatrix();
2711
2712                 /* draw original angle line */
2713                 gpuRotate2D(RAD2DEGF(rc->initial_value));
2714                 immBegin(GWN_PRIM_LINES, 2);
2715                 immVertex2f(pos, (float)WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE, 0.0f);
2716                 immVertex2f(pos, (float)WM_RADIAL_CONTROL_DISPLAY_SIZE, 0.0f);
2717                 immEnd();
2718
2719                 /* draw new angle line */
2720                 gpuRotate2D(RAD2DEGF(rc->current_value - rc->initial_value));
2721                 immBegin(GWN_PRIM_LINES, 2);
2722                 immVertex2f(pos, (float)WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE, 0.0f);
2723                 immVertex2f(pos, (float)WM_RADIAL_CONTROL_DISPLAY_SIZE, 0.0f);
2724                 immEnd();
2725
2726                 gpuPopMatrix();
2727         }
2728
2729         /* draw circles on top */
2730         imm_draw_circle_wire_2d(pos, 0.0f, 0.0f, r1, 40);
2731         imm_draw_circle_wire_2d(pos, 0.0f, 0.0f, r2, 40);
2732         if (rmin > 0.0f)
2733                 imm_draw_circle_wire_2d(pos, 0.0, 0.0f, rmin, 40);
2734         immUnbindProgram();
2735
2736         BLF_size(fontid, 1.5 * fstyle_points * U.pixelsize, U.dpi);
2737         BLF_enable(fontid, BLF_SHADOW);
2738         BLF_shadow(fontid, 3, (const float[4]){0.0f, 0.0f, 0.0f, 0.5f});
2739         BLF_shadow_offset(fontid, 1, -1);
2740
2741         /* draw value */
2742         BLF_width_and_height(fontid, str, strdrawlen, &strwidth, &strheight);
2743         BLF_position(fontid, -0.5f * strwidth, -0.5f * strheight, 0.0f);
2744         BLF_draw(fontid, str, strdrawlen);
2745
2746         BLF_disable(fontid, BLF_SHADOW);
2747
2748         glDisable(GL_BLEND);
2749         glDisable(GL_LINE_SMOOTH);
2750
2751 }
2752
2753 typedef enum {
2754         RC_PROP_ALLOW_MISSING = 1,
2755         RC_PROP_REQUIRE_FLOAT = 2,
2756         RC_PROP_REQUIRE_BOOL = 4,
2757 } RCPropFlags;
2758
2759 /**
2760  * Attempt to retrieve the rna pointer/property from an rna path.
2761  *
2762  * \return 0 for failure, 1 for success, and also 1 if property is not set.
2763  */
2764 static int radial_control_get_path(
2765         PointerRNA *ctx_ptr, wmOperator *op,
2766         const char *name, PointerRNA *r_ptr,
2767         PropertyRNA **r_prop, int req_length, RCPropFlags flags)
2768 {
2769         PropertyRNA *unused_prop;
2770         int len;
2771         char *str;
2772
2773         /* check flags */
2774         if ((flags & RC_PROP_REQUIRE_BOOL) && (flags & RC_PROP_REQUIRE_FLOAT)) {
2775                 BKE_report(op->reports, RPT_ERROR, "Property cannot be both boolean and float");
2776                 return 0;
2777         }
2778
2779         /* get an rna string path from the operator's properties */
2780         if (!(str = RNA_string_get_alloc(op->ptr, name, NULL, 0)))
2781                 return 1;
2782
2783         if (str[0] == '\0') {
2784                 if (r_prop) *r_prop = NULL;
2785                 MEM_freeN(str);
2786                 return 1;
2787         }
2788
2789         if (!r_prop)
2790                 r_prop = &unused_prop;
2791
2792         /* get rna from path */
2793         if (!RNA_path_resolve(ctx_ptr, str, r_ptr, r_prop)) {
2794                 MEM_freeN(str);
2795                 if (flags & RC_PROP_ALLOW_MISSING)
2796                         return 1;
2797                 else {
2798                         BKE_reportf(op->reports, RPT_ERROR, "Could not resolve path '%s'", name);
2799                         return 0;
2800                 }
2801         }
2802
2803         /* check property type */
2804         if (flags & (RC_PROP_REQUIRE_BOOL | RC_PROP_REQUIRE_FLOAT)) {
2805                 PropertyType prop_type = RNA_property_type(*r_prop);
2806
2807                 if (((flags & RC_PROP_REQUIRE_BOOL) && (prop_type != PROP_BOOLEAN)) ||
2808                     ((flags & RC_PROP_REQUIRE_FLOAT) && (prop_type != PROP_FLOAT)))
2809                 {
2810                         MEM_freeN(str);
2811                         BKE_reportf(op->reports, RPT_ERROR, "Property from path '%s' is not a float", name);
2812                         return 0;
2813                 }
2814         }
2815
2816         /* check property's array length */
2817         if (*r_prop && (len = RNA_property_array_length(r_ptr, *r_prop)) != req_length) {
2818                 MEM_freeN(str);
2819                 BKE_reportf(op->reports, RPT_ERROR, "Property from path '%s' has length %d instead of %d",
2820                             name, len, req_length);
2821                 return 0;
2822         }
2823
2824         /* success */
2825         MEM_freeN(str);
2826         return 1;
2827 }
2828
2829 /* initialize the rna pointers and properties using rna paths */
2830 static int radial_control_get_properties(bContext *C, wmOperator *op)
2831 {
2832         RadialControl *rc = op->customdata;
2833         PointerRNA ctx_ptr, use_secondary_ptr;
2834         PropertyRNA *use_secondary_prop = NULL;
2835         const char *data_path;
2836
2837         RNA_pointer_create(NULL, &RNA_Context, C, &ctx_ptr);
2838
2839         /* check if we use primary or secondary path */
2840         if (!radial_control_get_path(&ctx_ptr, op, "use_secondary",
2841                                      &use_secondary_ptr, &use_secondary_prop,
2842                                      0, (RC_PROP_ALLOW_MISSING |
2843                                          RC_PROP_REQUIRE_BOOL)))
2844         {
2845                 return 0;
2846         }
2847         else {
2848                 if (use_secondary_prop &&
2849                     RNA_property_boolean_get(&use_secondary_ptr, use_secondary_prop))
2850                 {
2851                         data_path = "data_path_secondary";
2852                 }
2853                 else {
2854                         data_path = "data_path_primary";
2855                 }
2856         }
2857
2858         if (!radial_control_get_path(&ctx_ptr, op, data_path, &rc->ptr, &rc->prop, 0, 0))
2859                 return 0;
2860
2861         /* data path is required */
2862         if (!rc->prop)
2863                 return 0;
2864
2865         if (!radial_control_get_path(&ctx_ptr, op, "rotation_path", &rc->rot_ptr, &rc->rot_prop, 0, RC_PROP_REQUIRE_FLOAT))
2866                 return 0;
2867         if (!radial_control_get_path(&ctx_ptr, op, "color_path", &rc->col_ptr, &rc->col_prop, 3, RC_PROP_REQUIRE_FLOAT))
2868                 return 0;
2869
2870
2871         if (!radial_control_get_path(
2872                 &ctx_ptr, op, "fill_color_path", &rc->fill_col_ptr, &rc->fill_col_prop, 3, RC_PROP_REQUIRE_FLOAT))
2873         {
2874                 return 0;
2875         }
2876
2877         if (!radial_control_get_path(
2878                 &ctx_ptr, op, "fill_color_override_path",
2879                 &rc->fill_col_override_ptr, &rc->fill_col_override_prop, 3, RC_PROP_REQUIRE_FLOAT))
2880         {
2881                 return 0;
2882         }
2883         if (!radial_control_get_path(
2884                 &ctx_ptr, op, "fill_color_override_test_path",
2885                 &rc->fill_col_override_test_ptr, &rc->fill_col_override_test_prop, 0, RC_PROP_REQUIRE_BOOL))
2886         {
2887                 return 0;
2888         }
2889
2890         /* slightly ugly; allow this property to not resolve
2891          * correctly. needed because 3d texture paint shares the same
2892          * keymap as 2d image paint */
2893         if (!radial_control_get_path(&ctx_ptr, op, "zoom_path",
2894                                      &rc->zoom_ptr, &rc->zoom_prop, 2,
2895                                      RC_PROP_REQUIRE_FLOAT | RC_PROP_ALLOW_MISSING))
2896         {
2897                 return 0;
2898         }
2899
2900         if (!radial_control_get_path(&ctx_ptr, op, "image_id", &rc->image_id_ptr, NULL, 0, 0))
2901                 return 0;
2902         else if (rc->image_id_ptr.data) {
2903                 /* extra check, pointer must be to an ID */
2904                 if (!RNA_struct_is_ID(rc->image_id_ptr.type)) {
2905                         BKE_report(op->reports, RPT_ERROR, "Pointer from path image_id is not an ID");
2906                         return 0;
2907                 }
2908         }
2909
2910         rc->use_secondary_tex = RNA_boolean_get(op->ptr, "secondary_tex");
2911
2912         return 1;
2913 }
2914
2915 static int radial_control_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2916 {
2917         wmWindowManager *wm;
2918         RadialControl *rc;
2919
2920
2921         if (!(op->customdata = rc = MEM_callocN(sizeof(RadialControl), "RadialControl")))
2922                 return OPERATOR_CANCELLED;
2923
2924         if (!radial_control_get_properties(C, op)) {
2925                 MEM_freeN(rc);
2926                 return OPERATOR_CANCELLED;
2927         }
2928
2929         /* get type, initial, min, and max values of the property */
2930         switch ((rc->type = RNA_property_type(rc->prop))) {
2931                 case PROP_INT:
2932                 {
2933                         int value, min, max, step;
2934
2935                         value = RNA_property_int_get(&rc->ptr, rc->prop);
2936                         RNA_property_int_ui_range(&rc->ptr, rc->prop, &min, &max, &step);
2937
2938                         rc->initial_value = value;
2939                         rc->min_value = min_ii(value, min);
2940                         rc->max_value = max_ii(value, max);
2941                         break;
2942                 }
2943                 case PROP_FLOAT:
2944                 {
2945                         float value, min, max, step, precision;
2946
2947                         value = RNA_property_float_get(&rc->ptr, rc->prop);
2948                         RNA_property_float_ui_range(&rc->ptr, rc->prop, &min, &max, &step, &precision);
2949
2950                         rc->initial_value = value;
2951                         rc->min_value = min_ff(value, min);
2952                         rc->max_value = max_ff(value, max);
2953                         break;
2954                 }
2955                 default:
2956                         BKE_report(op->reports, RPT_ERROR, "Property must be an integer or a float");
2957                         MEM_freeN(rc);
2958                         return OPERATOR_CANCELLED;
2959         }
2960
2961         /* initialize numerical input */
2962         initNumInput(&rc->num_input);
2963         rc->num_input.idx_max = 0;
2964         rc->num_input.val_flag[0] |= NUM_NO_NEGATIVE;
2965         rc->num_input.unit_sys = USER_UNIT_NONE;
2966         rc->num_input.unit_type[0] = B_UNIT_LENGTH;
2967
2968         /* get subtype of property */
2969         rc->subtype = RNA_property_subtype(rc->prop);
2970         if (!ELEM(rc->subtype, PROP_NONE, PROP_DISTANCE, PROP_FACTOR, PROP_PERCENTAGE, PROP_ANGLE, PROP_PIXEL)) {
2971                 BKE_report(op->reports, RPT_ERROR, "Property must be a none, distance, factor, percentage, angle, or pixel");
2972                 MEM_freeN(rc);
2973                 return OPERATOR_CANCELLED;
2974         }
2975
2976         rc->current_value = rc->initial_value;
2977         radial_control_set_initial_mouse(rc, event);
2978         radial_control_set_tex(rc);
2979
2980         /* temporarily disable other paint cursors */
2981         wm = CTX_wm_manager(C);
2982         rc->orig_paintcursors = wm->paintcursors;
2983         BLI_listbase_clear(&wm->paintcursors);
2984
2985         /* add radial control paint cursor */
2986         rc->cursor = WM_paint_cursor_activate(wm, op->type->poll,
2987                                               radial_control_paint_cursor, rc);
2988
2989         WM_event_add_modal_handler(C, op);
2990
2991         return OPERATOR_RUNNING_MODAL;
2992 }
2993
2994 static void radial_control_set_value(RadialControl *rc, float val)
2995 {
2996         switch (rc->type) {
2997                 case PROP_INT:
2998                         RNA_property_int_set(&rc->ptr, rc->prop, val);
2999                         break;
3000                 case PROP_FLOAT:
3001                         RNA_property_float_set(&rc->ptr, rc->prop, val);
3002                         break;
3003                 default:
3004                         break;
3005         }
3006 }
3007
3008 static void radial_control_cancel(bContext *C, wmOperator *op)
3009 {
3010         RadialControl *rc = op->customdata;
3011         wmWindowManager *wm = CTX_wm_manager(C);
3012         ScrArea *sa = CTX_wm_area(C);
3013
3014         if (rc->dial) {
3015                 MEM_freeN(rc->dial);
3016                 rc->dial = NULL;
3017         }
3018
3019         ED_area_status_text(sa, NULL);
3020
3021         WM_paint_cursor_end(wm, rc->cursor);
3022
3023         /* restore original paint cursors */
3024         wm->paintcursors = rc->orig_paintcursors;
3025
3026         /* not sure if this is a good notifier to use;
3027          * intended purpose is to update the UI so that the
3028          * new value is displayed in sliders/numfields */
3029         WM_event_add_notifier(C, NC_WINDOW, NULL);
3030
3031         glDeleteTextures(1, &rc->gltex);
3032
3033         MEM_freeN(rc);
3034 }
3035
3036 static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *event)
3037 {
3038         RadialControl *rc = op->customdata;
3039         float new_value, dist = 0.0f, zoom[2];
3040         float delta[2], ret = OPERATOR_RUNNING_MODAL;
3041         bool snap;
3042         float angle_precision = 0.0f;
3043         const bool has_numInput = hasNumInput(&rc->num_input);
3044         bool handled = false;
3045         float numValue;
3046         /* TODO: fix hardcoded events */
3047
3048         snap = event->ctrl != 0;
3049
3050         /* Modal numinput active, try to handle numeric inputs first... */
3051         if (event->val == KM_PRESS && has_numInput && handleNumInput(C, &rc->num_input, event)) {
3052                 handled = true;
3053                 applyNumInput(&rc->num_input, &numValue);
3054
3055                 if (rc->subtype == PROP_ANGLE) {
3056                         numValue = DEG2RADF(numValue);
3057                         numValue = fmod(numValue, 2.0f * (float)M_PI);
3058                         if (numValue < 0.0f)
3059                                 numValue += 2.0f * (float)M_PI;
3060                 }
3061
3062                 CLAMP(numValue, rc->min_value, rc->max_value);
3063                 new_value = numValue;
3064
3065                 radial_control_set_value(rc, new_value);
3066                 rc->current_value = new_value;
3067                 radial_control_update_header(op, C);
3068                 return OPERATOR_RUNNING_MODAL;
3069         }
3070         else {
3071                 handled = false;
3072                 switch (event->type) {
3073                         case ESCKEY:
3074                         case RIGHTMOUSE:
3075                                 /* canceled; restore original value */
3076                                 radial_control_set_value(rc, rc->initial_value);
3077                                 ret = OPERATOR_CANCELLED;
3078                                 break;
3079
3080                         case LEFTMOUSE:
3081                         case PADENTER:
3082                         case RETKEY:
3083                                 /* done; value already set */
3084                                 RNA_property_update(C, &rc->ptr, rc->prop);
3085                                 ret = OPERATOR_FINISHED;
3086                                 break;
3087
3088                         case MOUSEMOVE:
3089                                 if (!has_numInput) {
3090                                         if (rc->slow_mode) {
3091                                                 if (rc->subtype == PROP_ANGLE) {
3092                                                         float position[2] = {event->x, event->y};
3093
3094                                                         /* calculate the initial angle here first */
3095                                                         delta[0] = rc->initial_mouse[0] - rc->slow_mouse[0];
3096                                                         delta[1] = rc->initial_mouse[1] - rc->slow_mouse[1];
3097
3098                                                         /* precision angle gets calculated from dial and gets added later */
3099                                                         angle_precision = -0.1f * BLI_dial_angle(rc->dial, position);
3100                                                 }
3101                                                 else {
3102                                                         delta[0] = rc->initial_mouse[0] - rc->slow_mouse[0];
3103                                                         delta[1] = rc->initial_mouse[1] - rc->slow_mouse[1];
3104
3105                                                         if (rc->zoom_prop) {
3106                                                                 RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom);
3107                                                                 delta[0] /= zoom[0];
3108                                                                 delta[1] /= zoom[1];
3109                                                         }
3110
3111                                                         dist = len_v2(delta);
3112
3113                                                         delta[0] = event->x - rc->slow_mouse[0];
3114                                                         delta[1] = event->y - rc->slow_mouse[1];
3115
3116                                                         if (rc->zoom_prop) {
3117                                                                 delta[0] /= zoom[0];
3118                                                                 delta[1] /= zoom[1];
3119                                                         }
3120
3121                                                         dist = dist + 0.1f * (delta[0] + delta[1]);
3122                                                 }
3123                                         }
3124                                         else {
3125                                                 delta[0] = rc->initial_mouse[0] - event->x;
3126                                                 delta[1] = rc->initial_mouse[1] - event->y;
3127
3128                                                 if (rc->zoom_prop) {
3129                                                         RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom);
3130                                                         delta[0] /= zoom[0];
3131                                                         delta[1] /= zoom[1];
3132                                                 }
3133
3134                                                 dist = len_v2(delta);
3135                                         }
3136
3137                                         /* calculate new value and apply snapping  */
3138                                         switch (rc->subtype) {
3139                                                 case PROP_NONE:
3140                                                 case PROP_DISTANCE:
3141                                                 case PROP_PIXEL:
3142                                                         new_value = dist;
3143                                                         if (snap) new_value = ((int)new_value + 5) / 10 * 10;
3144                                                         break;
3145                                                 case PROP_PERCENTAGE:
3146                                                         new_value = ((dist - WM_RADIAL_CONTROL_DISPLAY_MIN_SIZE) / WM_RADIAL_CONTROL_DISPLAY_WIDTH) * 100.0f;
3147                                                         if (snap) new_value = ((int)(new_value + 2.5f)) / 5 * 5;
3148                                                         break;
3149                                                 case PROP_FACTOR:
3150                                                         new_value = (WM_RADIAL_CONTROL_DISPLAY_SIZE - dist) / WM_RADIAL_CONTROL_DISPLAY_WIDTH;
3151                                                         if (snap) new_value = ((int)ceil(new_value * 10.f) * 10.0f) / 100.f;
3152                                                         break;
3153                                                 case PROP_ANGLE:
3154                                                         new_value = atan2f(delta[1], delta[0]) + (float)M_PI + angle_precision;
3155                                                         new_value = fmod(new_value, 2.0f * (float)M_PI);
3156                                                         if (new_value < 0.0f)
3157                                                                 new_value += 2.0f * (float)M_PI;
3158                                                         if (snap) new_value = DEG2RADF(((int)RAD2DEGF(new_value) + 5) / 10 * 10);
3159                                                         break;
3160                                                 default:
3161                                                         new_value = dist; /* dummy value, should this ever happen? - campbell */
3162                                                         break;
3163                                         }
3164
3165                                         /* clamp and update */
3166                                         CLAMP(new_value, rc->min_value, rc->max_value);
3167                                         radial_control_set_value(rc, new_value);
3168                                         rc->current_value = new_value;
3169                                         handled = true;
3170                                         break;
3171                                 }
3172                                 break;
3173
3174                         case LEFTSHIFTKEY: