fix [#33442] Units
[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
31
32 #include <float.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include <stdio.h>
36 #include <stddef.h>
37 #include <assert.h>
38
39 #include "GHOST_C-api.h"
40
41 #include "MEM_guardedalloc.h"
42
43 #include "DNA_ID.h"
44 #include "DNA_object_types.h"
45 #include "DNA_screen_types.h"
46 #include "DNA_scene_types.h"
47 #include "DNA_userdef_types.h"
48 #include "DNA_windowmanager_types.h"
49 #include "DNA_mesh_types.h" /* only for USE_BMESH_SAVE_AS_COMPAT */
50
51 #include "BLF_translation.h"
52
53 #include "PIL_time.h"
54
55 #include "BLI_blenlib.h"
56 #include "BLI_dynstr.h" /*for WM_operator_pystring */
57 #include "BLI_math.h"
58 #include "BLI_utildefines.h"
59 #include "BLI_ghash.h"
60
61 #include "BLO_readfile.h"
62
63 #include "BKE_blender.h"
64 #include "BKE_brush.h"
65 #include "BKE_context.h"
66 #include "BKE_depsgraph.h"
67 #include "BKE_idprop.h"
68 #include "BKE_image.h"
69 #include "BKE_library.h"
70 #include "BKE_global.h"
71 #include "BKE_main.h"
72 #include "BKE_report.h"
73 #include "BKE_scene.h"
74 #include "BKE_screen.h" /* BKE_ST_MAXNAME */
75 #include "BKE_utildefines.h"
76
77 #include "BKE_idcode.h"
78
79 #include "BIF_gl.h"
80 #include "BIF_glutil.h" /* for paint cursor */
81 #include "BLF_api.h"
82
83 #include "IMB_colormanagement.h"
84 #include "IMB_imbuf_types.h"
85 #include "IMB_imbuf.h"
86
87 #include "ED_screen.h"
88 #include "ED_util.h"
89 #include "ED_object.h"
90 #include "ED_view3d.h"
91
92 #include "RNA_access.h"
93 #include "RNA_define.h"
94 #include "RNA_enum_types.h"
95
96 #include "UI_interface.h"
97 #include "UI_resources.h"
98
99 #include "WM_api.h"
100 #include "WM_types.h"
101
102 #include "wm.h"
103 #include "wm_draw.h"
104 #include "wm_event_system.h"
105 #include "wm_event_types.h"
106 #include "wm_subwindow.h"
107 #include "wm_window.h"
108
109 static GHash *global_ops_hash = NULL;
110
111 /* ************ operator API, exported ********** */
112
113
114 wmOperatorType *WM_operatortype_find(const char *idname, int quiet)
115 {
116         if (idname[0]) {
117                 wmOperatorType *ot;
118
119                 /* needed to support python style names without the _OT_ syntax */
120                 char idname_bl[OP_MAX_TYPENAME];
121                 WM_operator_bl_idname(idname_bl, idname);
122
123                 ot = BLI_ghash_lookup(global_ops_hash, idname_bl);
124                 if (ot) {
125                         return ot;
126                 }
127
128                 if (!quiet) {
129                         printf("search for unknown operator '%s', '%s'\n", idname_bl, idname);
130                 }
131         }
132         else {
133                 if (!quiet) {
134                         printf("search for empty operator\n");
135                 }
136         }
137
138         return NULL;
139 }
140
141 /* caller must free */
142 GHashIterator *WM_operatortype_iter(void)
143 {
144         return BLI_ghashIterator_new(global_ops_hash);
145 }
146
147 /* all ops in 1 list (for time being... needs evaluation later) */
148 void WM_operatortype_append(void (*opfunc)(wmOperatorType *))
149 {
150         wmOperatorType *ot;
151         
152         ot = MEM_callocN(sizeof(wmOperatorType), "operatortype");
153         ot->srna = RNA_def_struct(&BLENDER_RNA, "", "OperatorProperties");
154         /* Set the default i18n context now, so that opfunc can redefine it if needed! */
155         RNA_def_struct_translation_context(ot->srna, BLF_I18NCONTEXT_OPERATOR_DEFAULT);
156         opfunc(ot);
157
158         if (ot->name == NULL) {
159                 fprintf(stderr, "ERROR: Operator %s has no name property!\n", ot->idname);
160                 ot->name = N_("Dummy Name");
161         }
162
163         /* XXX All ops should have a description but for now allow them not to. */
164         RNA_def_struct_ui_text(ot->srna, ot->name, ot->description ? ot->description : N_("(undocumented operator)"));
165         RNA_def_struct_identifier(ot->srna, ot->idname);
166
167         BLI_ghash_insert(global_ops_hash, (void *)ot->idname, ot);
168 }
169
170 void WM_operatortype_append_ptr(void (*opfunc)(wmOperatorType *, void *), void *userdata)
171 {
172         wmOperatorType *ot;
173
174         ot = MEM_callocN(sizeof(wmOperatorType), "operatortype");
175         ot->srna = RNA_def_struct(&BLENDER_RNA, "", "OperatorProperties");
176         /* Set the default i18n context now, so that opfunc can redefine it if needed! */
177         RNA_def_struct_translation_context(ot->srna, BLF_I18NCONTEXT_OPERATOR_DEFAULT);
178         opfunc(ot, userdata);
179         RNA_def_struct_ui_text(ot->srna, ot->name, ot->description ? ot->description : N_("(undocumented operator)"));
180         RNA_def_struct_identifier(ot->srna, ot->idname);
181
182         BLI_ghash_insert(global_ops_hash, (void *)ot->idname, ot);
183 }
184
185 /* ********************* macro operator ******************** */
186
187 typedef struct {
188         int retval;
189 } MacroData;
190
191 static void wm_macro_start(wmOperator *op)
192 {
193         if (op->customdata == NULL) {
194                 op->customdata = MEM_callocN(sizeof(MacroData), "MacroData");
195         }
196 }
197
198 static int wm_macro_end(wmOperator *op, int retval)
199 {
200         if (retval & OPERATOR_CANCELLED) {
201                 MacroData *md = op->customdata;
202
203                 if (md->retval & OPERATOR_FINISHED) {
204                         retval |= OPERATOR_FINISHED;
205                         retval &= ~OPERATOR_CANCELLED;
206                 }
207         }
208
209         /* if modal is ending, free custom data */
210         if (retval & (OPERATOR_FINISHED | OPERATOR_CANCELLED)) {
211                 if (op->customdata) {
212                         MEM_freeN(op->customdata);
213                         op->customdata = NULL;
214                 }
215         }
216
217         return retval;
218 }
219
220 /* macro exec only runs exec calls */
221 static int wm_macro_exec(bContext *C, wmOperator *op)
222 {
223         wmOperator *opm;
224         int retval = OPERATOR_FINISHED;
225         
226         wm_macro_start(op);
227
228         for (opm = op->macro.first; opm; opm = opm->next) {
229                 
230                 if (opm->type->exec) {
231                         retval = opm->type->exec(C, opm);
232                         OPERATOR_RETVAL_CHECK(retval);
233                 
234                         if (retval & OPERATOR_FINISHED) {
235                                 MacroData *md = op->customdata;
236                                 md->retval = OPERATOR_FINISHED; /* keep in mind that at least one operator finished */
237                         }
238                         else {
239                                 break; /* operator didn't finish, end macro */
240                         }
241                 }
242         }
243         
244         return wm_macro_end(op, retval);
245 }
246
247 static int wm_macro_invoke_internal(bContext *C, wmOperator *op, wmEvent *event, wmOperator *opm)
248 {
249         int retval = OPERATOR_FINISHED;
250
251         /* start from operator received as argument */
252         for (; opm; opm = opm->next) {
253                 if (opm->type->invoke)
254                         retval = opm->type->invoke(C, opm, event);
255                 else if (opm->type->exec)
256                         retval = opm->type->exec(C, opm);
257
258                 OPERATOR_RETVAL_CHECK(retval);
259
260                 BLI_movelisttolist(&op->reports->list, &opm->reports->list);
261                 
262                 if (retval & OPERATOR_FINISHED) {
263                         MacroData *md = op->customdata;
264                         md->retval = OPERATOR_FINISHED; /* keep in mind that at least one operator finished */
265                 }
266                 else {
267                         break; /* operator didn't finish, end macro */
268                 }
269         }
270
271         return wm_macro_end(op, retval);
272 }
273
274 static int wm_macro_invoke(bContext *C, wmOperator *op, wmEvent *event)
275 {
276         wm_macro_start(op);
277         return wm_macro_invoke_internal(C, op, event, op->macro.first);
278 }
279
280 static int wm_macro_modal(bContext *C, wmOperator *op, wmEvent *event)
281 {
282         wmOperator *opm = op->opm;
283         int retval = OPERATOR_FINISHED;
284         
285         if (opm == NULL)
286                 printf("%s: macro error, calling NULL modal()\n", __func__);
287         else {
288                 retval = opm->type->modal(C, opm, event);
289                 OPERATOR_RETVAL_CHECK(retval);
290
291                 /* if this one is done but it's not the last operator in the macro */
292                 if ((retval & OPERATOR_FINISHED) && opm->next) {
293                         MacroData *md = op->customdata;
294
295                         md->retval = OPERATOR_FINISHED; /* keep in mind that at least one operator finished */
296
297                         retval = wm_macro_invoke_internal(C, op, event, opm->next);
298
299                         /* if new operator is modal and also added its own handler */
300                         if (retval & OPERATOR_RUNNING_MODAL && op->opm != opm) {
301                                 wmWindow *win = CTX_wm_window(C);
302                                 wmEventHandler *handler = NULL;
303
304                                 for (handler = win->modalhandlers.first; handler; handler = handler->next) {
305                                         /* first handler in list is the new one */
306                                         if (handler->op == op)
307                                                 break;
308                                 }
309
310                                 if (handler) {
311                                         BLI_remlink(&win->modalhandlers, handler);
312                                         wm_event_free_handler(handler);
313                                 }
314
315                                 /* if operator is blocking, grab cursor
316                                  * This may end up grabbing twice, but we don't care.
317                                  * */
318                                 if (op->opm->type->flag & OPTYPE_BLOCKING) {
319                                         int bounds[4] = {-1, -1, -1, -1};
320                                         int wrap = (U.uiflag & USER_CONTINUOUS_MOUSE) && ((op->opm->flag & OP_GRAB_POINTER) || (op->opm->type->flag & OPTYPE_GRAB_POINTER));
321
322                                         if (wrap) {
323                                                 ARegion *ar = CTX_wm_region(C);
324                                                 if (ar) {
325                                                         bounds[0] = ar->winrct.xmin;
326                                                         bounds[1] = ar->winrct.ymax;
327                                                         bounds[2] = ar->winrct.xmax;
328                                                         bounds[3] = ar->winrct.ymin;
329                                                 }
330                                         }
331
332                                         WM_cursor_grab_enable(CTX_wm_window(C), wrap, FALSE, bounds);
333                                 }
334                         }
335                 }
336         }
337
338         return wm_macro_end(op, retval);
339 }
340
341 static int wm_macro_cancel(bContext *C, wmOperator *op)
342 {
343         /* call cancel on the current modal operator, if any */
344         if (op->opm && op->opm->type->cancel) {
345                 op->opm->type->cancel(C, op->opm);
346         }
347
348         return wm_macro_end(op, OPERATOR_CANCELLED);
349 }
350
351 /* Names have to be static for now */
352 wmOperatorType *WM_operatortype_append_macro(const char *idname, const char *name, const char *description, int flag)
353 {
354         wmOperatorType *ot;
355         
356         if (WM_operatortype_find(idname, TRUE)) {
357                 printf("%s: macro error: operator %s exists\n", __func__, idname);
358                 return NULL;
359         }
360         
361         ot = MEM_callocN(sizeof(wmOperatorType), "operatortype");
362         ot->srna = RNA_def_struct(&BLENDER_RNA, "", "OperatorProperties");
363         
364         ot->idname = idname;
365         ot->name = name;
366         ot->description = description;
367         ot->flag = OPTYPE_MACRO | flag;
368         
369         ot->exec = wm_macro_exec;
370         ot->invoke = wm_macro_invoke;
371         ot->modal = wm_macro_modal;
372         ot->cancel = wm_macro_cancel;
373         ot->poll = NULL;
374
375         if (!ot->description) /* XXX All ops should have a description but for now allow them not to. */
376                 ot->description = N_("(undocumented operator)");
377         
378         RNA_def_struct_ui_text(ot->srna, ot->name, ot->description);
379         RNA_def_struct_identifier(ot->srna, ot->idname);
380         RNA_def_struct_translation_context(ot->srna, BLF_I18NCONTEXT_OPERATOR_DEFAULT);
381
382         BLI_ghash_insert(global_ops_hash, (void *)ot->idname, ot);
383
384         return ot;
385 }
386
387 void WM_operatortype_append_macro_ptr(void (*opfunc)(wmOperatorType *, void *), void *userdata)
388 {
389         wmOperatorType *ot;
390
391         ot = MEM_callocN(sizeof(wmOperatorType), "operatortype");
392         ot->srna = RNA_def_struct(&BLENDER_RNA, "", "OperatorProperties");
393
394         ot->flag = OPTYPE_MACRO;
395         ot->exec = wm_macro_exec;
396         ot->invoke = wm_macro_invoke;
397         ot->modal = wm_macro_modal;
398         ot->cancel = wm_macro_cancel;
399         ot->poll = NULL;
400
401         if (!ot->description)
402                 ot->description = N_("(undocumented operator)");
403
404         /* Set the default i18n context now, so that opfunc can redefine it if needed! */
405         RNA_def_struct_translation_context(ot->srna, BLF_I18NCONTEXT_OPERATOR_DEFAULT);
406         opfunc(ot, userdata);
407
408         RNA_def_struct_ui_text(ot->srna, ot->name, ot->description);
409         RNA_def_struct_identifier(ot->srna, ot->idname);
410
411         BLI_ghash_insert(global_ops_hash, (void *)ot->idname, ot);
412 }
413
414 wmOperatorTypeMacro *WM_operatortype_macro_define(wmOperatorType *ot, const char *idname)
415 {
416         wmOperatorTypeMacro *otmacro = MEM_callocN(sizeof(wmOperatorTypeMacro), "wmOperatorTypeMacro");
417
418         BLI_strncpy(otmacro->idname, idname, OP_MAX_TYPENAME);
419
420         /* do this on first use, since operatordefinitions might have been not done yet */
421         WM_operator_properties_alloc(&(otmacro->ptr), &(otmacro->properties), idname);
422         WM_operator_properties_sanitize(otmacro->ptr, 1);
423
424         BLI_addtail(&ot->macro, otmacro);
425
426         {
427                 /* operator should always be found but in the event its not. don't segfault */
428                 wmOperatorType *otsub = WM_operatortype_find(idname, 0);
429                 if (otsub) {
430                         RNA_def_pointer_runtime(ot->srna, otsub->idname, otsub->srna,
431                                                 otsub->name, otsub->description);
432                 }
433         }
434
435         return otmacro;
436 }
437
438 static void wm_operatortype_free_macro(wmOperatorType *ot)
439 {
440         wmOperatorTypeMacro *otmacro;
441         
442         for (otmacro = ot->macro.first; otmacro; otmacro = otmacro->next) {
443                 if (otmacro->ptr) {
444                         WM_operator_properties_free(otmacro->ptr);
445                         MEM_freeN(otmacro->ptr);
446                 }
447         }
448         BLI_freelistN(&ot->macro);
449 }
450
451
452 int WM_operatortype_remove(const char *idname)
453 {
454         wmOperatorType *ot = WM_operatortype_find(idname, 0);
455
456         if (ot == NULL)
457                 return 0;
458         
459         RNA_struct_free(&BLENDER_RNA, ot->srna);
460
461         if (ot->last_properties) {
462                 IDP_FreeProperty(ot->last_properties);
463                 MEM_freeN(ot->last_properties);
464         }
465
466         if (ot->macro.first)
467                 wm_operatortype_free_macro(ot);
468
469         BLI_ghash_remove(global_ops_hash, (void *)ot->idname, NULL, NULL);
470
471         MEM_freeN(ot);
472         return 1;
473 }
474
475 /* SOME_OT_op -> some.op */
476 void WM_operator_py_idname(char *to, const char *from)
477 {
478         char *sep = strstr(from, "_OT_");
479         if (sep) {
480                 int ofs = (sep - from);
481                 
482                 /* note, we use ascii tolower instead of system tolower, because the
483                  * latter depends on the locale, and can lead to idname mistmatch */
484                 memcpy(to, from, sizeof(char) * ofs);
485                 BLI_ascii_strtolower(to, ofs);
486
487                 to[ofs] = '.';
488                 BLI_strncpy(to + (ofs + 1), sep + 4, OP_MAX_TYPENAME);
489         }
490         else {
491                 /* should not happen but support just in case */
492                 BLI_strncpy(to, from, OP_MAX_TYPENAME);
493         }
494 }
495
496 /* some.op -> SOME_OT_op */
497 void WM_operator_bl_idname(char *to, const char *from)
498 {
499         if (from) {
500                 char *sep = strchr(from, '.');
501
502                 if (sep) {
503                         int ofs = (sep - from);
504
505                         memcpy(to, from, sizeof(char) * ofs);
506                         BLI_ascii_strtoupper(to, ofs);
507
508                         BLI_strncpy(to + ofs, "_OT_", OP_MAX_TYPENAME);
509                         BLI_strncpy(to + (ofs + 4), sep + 1, OP_MAX_TYPENAME);
510                 }
511                 else {
512                         /* should not happen but support just in case */
513                         BLI_strncpy(to, from, OP_MAX_TYPENAME);
514                 }
515         }
516         else
517                 to[0] = 0;
518 }
519
520 /* print a string representation of the operator, with the args that it runs 
521  * so python can run it again,
522  *
523  * When calling from an existing wmOperator do.
524  * WM_operator_pystring(op->type, op->ptr);
525  */
526 char *WM_operator_pystring(bContext *C, wmOperatorType *ot, PointerRNA *opptr, int all_args)
527 {
528         char idname_py[OP_MAX_TYPENAME];
529
530         /* for building the string */
531         DynStr *dynstr = BLI_dynstr_new();
532         char *cstring;
533         char *cstring_args;
534
535         /* only to get the orginal props for comparisons */
536         PointerRNA opptr_default;
537
538         if (all_args == 0 || opptr == NULL) {
539                 WM_operator_properties_create_ptr(&opptr_default, ot);
540
541                 if (opptr == NULL)
542                         opptr = &opptr_default;
543         }
544
545         WM_operator_py_idname(idname_py, ot->idname);
546         BLI_dynstr_appendf(dynstr, "bpy.ops.%s(", idname_py);
547
548         cstring_args = RNA_pointer_as_string_keywords(C, opptr, &opptr_default, FALSE, all_args);
549         BLI_dynstr_append(dynstr, cstring_args);
550         MEM_freeN(cstring_args);
551
552         if (all_args == 0 || opptr == &opptr_default)
553                 WM_operator_properties_free(&opptr_default);
554
555         BLI_dynstr_append(dynstr, ")");
556
557         cstring = BLI_dynstr_get_cstring(dynstr);
558         BLI_dynstr_free(dynstr);
559         return cstring;
560 }
561
562 void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
563 {
564         RNA_pointer_create(NULL, ot->srna, NULL, ptr);
565 }
566
567 void WM_operator_properties_create(PointerRNA *ptr, const char *opstring)
568 {
569         wmOperatorType *ot = WM_operatortype_find(opstring, 0);
570
571         if (ot)
572                 WM_operator_properties_create_ptr(ptr, ot);
573         else
574                 RNA_pointer_create(NULL, &RNA_OperatorProperties, NULL, ptr);
575 }
576
577 /* similar to the function above except its uses ID properties
578  * used for keymaps and macros */
579 void WM_operator_properties_alloc(PointerRNA **ptr, IDProperty **properties, const char *opstring)
580 {
581         if (*properties == NULL) {
582                 IDPropertyTemplate val = {0};
583                 *properties = IDP_New(IDP_GROUP, &val, "wmOpItemProp");
584         }
585
586         if (*ptr == NULL) {
587                 *ptr = MEM_callocN(sizeof(PointerRNA), "wmOpItemPtr");
588                 WM_operator_properties_create(*ptr, opstring);
589         }
590
591         (*ptr)->data = *properties;
592
593 }
594
595 void WM_operator_properties_sanitize(PointerRNA *ptr, const short no_context)
596 {
597         RNA_STRUCT_BEGIN(ptr, prop)
598         {
599                 switch (RNA_property_type(prop)) {
600                         case PROP_ENUM:
601                                 if (no_context)
602                                         RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT);
603                                 else
604                                         RNA_def_property_clear_flag(prop, PROP_ENUM_NO_CONTEXT);
605                                 break;
606                         case PROP_POINTER:
607                         {
608                                 StructRNA *ptype = RNA_property_pointer_type(ptr, prop);
609
610                                 /* recurse into operator properties */
611                                 if (RNA_struct_is_a(ptype, &RNA_OperatorProperties)) {
612                                         PointerRNA opptr = RNA_property_pointer_get(ptr, prop);
613                                         WM_operator_properties_sanitize(&opptr, no_context);
614                                 }
615                                 break;
616                         }
617                         default:
618                                 break;
619                 }
620         }
621         RNA_STRUCT_END;
622 }
623
624
625 /** set all props to their default,
626  * \param do_update Only update un-initialized props.
627  *
628  * \note, theres nothing specific to operators here.
629  * this could be made a general function.
630  */
631 int WM_operator_properties_default(PointerRNA *ptr, const int do_update)
632 {
633         int is_change = FALSE;
634         RNA_STRUCT_BEGIN(ptr, prop)
635         {
636                 switch (RNA_property_type(prop)) {
637                         case PROP_POINTER:
638                         {
639                                 StructRNA *ptype = RNA_property_pointer_type(ptr, prop);
640                                 if (ptype != &RNA_Struct) {
641                                         PointerRNA opptr = RNA_property_pointer_get(ptr, prop);
642                                         is_change |= WM_operator_properties_default(&opptr, do_update);
643                                 }
644                                 break;
645                         }
646                         default:
647                                 if ((do_update == FALSE) || (RNA_property_is_set(ptr, prop) == FALSE)) {
648                                         if (RNA_property_reset(ptr, prop, -1)) {
649                                                 is_change = 1;
650                                         }
651                                 }
652                                 break;
653                 }
654         }
655         RNA_STRUCT_END;
656
657         return is_change;
658 }
659
660 /* remove all props without PROP_SKIP_SAVE */
661 void WM_operator_properties_reset(wmOperator *op)
662 {
663         if (op->ptr->data) {
664                 PropertyRNA *iterprop;
665                 iterprop = RNA_struct_iterator_property(op->type->srna);
666
667                 RNA_PROP_BEGIN(op->ptr, itemptr, iterprop)
668                 {
669                         PropertyRNA *prop = itemptr.data;
670
671                         if ((RNA_property_flag(prop) & PROP_SKIP_SAVE) == 0) {
672                                 const char *identifier = RNA_property_identifier(prop);
673                                 RNA_struct_idprops_unset(op->ptr, identifier);
674                         }
675                 }
676                 RNA_PROP_END;
677         }
678 }
679
680 void WM_operator_properties_free(PointerRNA *ptr)
681 {
682         IDProperty *properties = ptr->data;
683
684         if (properties) {
685                 IDP_FreeProperty(properties);
686                 MEM_freeN(properties);
687                 ptr->data = NULL; /* just in case */
688         }
689 }
690
691 /* ************ default op callbacks, exported *********** */
692
693 int WM_operator_view3d_distance_invoke(struct bContext *C, struct wmOperator *op, struct wmEvent *UNUSED(event))
694 {
695         Scene *scene = CTX_data_scene(C);
696         View3D *v3d = CTX_wm_view3d(C);
697
698         const float dia = v3d ? ED_view3d_grid_scale(scene, v3d, NULL) : ED_scene_grid_scale(scene, NULL);
699
700         /* always run, so the values are initialized,
701          * otherwise we may get differ behavior when (dia != 1.0) */
702         RNA_STRUCT_BEGIN(op->ptr, prop)
703         {
704                 if (RNA_property_type(prop) == PROP_FLOAT) {
705                         PropertySubType pstype = RNA_property_subtype(prop);
706                         if (pstype == PROP_DISTANCE) {
707                                 /* we don't support arrays yet */
708                                 BLI_assert(RNA_property_array_check(prop) == FALSE);
709                                 /* initialize */
710                                 if (!RNA_property_is_set_ex(op->ptr, prop, FALSE)) {
711                                         const float value = RNA_property_float_get_default(op->ptr, prop) * dia;
712                                         RNA_property_float_set(op->ptr, prop, value);
713                                 }
714                         }
715                 }
716         }
717         RNA_STRUCT_END;
718
719         return op->type->exec(C, op);
720 }
721
722 /* invoke callback, uses enum property named "type" */
723 int WM_menu_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
724 {
725         PropertyRNA *prop = op->type->prop;
726         uiPopupMenu *pup;
727         uiLayout *layout;
728
729         if (prop == NULL) {
730                 printf("%s: %s has no enum property set\n", __func__, op->type->idname);
731         }
732         else if (RNA_property_type(prop) != PROP_ENUM) {
733                 printf("%s: %s \"%s\" is not an enum property\n",
734                        __func__, op->type->idname, RNA_property_identifier(prop));
735         }
736         else if (RNA_property_is_set(op->ptr, prop)) {
737                 const int retval = op->type->exec(C, op);
738                 OPERATOR_RETVAL_CHECK(retval);
739                 return retval;
740         }
741         else {
742                 pup = uiPupMenuBegin(C, RNA_struct_ui_name(op->type->srna), ICON_NONE);
743                 layout = uiPupMenuLayout(pup);
744                 /* set this so the default execution context is the same as submenus */
745                 uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_REGION_WIN);
746                 uiItemsFullEnumO(layout, op->type->idname, RNA_property_identifier(prop), op->ptr->data, WM_OP_EXEC_REGION_WIN, 0);
747                 uiPupMenuEnd(C, pup);
748         }
749
750         return OPERATOR_CANCELLED;
751 }
752
753
754 /* generic enum search invoke popup */
755 static void operator_enum_search_cb(const struct bContext *C, void *arg_ot, const char *str, uiSearchItems *items)
756 {
757         wmOperatorType *ot = (wmOperatorType *)arg_ot;
758         PropertyRNA *prop = ot->prop;
759
760         if (prop == NULL) {
761                 printf("%s: %s has no enum property set\n",
762                        __func__, ot->idname);
763         }
764         else if (RNA_property_type(prop) != PROP_ENUM) {
765                 printf("%s: %s \"%s\" is not an enum property\n",
766                        __func__, ot->idname, RNA_property_identifier(prop));
767         }
768         else {
769                 PointerRNA ptr;
770
771                 EnumPropertyItem *item, *item_array;
772                 int do_free;
773
774                 RNA_pointer_create(NULL, ot->srna, NULL, &ptr);
775                 RNA_property_enum_items((bContext *)C, &ptr, prop, &item_array, NULL, &do_free);
776
777                 for (item = item_array; item->identifier; item++) {
778                         /* note: need to give the index rather than the identifier because the enum can be freed */
779                         if (BLI_strcasestr(item->name, str))
780                                 if (0 == uiSearchItemAdd(items, item->name, SET_INT_IN_POINTER(item->value), 0))
781                                         break;
782                 }
783
784                 if (do_free)
785                         MEM_freeN(item_array);
786         }
787 }
788
789 static void operator_enum_call_cb(struct bContext *C, void *arg1, void *arg2)
790 {
791         wmOperatorType *ot = arg1;
792
793         if (ot) {
794                 if (ot->prop) {
795                         PointerRNA props_ptr;
796                         WM_operator_properties_create_ptr(&props_ptr, ot);
797                         RNA_property_enum_set(&props_ptr, ot->prop, GET_INT_FROM_POINTER(arg2));
798                         WM_operator_name_call(C, ot->idname, WM_OP_EXEC_DEFAULT, &props_ptr);
799                         WM_operator_properties_free(&props_ptr);
800                 }
801                 else {
802                         printf("%s: op->prop for '%s' is NULL\n", __func__, ot->idname);
803                 }
804         }
805 }
806
807 static uiBlock *wm_enum_search_menu(bContext *C, ARegion *ar, void *arg_op)
808 {
809         static char search[256] = "";
810         wmEvent event;
811         wmWindow *win = CTX_wm_window(C);
812         uiBlock *block;
813         uiBut *but;
814         wmOperator *op = (wmOperator *)arg_op;
815
816         block = uiBeginBlock(C, ar, "_popup", UI_EMBOSS);
817         uiBlockSetFlag(block, UI_BLOCK_LOOP | UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_SEARCH_MENU);
818
819 #if 0 /* ok, this isn't so easy... */
820         uiDefBut(block, LABEL, 0, RNA_struct_ui_name(op->type->srna), 10, 10, 180, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
821 #endif
822         but = uiDefSearchBut(block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 10, 9 * UI_UNIT_X, UI_UNIT_Y, 0, 0, "");
823         uiButSetSearchFunc(but, operator_enum_search_cb, op->type, operator_enum_call_cb, NULL);
824
825         /* fake button, it holds space for search items */
826         uiDefBut(block, LABEL, 0, "", 10, 10 - uiSearchBoxHeight(), uiSearchBoxWidth(), uiSearchBoxHeight(), NULL, 0, 0, 0, 0, NULL);
827
828         uiPopupBoundsBlock(block, 6, 0, -UI_UNIT_Y); /* move it downwards, mouse over button */
829         uiEndBlock(C, block);
830
831         event = *(win->eventstate);  /* XXX huh huh? make api call */
832         event.type = EVT_BUT_OPEN;
833         event.val = KM_PRESS;
834         event.customdata = but;
835         event.customdatafree = FALSE;
836         wm_event_add(win, &event);
837
838         return block;
839 }
840
841
842 int WM_enum_search_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
843 {
844         uiPupBlock(C, wm_enum_search_menu, op);
845         return OPERATOR_CANCELLED;
846 }
847
848 /* Can't be used as an invoke directly, needs message arg (can be NULL) */
849 int WM_operator_confirm_message(bContext *C, wmOperator *op, const char *message)
850 {
851         uiPopupMenu *pup;
852         uiLayout *layout;
853         IDProperty *properties = op->ptr->data;
854
855         if (properties && properties->len)
856                 properties = IDP_CopyProperty(op->ptr->data);
857         else
858                 properties = NULL;
859
860         pup = uiPupMenuBegin(C, IFACE_("OK?"), ICON_QUESTION);
861         layout = uiPupMenuLayout(pup);
862         uiItemFullO_ptr(layout, op->type, message, ICON_NONE, properties, WM_OP_EXEC_REGION_WIN, 0);
863         uiPupMenuEnd(C, pup);
864         
865         return OPERATOR_CANCELLED;
866 }
867
868
869 int WM_operator_confirm(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
870 {
871         return WM_operator_confirm_message(C, op, NULL);
872 }
873
874 /* op->invoke, opens fileselect if path property not set, otherwise executes */
875 int WM_operator_filesel(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
876 {
877         if (RNA_struct_property_is_set(op->ptr, "filepath")) {
878                 return WM_operator_call_notest(C, op); /* call exec direct */
879         }
880         else {
881                 WM_event_add_fileselect(C, op);
882                 return OPERATOR_RUNNING_MODAL;
883         }
884 }
885
886 int WM_operator_filesel_ensure_ext_imtype(wmOperator *op, const char imtype)
887 {
888         PropertyRNA *prop;
889         char filepath[FILE_MAX];
890         /* dont NULL check prop, this can only run on ops with a 'filepath' */
891         prop = RNA_struct_find_property(op->ptr, "filepath");
892         RNA_property_string_get(op->ptr, prop, filepath);
893         if (BKE_add_image_extension(filepath, imtype)) {
894                 RNA_property_string_set(op->ptr, prop, filepath);
895                 /* note, we could check for and update 'filename' here,
896                  * but so far nothing needs this. */
897                 return TRUE;
898         }
899         return FALSE;
900 }
901
902 /* default properties for fileselect */
903 void WM_operator_properties_filesel(wmOperatorType *ot, int filter, short type, short action, short flag, short display)
904 {
905         PropertyRNA *prop;
906
907         static EnumPropertyItem file_display_items[] = {
908                 {FILE_DEFAULTDISPLAY, "FILE_DEFAULTDISPLAY", 0, "Default", "Automatically determine display type for files"},
909                 {FILE_SHORTDISPLAY, "FILE_SHORTDISPLAY", ICON_SHORTDISPLAY, "Short List", "Display files as short list"},
910                 {FILE_LONGDISPLAY, "FILE_LONGDISPLAY", ICON_LONGDISPLAY, "Long List", "Display files as a detailed list"},
911                 {FILE_IMGDISPLAY, "FILE_IMGDISPLAY", ICON_IMGDISPLAY, "Thumbnails", "Display files as thumbnails"},
912                 {0, NULL, 0, NULL, NULL}
913         };
914
915
916         if (flag & WM_FILESEL_FILEPATH)
917                 RNA_def_string_file_path(ot->srna, "filepath", "", FILE_MAX, "File Path", "Path to file");
918
919         if (flag & WM_FILESEL_DIRECTORY)
920                 RNA_def_string_dir_path(ot->srna, "directory", "", FILE_MAX, "Directory", "Directory of the file");
921
922         if (flag & WM_FILESEL_FILENAME)
923                 RNA_def_string_file_name(ot->srna, "filename", "", FILE_MAX, "File Name", "Name of the file");
924
925         if (flag & WM_FILESEL_FILES)
926                 RNA_def_collection_runtime(ot->srna, "files", &RNA_OperatorFileListElement, "Files", "");
927
928         if (action == FILE_SAVE) {
929                 /* note, this is only used to check if we should highlight the filename area red when the
930                  * filepath is an existing file. */
931                 prop = RNA_def_boolean(ot->srna, "check_existing", 1, "Check Existing", "Check and warn on overwriting existing files");
932                 RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
933         }
934         
935         prop = RNA_def_boolean(ot->srna, "filter_blender", (filter & BLENDERFILE), "Filter .blend files", "");
936         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
937         prop = RNA_def_boolean(ot->srna, "filter_backup", (filter & BLENDERFILE_BACKUP), "Filter .blend files", "");
938         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
939         prop = RNA_def_boolean(ot->srna, "filter_image", (filter & IMAGEFILE), "Filter image files", "");
940         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
941         prop = RNA_def_boolean(ot->srna, "filter_movie", (filter & MOVIEFILE), "Filter movie files", "");
942         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
943         prop = RNA_def_boolean(ot->srna, "filter_python", (filter & PYSCRIPTFILE), "Filter python files", "");
944         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
945         prop = RNA_def_boolean(ot->srna, "filter_font", (filter & FTFONTFILE), "Filter font files", "");
946         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
947         prop = RNA_def_boolean(ot->srna, "filter_sound", (filter & SOUNDFILE), "Filter sound files", "");
948         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
949         prop = RNA_def_boolean(ot->srna, "filter_text", (filter & TEXTFILE), "Filter text files", "");
950         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
951         prop = RNA_def_boolean(ot->srna, "filter_btx", (filter & BTXFILE), "Filter btx files", "");
952         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
953         prop = RNA_def_boolean(ot->srna, "filter_collada", (filter & COLLADAFILE), "Filter COLLADA files", "");
954         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
955         prop = RNA_def_boolean(ot->srna, "filter_folder", (filter & FOLDERFILE), "Filter folders", "");
956         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
957
958         prop = RNA_def_int(ot->srna, "filemode", type, FILE_LOADLIB, FILE_SPECIAL,
959                            "File Browser Mode", "The setting for the file browser mode to load a .blend file, a library or a special file",
960                            FILE_LOADLIB, FILE_SPECIAL);
961         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
962
963         if (flag & WM_FILESEL_RELPATH)
964                 RNA_def_boolean(ot->srna, "relative_path", TRUE, "Relative Path", "Select the file relative to the blend file");
965
966         prop = RNA_def_enum(ot->srna, "display_type", file_display_items, display, "Display Type", "");
967         RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
968 }
969
970 void WM_operator_properties_select_all(wmOperatorType *ot)
971 {
972         static EnumPropertyItem select_all_actions[] = {
973                 {SEL_TOGGLE, "TOGGLE", 0, "Toggle", "Toggle selection for all elements"},
974                 {SEL_SELECT, "SELECT", 0, "Select", "Select all elements"},
975                 {SEL_DESELECT, "DESELECT", 0, "Deselect", "Deselect all elements"},
976                 {SEL_INVERT, "INVERT", 0, "Invert", "Invert selection of all elements"},
977                 {0, NULL, 0, NULL, NULL}
978         };
979
980         RNA_def_enum(ot->srna, "action", select_all_actions, SEL_TOGGLE, "Action", "Selection action to execute");
981 }
982
983 void WM_operator_properties_border(wmOperatorType *ot)
984 {
985         RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
986         RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
987         RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
988         RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
989 }
990
991 void WM_operator_properties_border_to_rcti(struct wmOperator *op, rcti *rect)
992 {
993         rect->xmin = RNA_int_get(op->ptr, "xmin");
994         rect->ymin = RNA_int_get(op->ptr, "ymin");
995         rect->xmax = RNA_int_get(op->ptr, "xmax");
996         rect->ymax = RNA_int_get(op->ptr, "ymax");
997 }
998
999 void WM_operator_properties_gesture_border(wmOperatorType *ot, int extend)
1000 {
1001         RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Gesture Mode", "", INT_MIN, INT_MAX);
1002
1003         WM_operator_properties_border(ot);
1004
1005         if (extend) {
1006                 RNA_def_boolean(ot->srna, "extend", 1, "Extend", "Extend selection instead of deselecting everything first");
1007         }
1008 }
1009
1010 void WM_operator_properties_mouse_select(wmOperatorType *ot)
1011 {
1012         RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend selection instead of deselecting everything first");
1013         RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Remove from selection");
1014         RNA_def_boolean(ot->srna, "toggle", 0, "Toggle Selection", "Toggle the selection");
1015 }
1016
1017 void WM_operator_properties_gesture_straightline(wmOperatorType *ot, int cursor)
1018 {
1019         RNA_def_int(ot->srna, "xstart", 0, INT_MIN, INT_MAX, "X Start", "", INT_MIN, INT_MAX);
1020         RNA_def_int(ot->srna, "xend", 0, INT_MIN, INT_MAX, "X End", "", INT_MIN, INT_MAX);
1021         RNA_def_int(ot->srna, "ystart", 0, INT_MIN, INT_MAX, "Y Start", "", INT_MIN, INT_MAX);
1022         RNA_def_int(ot->srna, "yend", 0, INT_MIN, INT_MAX, "Y End", "", INT_MIN, INT_MAX);
1023         
1024         if (cursor)
1025                 RNA_def_int(ot->srna, "cursor", cursor, 0, INT_MAX, "Cursor", "Mouse cursor style to use during the modal operator", 0, INT_MAX);
1026 }
1027
1028
1029 /* op->poll */
1030 int WM_operator_winactive(bContext *C)
1031 {
1032         if (CTX_wm_window(C) == NULL) return 0;
1033         return 1;
1034 }
1035
1036 /* return FALSE, if the UI should be disabled */
1037 int WM_operator_check_ui_enabled(const bContext *C, const char *idname)
1038 {
1039         wmWindowManager *wm = CTX_wm_manager(C);
1040         Scene *scene = CTX_data_scene(C);
1041
1042         return !(ED_undo_valid(C, idname) == 0 || WM_jobs_test(wm, scene, WM_JOB_TYPE_ANY));
1043 }
1044
1045 wmOperator *WM_operator_last_redo(const bContext *C)
1046 {
1047         wmWindowManager *wm = CTX_wm_manager(C);
1048         wmOperator *op;
1049
1050         /* only for operators that are registered and did an undo push */
1051         for (op = wm->operators.last; op; op = op->prev)
1052                 if ((op->type->flag & OPTYPE_REGISTER) && (op->type->flag & OPTYPE_UNDO))
1053                         break;
1054
1055         return op;
1056 }
1057
1058 static void wm_block_redo_cb(bContext *C, void *arg_op, int UNUSED(arg_event))
1059 {
1060         wmOperator *op = arg_op;
1061
1062         if (op == WM_operator_last_redo(C)) {
1063                 /* operator was already executed once? undo & repeat */
1064                 ED_undo_operator_repeat(C, op);
1065         }
1066         else {
1067                 /* operator not executed yet, call it */
1068                 ED_undo_push_op(C, op);
1069                 wm_operator_register(C, op);
1070
1071                 WM_operator_repeat(C, op);
1072         }
1073 }
1074
1075 static uiBlock *wm_block_create_redo(bContext *C, ARegion *ar, void *arg_op)
1076 {
1077         wmOperator *op = arg_op;
1078         uiBlock *block;
1079         uiLayout *layout;
1080         uiStyle *style = UI_GetStyle();
1081         int width = 300;
1082
1083         block = uiBeginBlock(C, ar, __func__, UI_EMBOSS);
1084         uiBlockClearFlag(block, UI_BLOCK_LOOP);
1085         uiBlockSetFlag(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_MOVEMOUSE_QUIT);
1086
1087         /* if register is not enabled, the operator gets freed on OPERATOR_FINISHED
1088          * ui_apply_but_funcs_after calls ED_undo_operator_repeate_cb and crashes */
1089         assert(op->type->flag & OPTYPE_REGISTER);
1090
1091         uiBlockSetHandleFunc(block, wm_block_redo_cb, arg_op);
1092         layout = uiBlockLayout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, width, UI_UNIT_Y, style);
1093
1094         if (op == WM_operator_last_redo(C))
1095                 if (!WM_operator_check_ui_enabled(C, op->type->name))
1096                         uiLayoutSetEnabled(layout, FALSE);
1097
1098         if (op->type->flag & OPTYPE_MACRO) {
1099                 for (op = op->macro.first; op; op = op->next) {
1100                         uiItemL(layout, RNA_struct_ui_name(op->type->srna), ICON_NONE);
1101                         uiLayoutOperatorButs(C, layout, op, NULL, 'H', UI_LAYOUT_OP_SHOW_TITLE);
1102                 }
1103         }
1104         else {
1105                 uiLayoutOperatorButs(C, layout, op, NULL, 'H', UI_LAYOUT_OP_SHOW_TITLE);
1106         }
1107         
1108         uiPopupBoundsBlock(block, 4, 0, 0);
1109         uiEndBlock(C, block);
1110
1111         return block;
1112 }
1113
1114 typedef struct wmOpPopUp {
1115         wmOperator *op;
1116         int width;
1117         int height;
1118         int free_op;
1119 } wmOpPopUp;
1120
1121 /* Only invoked by OK button in popups created with wm_block_dialog_create() */
1122 static void dialog_exec_cb(bContext *C, void *arg1, void *arg2)
1123 {
1124         wmOpPopUp *data = arg1;
1125         uiBlock *block = arg2;
1126
1127         WM_operator_call(C, data->op);
1128
1129         /* let execute handle freeing it */
1130         //data->free_op = FALSE;
1131         //data->op = NULL;
1132
1133         /* in this case, wm_operator_ui_popup_cancel wont run */
1134         MEM_freeN(data);
1135
1136         uiPupBlockClose(C, block);
1137 }
1138
1139 static void dialog_check_cb(bContext *C, void *op_ptr, void *UNUSED(arg))
1140 {
1141         wmOperator *op = op_ptr;
1142         if (op->type->check) {
1143                 if (op->type->check(C, op)) {
1144                         /* refresh */
1145                 }
1146         }
1147 }
1148
1149 /* Dialogs are popups that require user verification (click OK) before exec */
1150 static uiBlock *wm_block_dialog_create(bContext *C, ARegion *ar, void *userData)
1151 {
1152         wmOpPopUp *data = userData;
1153         wmOperator *op = data->op;
1154         uiBlock *block;
1155         uiLayout *layout;
1156         uiStyle *style = UI_GetStyle();
1157
1158         block = uiBeginBlock(C, ar, __func__, UI_EMBOSS);
1159         uiBlockClearFlag(block, UI_BLOCK_LOOP);
1160
1161         /* intentionally don't use 'UI_BLOCK_MOVEMOUSE_QUIT', some dialogs have many items
1162          * where quitting by accident is very annoying */
1163         uiBlockSetFlag(block, UI_BLOCK_KEEP_OPEN);
1164
1165         layout = uiBlockLayout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, data->width, data->height, style);
1166         
1167         uiBlockSetFunc(block, dialog_check_cb, op, NULL);
1168
1169         uiLayoutOperatorButs(C, layout, op, NULL, 'H', UI_LAYOUT_OP_SHOW_TITLE);
1170         
1171         /* clear so the OK button is left alone */
1172         uiBlockSetFunc(block, NULL, NULL, NULL);
1173
1174         /* new column so as not to interfere with custom layouts [#26436] */
1175         {
1176                 uiBlock *col_block;
1177                 uiLayout *col;
1178                 uiBut *btn;
1179
1180                 col = uiLayoutColumn(layout, FALSE);
1181                 col_block = uiLayoutGetBlock(col);
1182                 /* Create OK button, the callback of which will execute op */
1183                 btn = uiDefBut(col_block, BUT, 0, IFACE_("OK"), 0, -30, 0, UI_UNIT_Y, NULL, 0, 0, 0, 0, "");
1184                 uiButSetFunc(btn, dialog_exec_cb, data, col_block);
1185         }
1186
1187         /* center around the mouse */
1188         uiPopupBoundsBlock(block, 4, data->width / -2, data->height / 2);
1189         uiEndBlock(C, block);
1190
1191         return block;
1192 }
1193
1194 static uiBlock *wm_operator_ui_create(bContext *C, ARegion *ar, void *userData)
1195 {
1196         wmOpPopUp *data = userData;
1197         wmOperator *op = data->op;
1198         uiBlock *block;
1199         uiLayout *layout;
1200         uiStyle *style = UI_GetStyle();
1201
1202         block = uiBeginBlock(C, ar, __func__, UI_EMBOSS);
1203         uiBlockClearFlag(block, UI_BLOCK_LOOP);
1204         uiBlockSetFlag(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_MOVEMOUSE_QUIT);
1205
1206         layout = uiBlockLayout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, data->width, data->height, style);
1207
1208         /* since ui is defined the auto-layout args are not used */
1209         uiLayoutOperatorButs(C, layout, op, NULL, 'V', 0);
1210
1211         uiPopupBoundsBlock(block, 4, 0, 0);
1212         uiEndBlock(C, block);
1213
1214         return block;
1215 }
1216
1217 static void wm_operator_ui_popup_cancel(void *userData)
1218 {
1219         wmOpPopUp *data = userData;
1220         if (data->free_op && data->op) {
1221                 wmOperator *op = data->op;
1222                 WM_operator_free(op);
1223         }
1224
1225         MEM_freeN(data);
1226 }
1227
1228 static void wm_operator_ui_popup_ok(struct bContext *C, void *arg, int retval)
1229 {
1230         wmOpPopUp *data = arg;
1231         wmOperator *op = data->op;
1232
1233         if (op && retval > 0)
1234                 WM_operator_call(C, op);
1235         
1236         MEM_freeN(data);
1237 }
1238
1239 int WM_operator_ui_popup(bContext *C, wmOperator *op, int width, int height)
1240 {
1241         wmOpPopUp *data = MEM_callocN(sizeof(wmOpPopUp), "WM_operator_ui_popup");
1242         data->op = op;
1243         data->width = width;
1244         data->height = height;
1245         data->free_op = TRUE; /* if this runs and gets registered we may want not to free it */
1246         uiPupBlockEx(C, wm_operator_ui_create, NULL, wm_operator_ui_popup_cancel, data);
1247         return OPERATOR_RUNNING_MODAL;
1248 }
1249
1250 /**
1251  * For use by #WM_operator_props_popup_call, #WM_operator_props_popup only.
1252  *
1253  * \note operator menu needs undo flag enabled , for redo callback */
1254 static int wm_operator_props_popup_ex(bContext *C, wmOperator *op, const int do_call)
1255 {
1256         if ((op->type->flag & OPTYPE_REGISTER) == 0) {
1257                 BKE_reportf(op->reports, RPT_ERROR,
1258                             "Operator '%s' does not have register enabled, incorrect invoke function", op->type->idname);
1259                 return OPERATOR_CANCELLED;
1260         }
1261
1262         /* if we don't have global undo, we can't do undo push for automatic redo,
1263          * so we require manual OK clicking in this popup */
1264         if (!(U.uiflag & USER_GLOBALUNDO))
1265                 return WM_operator_props_dialog_popup(C, op, 300, UI_UNIT_Y);
1266
1267         uiPupBlock(C, wm_block_create_redo, op);
1268
1269         if (do_call)
1270                 wm_block_redo_cb(C, op, 0);
1271
1272         return OPERATOR_RUNNING_MODAL;
1273 }
1274
1275 /* Same as WM_operator_props_popup but call the operator first,
1276  * This way - the button values correspond to the result of the operator.
1277  * Without this, first access to a button will make the result jump,
1278  * see [#32452] */
1279 int WM_operator_props_popup_call(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1280 {
1281         return wm_operator_props_popup_ex(C, op, TRUE);
1282 }
1283
1284 int WM_operator_props_popup(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1285 {
1286         return wm_operator_props_popup_ex(C, op, FALSE);
1287 }
1288
1289 int WM_operator_props_dialog_popup(bContext *C, wmOperator *op, int width, int height)
1290 {
1291         wmOpPopUp *data = MEM_callocN(sizeof(wmOpPopUp), "WM_operator_props_dialog_popup");
1292         
1293         data->op = op;
1294         data->width = width;
1295         data->height = height;
1296         data->free_op = TRUE; /* if this runs and gets registered we may want not to free it */
1297
1298         /* op is not executed until popup OK but is clicked */
1299         uiPupBlockEx(C, wm_block_dialog_create, wm_operator_ui_popup_ok, wm_operator_ui_popup_cancel, data);
1300
1301         return OPERATOR_RUNNING_MODAL;
1302 }
1303
1304 int WM_operator_redo_popup(bContext *C, wmOperator *op)
1305 {
1306         /* CTX_wm_reports(C) because operator is on stack, not active in event system */
1307         if ((op->type->flag & OPTYPE_REGISTER) == 0) {
1308                 BKE_reportf(CTX_wm_reports(C), RPT_ERROR,
1309                             "Operator redo '%s' does not have register enabled, incorrect invoke function", op->type->idname);
1310                 return OPERATOR_CANCELLED;
1311         }
1312         if (op->type->poll && op->type->poll(C) == 0) {
1313                 BKE_reportf(CTX_wm_reports(C), RPT_ERROR, "Operator redo '%s': wrong context", op->type->idname);
1314                 return OPERATOR_CANCELLED;
1315         }
1316         
1317         uiPupBlock(C, wm_block_create_redo, op);
1318
1319         return OPERATOR_CANCELLED;
1320 }
1321
1322 /* ***************** Debug menu ************************* */
1323
1324 static int wm_debug_menu_exec(bContext *C, wmOperator *op)
1325 {
1326         G.debug_value = RNA_int_get(op->ptr, "debug_value");
1327         ED_screen_refresh(CTX_wm_manager(C), CTX_wm_window(C));
1328         WM_event_add_notifier(C, NC_WINDOW, NULL);
1329
1330         return OPERATOR_FINISHED;
1331 }
1332
1333 static int wm_debug_menu_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1334 {
1335         RNA_int_set(op->ptr, "debug_value", G.debug_value);
1336         return WM_operator_props_dialog_popup(C, op, 9 * UI_UNIT_X, UI_UNIT_Y);
1337 }
1338
1339 static void WM_OT_debug_menu(wmOperatorType *ot)
1340 {
1341         ot->name = "Debug Menu";
1342         ot->idname = "WM_OT_debug_menu";
1343         ot->description = "Open a popup to set the debug level";
1344         
1345         ot->invoke = wm_debug_menu_invoke;
1346         ot->exec = wm_debug_menu_exec;
1347         ot->poll = WM_operator_winactive;
1348         
1349         RNA_def_int(ot->srna, "debug_value", 0, SHRT_MIN, SHRT_MAX, "Debug Value", "", -10000, 10000);
1350 }
1351
1352 /* ***************** Operator defaults ************************* */
1353 static int wm_operator_defaults_exec(bContext *C, wmOperator *op)
1354 {
1355         PointerRNA ptr = CTX_data_pointer_get_type(C, "active_operator", &RNA_Operator);
1356
1357         if (!ptr.data) {
1358                 BKE_report(op->reports, RPT_ERROR, "No operator in context");
1359                 return OPERATOR_CANCELLED;
1360         }
1361
1362         WM_operator_properties_reset((wmOperator *)ptr.data);
1363         return OPERATOR_FINISHED;
1364 }
1365
1366 /* used by operator preset menu. pre-2.65 this was a 'Reset' button */
1367 static void WM_OT_operator_defaults(wmOperatorType *ot)
1368 {
1369         ot->name = "Restore Defaults";
1370         ot->idname = "WM_OT_operator_defaults";
1371         ot->description = "Set the active operator to its default values";
1372
1373         ot->exec = wm_operator_defaults_exec;
1374
1375         ot->flag = OPTYPE_INTERNAL;
1376 }
1377
1378 /* ***************** Splash Screen ************************* */
1379
1380 static void wm_block_splash_close(bContext *C, void *arg_block, void *UNUSED(arg))
1381 {
1382         uiPupBlockClose(C, arg_block);
1383 }
1384
1385 static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *arg_unused);
1386
1387 /* XXX: hack to refresh splash screen with updated prest menu name,
1388  * since popup blocks don't get regenerated like panels do */
1389 static void wm_block_splash_refreshmenu(bContext *UNUSED(C), void *UNUSED(arg_block), void *UNUSED(arg))
1390 {
1391         /* ugh, causes crashes in other buttons, disabling for now until 
1392          * a better fix */
1393 #if 0
1394         uiPupBlockClose(C, arg_block);
1395         uiPupBlock(C, wm_block_create_splash, NULL);
1396 #endif
1397 }
1398
1399 static int wm_resource_check_prev(void)
1400 {
1401
1402         char *res = BLI_get_folder_version(BLENDER_RESOURCE_PATH_USER, BLENDER_VERSION, TRUE);
1403
1404         // if (res) printf("USER: %s\n", res);
1405
1406 #if 0 /* ignore the local folder */
1407         if (res == NULL) {
1408                 /* with a local dir, copying old files isn't useful since local dir get priority for config */
1409                 res = BLI_get_folder_version(BLENDER_RESOURCE_PATH_LOCAL, BLENDER_VERSION, TRUE);
1410         }
1411 #endif
1412
1413         // if (res) printf("LOCAL: %s\n", res);
1414         if (res) {
1415                 return FALSE;
1416         }
1417         else {
1418                 return (BLI_get_folder_version(BLENDER_RESOURCE_PATH_USER, BLENDER_VERSION - 1, TRUE) != NULL);
1419         }
1420 }
1421
1422 static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(arg))
1423 {
1424         uiBlock *block;
1425         uiBut *but;
1426         uiLayout *layout, *split, *col;
1427         uiStyle *style = UI_GetStyle();
1428         struct RecentFile *recent;
1429         int i;
1430         MenuType *mt = WM_menutype_find("USERPREF_MT_splash", TRUE);
1431         char url[96];
1432         char file [FILE_MAX];
1433
1434 #ifndef WITH_HEADLESS
1435         extern char datatoc_splash_png[];
1436         extern int datatoc_splash_png_size;
1437
1438         ImBuf *ibuf = IMB_ibImageFromMemory((unsigned char *)datatoc_splash_png,
1439                                             datatoc_splash_png_size, IB_rect, NULL, "<splash screen>");
1440 #else
1441         ImBuf *ibuf = NULL;
1442 #endif
1443
1444
1445 #ifdef WITH_BUILDINFO
1446         int ver_width, rev_width;
1447         char version_buf[128];
1448         char revision_buf[128];
1449         extern char build_rev[];
1450         
1451         BLI_snprintf(version_buf, sizeof(version_buf),
1452                      "%d.%02d.%d", BLENDER_VERSION / 100, BLENDER_VERSION % 100, BLENDER_SUBVERSION);
1453         BLI_snprintf(revision_buf, sizeof(revision_buf), "r%s", build_rev);
1454         
1455         BLF_size(style->widgetlabel.uifont_id, style->widgetlabel.points, U.dpi);
1456         ver_width = (int)BLF_width(style->widgetlabel.uifont_id, version_buf) + 5;
1457         rev_width = (int)BLF_width(style->widgetlabel.uifont_id, revision_buf) + 5;
1458 #endif  /* WITH_BUILDINFO */
1459
1460         block = uiBeginBlock(C, ar, "_popup", UI_EMBOSS);
1461
1462         /* note on UI_BLOCK_NO_WIN_CLIP, the window size is not always synchronized
1463          * with the OS when the splash shows, window clipping in this case gives
1464          * ugly results and clipping the splash isn't useful anyway, just disable it [#32938] */
1465         uiBlockSetFlag(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_NO_WIN_CLIP);
1466         
1467         but = uiDefBut(block, BUT_IMAGE, 0, "", 0, 10, 501, 282, ibuf, 0.0, 0.0, 0, 0, ""); /* button owns the imbuf now */
1468         uiButSetFunc(but, wm_block_splash_close, block, NULL);
1469         uiBlockSetFunc(block, wm_block_splash_refreshmenu, block, NULL);
1470         
1471 #ifdef WITH_BUILDINFO   
1472         uiDefBut(block, LABEL, 0, version_buf, 494 - ver_width, 282 - 24, ver_width, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL);
1473         uiDefBut(block, LABEL, 0, revision_buf, 494 - rev_width, 282 - 36, rev_width, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL);
1474 #endif  /* WITH_BUILDINFO */
1475         
1476         layout = uiBlockLayout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 10, 2, 480, 110, style);
1477         
1478         uiBlockSetEmboss(block, UI_EMBOSS);
1479         /* show the splash menu (containing interaction presets), using python */
1480         if (mt) {
1481                 Menu menu = {NULL};
1482                 menu.layout = layout;
1483                 menu.type = mt;
1484                 mt->draw(C, &menu);
1485
1486 //              wmWindowManager *wm = CTX_wm_manager(C);
1487 //              uiItemM(layout, C, "USERPREF_MT_keyconfigs", U.keyconfigstr, ICON_NONE);
1488         }
1489         
1490         uiBlockSetEmboss(block, UI_EMBOSSP);
1491         uiLayoutSetOperatorContext(layout, WM_OP_EXEC_REGION_WIN);
1492         
1493         split = uiLayoutSplit(layout, 0.0f, FALSE);
1494         col = uiLayoutColumn(split, FALSE);
1495         uiItemL(col, "Links", ICON_NONE);
1496         uiItemStringO(col, IFACE_("Donations"), ICON_URL, "WM_OT_url_open", "url", "http://www.blender.org/blenderorg/blender-foundation/donation-payment");
1497         uiItemStringO(col, IFACE_("Credits"), ICON_URL, "WM_OT_url_open", "url", "http://www.blender.org/development/credits");
1498         uiItemStringO(col, IFACE_("Release Log"), ICON_URL, "WM_OT_url_open", "url", "http://www.blender.org/development/release-logs/blender-265");
1499         uiItemStringO(col, IFACE_("Manual"), ICON_URL, "WM_OT_url_open", "url", "http://wiki.blender.org/index.php/Doc:2.6/Manual");
1500         uiItemStringO(col, IFACE_("Blender Website"), ICON_URL, "WM_OT_url_open", "url", "http://www.blender.org");
1501         uiItemStringO(col, IFACE_("User Community"), ICON_URL, "WM_OT_url_open", "url", "http://www.blender.org/community/user-community");
1502         if (strcmp(STRINGIFY(BLENDER_VERSION_CYCLE), "release") == 0) {
1503                 BLI_snprintf(url, sizeof(url), "http://www.blender.org/documentation/blender_python_api_%d_%d" STRINGIFY(BLENDER_VERSION_CHAR) "_release", BLENDER_VERSION / 100, BLENDER_VERSION % 100);
1504         }
1505         else {
1506                 BLI_snprintf(url, sizeof(url), "http://www.blender.org/documentation/blender_python_api_%d_%d_%d", BLENDER_VERSION / 100, BLENDER_VERSION % 100, BLENDER_SUBVERSION);
1507         }
1508         uiItemStringO(col, IFACE_("Python API Reference"), ICON_URL, "WM_OT_url_open", "url", url);
1509         uiItemL(col, "", ICON_NONE);
1510
1511         col = uiLayoutColumn(split, FALSE);
1512
1513         if (wm_resource_check_prev()) {
1514                 uiItemO(col, NULL, ICON_NEW, "WM_OT_copy_prev_settings");
1515                 uiItemS(col);
1516         }
1517
1518         uiItemL(col, IFACE_("Recent"), ICON_NONE);
1519         for (recent = G.recent_files.first, i = 0; (i < 5) && (recent); recent = recent->next, i++) {
1520                 BLI_split_file_part(recent->filepath, file, sizeof(file));
1521                 if (BLO_has_bfile_extension(file))
1522                         uiItemStringO(col, BLI_path_basename(recent->filepath), ICON_FILE_BLEND, "WM_OT_open_mainfile", "filepath", recent->filepath);
1523                 else
1524                         uiItemStringO(col, BLI_path_basename(recent->filepath), ICON_FILE_BACKUP, "WM_OT_open_mainfile", "filepath", recent->filepath);
1525         }
1526
1527         uiItemS(col);
1528         uiItemO(col, NULL, ICON_RECOVER_LAST, "WM_OT_recover_last_session");
1529         uiItemL(col, "", ICON_NONE);
1530         
1531         uiCenteredBoundsBlock(block, 0);
1532         uiEndBlock(C, block);
1533         
1534         return block;
1535 }
1536
1537 static int wm_splash_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *UNUSED(event))
1538 {
1539         uiPupBlock(C, wm_block_create_splash, NULL);
1540         
1541         return OPERATOR_FINISHED;
1542 }
1543
1544 static void WM_OT_splash(wmOperatorType *ot)
1545 {
1546         ot->name = "Splash Screen";
1547         ot->idname = "WM_OT_splash";
1548         ot->description = "Opens a blocking popup region with release info";
1549         
1550         ot->invoke = wm_splash_invoke;
1551         ot->poll = WM_operator_winactive;
1552 }
1553
1554
1555 /* ***************** Search menu ************************* */
1556 static uiBlock *wm_block_search_menu(bContext *C, ARegion *ar, void *UNUSED(arg_op))
1557 {
1558         static char search[256] = "";
1559         wmEvent event;
1560         wmWindow *win = CTX_wm_window(C);
1561         uiBlock *block;
1562         uiBut *but;
1563         
1564         block = uiBeginBlock(C, ar, "_popup", UI_EMBOSS);
1565         uiBlockSetFlag(block, UI_BLOCK_LOOP | UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_SEARCH_MENU);
1566         
1567         but = uiDefSearchBut(block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 10, 9 * UI_UNIT_X, UI_UNIT_Y, 0, 0, "");
1568         uiOperatorSearch_But(but);
1569         
1570         /* fake button, it holds space for search items */
1571         uiDefBut(block, LABEL, 0, "", 10, 10 - uiSearchBoxHeight(), uiSearchBoxWidth(), uiSearchBoxHeight(), NULL, 0, 0, 0, 0, NULL);
1572         
1573         uiPopupBoundsBlock(block, 6, 0, -UI_UNIT_Y); /* move it downwards, mouse over button */
1574         uiEndBlock(C, block);
1575         
1576         event = *(win->eventstate);  /* XXX huh huh? make api call */
1577         event.type = EVT_BUT_OPEN;
1578         event.val = KM_PRESS;
1579         event.customdata = but;
1580         event.customdatafree = FALSE;
1581         wm_event_add(win, &event);
1582         
1583         return block;
1584 }
1585
1586 static int wm_search_menu_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
1587 {
1588         return OPERATOR_FINISHED;
1589 }
1590
1591 static int wm_search_menu_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1592 {
1593         uiPupBlock(C, wm_block_search_menu, op);
1594         
1595         return OPERATOR_CANCELLED;
1596 }
1597
1598 /* op->poll */
1599 static int wm_search_menu_poll(bContext *C)
1600 {
1601         if (CTX_wm_window(C) == NULL) {
1602                 return 0;
1603         }
1604         else {
1605                 ScrArea *sa = CTX_wm_area(C);
1606                 if (sa) {
1607                         if (sa->spacetype == SPACE_CONSOLE) return 0;  /* XXX - so we can use the shortcut in the console */
1608                         if (sa->spacetype == SPACE_TEXT) return 0;     /* XXX - so we can use the spacebar in the text editor */
1609                 }
1610                 else {
1611                         Object *editob = CTX_data_edit_object(C);
1612                         if (editob && editob->type == OB_FONT) return 0;  /* XXX - so we can use the spacebar for entering text */
1613                 }
1614         }
1615         return 1;
1616 }
1617
1618 static void WM_OT_search_menu(wmOperatorType *ot)
1619 {
1620         ot->name = "Search Menu";
1621         ot->idname = "WM_OT_search_menu";
1622         ot->description = "Pop-up a search menu over all available operators in current context";
1623         
1624         ot->invoke = wm_search_menu_invoke;
1625         ot->exec = wm_search_menu_exec;
1626         ot->poll = wm_search_menu_poll;
1627 }
1628
1629 static int wm_call_menu_exec(bContext *C, wmOperator *op)
1630 {
1631         char idname[BKE_ST_MAXNAME];
1632         RNA_string_get(op->ptr, "name", idname);
1633
1634         uiPupMenuInvoke(C, idname);
1635
1636         return OPERATOR_CANCELLED;
1637 }
1638
1639 static void WM_OT_call_menu(wmOperatorType *ot)
1640 {
1641         ot->name = "Call Menu";
1642         ot->idname = "WM_OT_call_menu";
1643
1644         ot->exec = wm_call_menu_exec;
1645         ot->poll = WM_operator_winactive;
1646
1647         ot->flag = OPTYPE_INTERNAL;
1648
1649         RNA_def_string(ot->srna, "name", "", BKE_ST_MAXNAME, "Name", "Name of the menu");
1650 }
1651
1652 /* ************ window / screen operator definitions ************** */
1653
1654 /* this poll functions is needed in place of WM_operator_winactive
1655  * while it crashes on full screen */
1656 static int wm_operator_winactive_normal(bContext *C)
1657 {
1658         wmWindow *win = CTX_wm_window(C);
1659
1660         if (win == NULL || win->screen == NULL || win->screen->full != SCREENNORMAL)
1661                 return 0;
1662
1663         return 1;
1664 }
1665
1666 static void WM_OT_window_duplicate(wmOperatorType *ot)
1667 {
1668         ot->name = "Duplicate Window";
1669         ot->idname = "WM_OT_window_duplicate";
1670         ot->description = "Duplicate the current Blender window";
1671                 
1672         ot->exec = wm_window_duplicate_exec;
1673         ot->poll = wm_operator_winactive_normal;
1674 }
1675
1676 static void WM_OT_save_homefile(wmOperatorType *ot)
1677 {
1678         ot->name = "Save User Settings";
1679         ot->idname = "WM_OT_save_homefile";
1680         ot->description = "Make the current file the default .blend file";
1681                 
1682         ot->invoke = WM_operator_confirm;
1683         ot->exec = WM_homefile_write_exec;
1684         ot->poll = WM_operator_winactive;
1685 }
1686
1687 static void WM_OT_read_homefile(wmOperatorType *ot)
1688 {
1689         ot->name = "Reload Start-Up File";
1690         ot->idname = "WM_OT_read_homefile";
1691         ot->description = "Open the default file (doesn't save the current file)";
1692         
1693         ot->invoke = WM_operator_confirm;
1694         ot->exec = WM_homefile_read_exec;
1695         /* ommit poll to run in background mode */
1696 }
1697
1698 static void WM_OT_read_factory_settings(wmOperatorType *ot)
1699 {
1700         ot->name = "Load Factory Settings";
1701         ot->idname = "WM_OT_read_factory_settings";
1702         ot->description = "Load default file and user preferences";
1703         
1704         ot->invoke = WM_operator_confirm;
1705         ot->exec = WM_homefile_read_exec;
1706         /* ommit poll to run in background mode */
1707 }
1708
1709 /* *************** open file **************** */
1710
1711 static void open_set_load_ui(wmOperator *op)
1712 {
1713         if (!RNA_struct_property_is_set(op->ptr, "load_ui"))
1714                 RNA_boolean_set(op->ptr, "load_ui", !(U.flag & USER_FILENOUI));
1715 }
1716
1717 static void open_set_use_scripts(wmOperator *op)
1718 {
1719         if (!RNA_struct_property_is_set(op->ptr, "use_scripts")) {
1720                 /* use G_SCRIPT_AUTOEXEC rather than the userpref because this means if
1721                  * the flag has been disabled from the command line, then opening
1722                  * from the menu wont enable this setting. */
1723                 RNA_boolean_set(op->ptr, "use_scripts", (G.f & G_SCRIPT_AUTOEXEC));
1724         }
1725 }
1726
1727 static int wm_open_mainfile_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1728 {
1729         const char *openname = G.main->name;
1730
1731         if (CTX_wm_window(C) == NULL) {
1732                 /* in rare cases this could happen, when trying to invoke in background
1733                  * mode on load for example. Don't use poll for this because exec()
1734                  * can still run without a window */
1735                 BKE_report(op->reports, RPT_ERROR, "Context window not set");
1736                 return OPERATOR_CANCELLED;
1737         }
1738
1739         /* if possible, get the name of the most recently used .blend file */
1740         if (G.recent_files.first) {
1741                 struct RecentFile *recent = G.recent_files.first;
1742                 openname = recent->filepath;
1743         }
1744
1745         RNA_string_set(op->ptr, "filepath", openname);
1746         open_set_load_ui(op);
1747         open_set_use_scripts(op);
1748
1749         WM_event_add_fileselect(C, op);
1750
1751         return OPERATOR_RUNNING_MODAL;
1752 }
1753
1754 static int wm_open_mainfile_exec(bContext *C, wmOperator *op)
1755 {
1756         char path[FILE_MAX];
1757
1758         RNA_string_get(op->ptr, "filepath", path);
1759         open_set_load_ui(op);
1760         open_set_use_scripts(op);
1761
1762         if (RNA_boolean_get(op->ptr, "load_ui"))
1763                 G.fileflags &= ~G_FILE_NO_UI;
1764         else
1765                 G.fileflags |= G_FILE_NO_UI;
1766                 
1767         if (RNA_boolean_get(op->ptr, "use_scripts"))
1768                 G.f |= G_SCRIPT_AUTOEXEC;
1769         else
1770                 G.f &= ~G_SCRIPT_AUTOEXEC;
1771         
1772         /* XXX wm in context is not set correctly after WM_file_read -> crash */
1773         /* do it before for now, but is this correct with multiple windows? */
1774         WM_event_add_notifier(C, NC_WINDOW, NULL);
1775
1776         WM_file_read(C, path, op->reports);
1777         
1778         return OPERATOR_FINISHED;
1779 }
1780
1781 static void WM_OT_open_mainfile(wmOperatorType *ot)
1782 {
1783         ot->name = "Open Blender File";
1784         ot->idname = "WM_OT_open_mainfile";
1785         ot->description = "Open a Blender file";
1786
1787         ot->invoke = wm_open_mainfile_invoke;
1788         ot->exec = wm_open_mainfile_exec;
1789         /* ommit window poll so this can work in background mode */
1790
1791         WM_operator_properties_filesel(ot, FOLDERFILE | BLENDERFILE, FILE_BLENDER, FILE_OPENFILE,
1792                                        WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY);
1793
1794         RNA_def_boolean(ot->srna, "load_ui", 1, "Load UI", "Load user interface setup in the .blend file");
1795         RNA_def_boolean(ot->srna, "use_scripts", 1, "Trusted Source",
1796                         "Allow .blend file to execute scripts automatically, default available from system preferences");
1797 }
1798
1799 /* **************** link/append *************** */
1800
1801 static int wm_link_append_poll(bContext *C)
1802 {
1803         if (WM_operator_winactive(C)) {
1804                 /* linking changes active object which is pretty useful in general,
1805                  * but which totally confuses edit mode (i.e. it becoming not so obvious
1806                  * to leave from edit mode and inwalid tools in toolbar might be displayed)
1807                  * so disable link/append when in edit mode (sergey) */
1808                 if (CTX_data_edit_object(C))
1809                         return 0;
1810
1811                 return 1;
1812         }
1813
1814         return 0;
1815 }
1816
1817 static int wm_link_append_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1818 {
1819         if (RNA_struct_property_is_set(op->ptr, "filepath")) {
1820                 return WM_operator_call_notest(C, op);
1821         }
1822         else {
1823                 /* XXX TODO solve where to get last linked library from */
1824                 if (G.lib[0] != '\0') {
1825                         RNA_string_set(op->ptr, "filepath", G.lib);
1826                 }
1827                 else if (G.relbase_valid) {
1828                         char path[FILE_MAX];
1829                         BLI_strncpy(path, G.main->name, sizeof(G.main->name));
1830                         BLI_parent_dir(path);
1831                         RNA_string_set(op->ptr, "filepath", path);
1832                 }
1833                 WM_event_add_fileselect(C, op);
1834                 return OPERATOR_RUNNING_MODAL;
1835         }
1836 }
1837
1838 static short wm_link_append_flag(wmOperator *op)
1839 {
1840         short flag = 0;
1841
1842         if (RNA_boolean_get(op->ptr, "autoselect")) flag |= FILE_AUTOSELECT;
1843         if (RNA_boolean_get(op->ptr, "active_layer")) flag |= FILE_ACTIVELAY;
1844         if (RNA_boolean_get(op->ptr, "relative_path")) flag |= FILE_RELPATH;
1845         if (RNA_boolean_get(op->ptr, "link")) flag |= FILE_LINK;
1846         if (RNA_boolean_get(op->ptr, "instance_groups")) flag |= FILE_GROUP_INSTANCE;
1847
1848         return flag;
1849 }
1850
1851 static int wm_link_append_exec(bContext *C, wmOperator *op)
1852 {
1853         Main *bmain = CTX_data_main(C);
1854         Scene *scene = CTX_data_scene(C);
1855         Main *mainl = NULL;
1856         BlendHandle *bh;
1857         PropertyRNA *prop;
1858         char name[FILE_MAX], dir[FILE_MAX], libname[FILE_MAX], group[GROUP_MAX];
1859         int idcode, totfiles = 0;
1860         short flag;
1861
1862         RNA_string_get(op->ptr, "filename", name);
1863         RNA_string_get(op->ptr, "directory", dir);
1864
1865         /* test if we have a valid data */
1866         if (BLO_is_a_library(dir, libname, group) == 0) {
1867                 BKE_report(op->reports, RPT_ERROR, "Not a library");
1868                 return OPERATOR_CANCELLED;
1869         }
1870         else if (group[0] == 0) {
1871                 BKE_report(op->reports, RPT_ERROR, "Nothing indicated");
1872                 return OPERATOR_CANCELLED;
1873         }
1874         else if (BLI_path_cmp(bmain->name, libname) == 0) {
1875                 BKE_report(op->reports, RPT_ERROR, "Cannot use current file as library");
1876                 return OPERATOR_CANCELLED;
1877         }
1878
1879         /* check if something is indicated for append/link */
1880         prop = RNA_struct_find_property(op->ptr, "files");
1881         if (prop) {
1882                 totfiles = RNA_property_collection_length(op->ptr, prop);
1883                 if (totfiles == 0) {
1884                         if (name[0] == '\0') {
1885                                 BKE_report(op->reports, RPT_ERROR, "Nothing indicated");
1886                                 return OPERATOR_CANCELLED;
1887                         }
1888                 }
1889         }
1890         else if (name[0] == '\0') {
1891                 BKE_report(op->reports, RPT_ERROR, "Nothing indicated");
1892                 return OPERATOR_CANCELLED;
1893         }
1894
1895         bh = BLO_blendhandle_from_file(libname, op->reports);
1896
1897         if (bh == NULL) {
1898                 /* unlikely since we just browsed it, but possible
1899                  * error reports will have been made by BLO_blendhandle_from_file() */
1900                 return OPERATOR_CANCELLED;
1901         }
1902
1903
1904         /* from here down, no error returns */
1905
1906         idcode = BKE_idcode_from_name(group);
1907
1908         /* now we have or selected, or an indicated file */
1909         if (RNA_boolean_get(op->ptr, "autoselect"))
1910                 BKE_scene_base_deselect_all(scene);
1911
1912         
1913         flag = wm_link_append_flag(op);
1914
1915         /* sanity checks for flag */
1916         if (scene->id.lib && (flag & FILE_GROUP_INSTANCE)) {
1917                 /* TODO, user never gets this message */
1918                 BKE_reportf(op->reports, RPT_WARNING, "Scene '%s' is linked, group instance disabled", scene->id.name + 2);
1919                 flag &= ~FILE_GROUP_INSTANCE;
1920         }
1921
1922
1923         /* tag everything, all untagged data can be made local
1924          * its also generally useful to know what is new
1925          *
1926          * take extra care flag_all_listbases_ids(LIB_LINK_TAG, 0) is called after! */
1927         flag_all_listbases_ids(LIB_PRE_EXISTING, 1);
1928
1929         /* here appending/linking starts */
1930         mainl = BLO_library_append_begin(bmain, &bh, libname);
1931         if (totfiles == 0) {
1932                 BLO_library_append_named_part_ex(C, mainl, &bh, name, idcode, flag);
1933         }
1934         else {
1935                 RNA_BEGIN(op->ptr, itemptr, "files")
1936                 {
1937                         RNA_string_get(&itemptr, "name", name);
1938                         BLO_library_append_named_part_ex(C, mainl, &bh, name, idcode, flag);
1939                 }
1940                 RNA_END;
1941         }
1942         BLO_library_append_end(C, mainl, &bh, idcode, flag);
1943         
1944         /* mark all library linked objects to be updated */
1945         recalc_all_library_objects(bmain);
1946         IMB_colormanagement_check_file_config(bmain);
1947
1948         /* append, rather than linking */
1949         if ((flag & FILE_LINK) == 0) {
1950                 Library *lib = BLI_findstring(&bmain->library, libname, offsetof(Library, filepath));
1951                 if (lib) BKE_library_make_local(bmain, lib, 1);
1952                 else BLI_assert(!"cant find name of just added library!");
1953         }
1954
1955         /* important we unset, otherwise these object wont
1956          * link into other scenes from this blend file */
1957         flag_all_listbases_ids(LIB_PRE_EXISTING, 0);
1958
1959         /* recreate dependency graph to include new objects */
1960         DAG_scene_sort(bmain, scene);
1961         DAG_ids_flush_update(bmain, 0);
1962
1963         BLO_blendhandle_close(bh);
1964
1965         /* XXX TODO: align G.lib with other directory storage (like last opened image etc...) */
1966         BLI_strncpy(G.lib, dir, FILE_MAX);
1967
1968         WM_event_add_notifier(C, NC_WINDOW, NULL);
1969
1970         return OPERATOR_FINISHED;
1971 }
1972
1973 static void WM_OT_link_append(wmOperatorType *ot)
1974 {
1975         PropertyRNA *prop;
1976
1977         ot->name = "Link/Append from Library";
1978         ot->idname = "WM_OT_link_append";
1979         ot->description = "Link or Append from a Library .blend file";
1980         
1981         ot->invoke = wm_link_append_invoke;
1982         ot->exec = wm_link_append_exec;
1983         ot->poll = wm_link_append_poll;
1984         
1985         ot->flag |= OPTYPE_UNDO;
1986
1987         WM_operator_properties_filesel(
1988                 ot, FOLDERFILE | BLENDERFILE, FILE_LOADLIB, FILE_OPENFILE,
1989                 WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILENAME | WM_FILESEL_RELPATH | WM_FILESEL_FILES,
1990                 FILE_DEFAULTDISPLAY);
1991         
1992         /* better not save _any_ settings for this operator */
1993         /* properties */
1994         prop = RNA_def_boolean(ot->srna, "link", 1, "Link", "Link the objects or datablocks rather than appending");
1995         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1996         prop = RNA_def_boolean(ot->srna, "autoselect", 1, "Select", "Select the linked objects");
1997         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
1998         prop = RNA_def_boolean(ot->srna, "active_layer", 1, "Active Layer", "Put the linked objects on the active layer");
1999         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2000         prop = RNA_def_boolean(ot->srna, "instance_groups", 1, "Instance Groups", "Create instances for each group as a DupliGroup");
2001         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2002 }
2003
2004 /* *************** recover last session **************** */
2005
2006 static int wm_recover_last_session_exec(bContext *C, wmOperator *op)
2007 {
2008         char filename[FILE_MAX];
2009
2010         G.fileflags |= G_FILE_RECOVER;
2011
2012         /* XXX wm in context is not set correctly after WM_file_read -> crash */
2013         /* do it before for now, but is this correct with multiple windows? */
2014         WM_event_add_notifier(C, NC_WINDOW, NULL);
2015
2016         /* load file */
2017         BLI_make_file_string("/", filename, BLI_temporary_dir(), "quit.blend");
2018         WM_file_read(C, filename, op->reports);
2019
2020         G.fileflags &= ~G_FILE_RECOVER;
2021         return OPERATOR_FINISHED;
2022 }
2023
2024 static void WM_OT_recover_last_session(wmOperatorType *ot)
2025 {
2026         ot->name = "Recover Last Session";
2027         ot->idname = "WM_OT_recover_last_session";
2028         ot->description = "Open the last closed file (\"quit.blend\")";
2029         
2030         ot->exec = wm_recover_last_session_exec;
2031         ot->poll = WM_operator_winactive;
2032 }
2033
2034 /* *************** recover auto save **************** */
2035
2036 static int wm_recover_auto_save_exec(bContext *C, wmOperator *op)
2037 {
2038         char path[FILE_MAX];
2039
2040         RNA_string_get(op->ptr, "filepath", path);
2041
2042         G.fileflags |= G_FILE_RECOVER;
2043
2044         /* XXX wm in context is not set correctly after WM_file_read -> crash */
2045         /* do it before for now, but is this correct with multiple windows? */
2046         WM_event_add_notifier(C, NC_WINDOW, NULL);
2047
2048         /* load file */
2049         WM_file_read(C, path, op->reports);
2050
2051         G.fileflags &= ~G_FILE_RECOVER;
2052
2053         return OPERATOR_FINISHED;
2054 }
2055
2056 static int wm_recover_auto_save_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
2057 {
2058         char filename[FILE_MAX];
2059
2060         wm_autosave_location(filename);
2061         RNA_string_set(op->ptr, "filepath", filename);
2062         WM_event_add_fileselect(C, op);
2063
2064         return OPERATOR_RUNNING_MODAL;
2065 }
2066
2067 static void WM_OT_recover_auto_save(wmOperatorType *ot)
2068 {
2069         ot->name = "Recover Auto Save";
2070         ot->idname = "WM_OT_recover_auto_save";
2071         ot->description = "Open an automatically saved file to recover it";
2072         
2073         ot->exec = wm_recover_auto_save_exec;
2074         ot->invoke = wm_recover_auto_save_invoke;
2075         ot->poll = WM_operator_winactive;
2076
2077         WM_operator_properties_filesel(ot, BLENDERFILE, FILE_BLENDER, FILE_OPENFILE,
2078                                        WM_FILESEL_FILEPATH, FILE_LONGDISPLAY);
2079 }
2080
2081 /* *************** save file as **************** */
2082
2083 static void untitled(char *filepath)
2084 {
2085         if (G.save_over == 0 && strlen(filepath) < FILE_MAX - 16) {
2086                 char *c = BLI_last_slash(filepath);
2087                 
2088                 if (c)
2089                         strcpy(&c[1], "untitled.blend");
2090                 else
2091                         strcpy(filepath, "untitled.blend");
2092         }
2093 }
2094
2095 static void save_set_compress(wmOperator *op)
2096 {
2097         if (!RNA_struct_property_is_set(op->ptr, "compress")) {
2098                 if (G.save_over) /* keep flag for existing file */
2099                         RNA_boolean_set(op->ptr, "compress", G.fileflags & G_FILE_COMPRESS);
2100                 else /* use userdef for new file */
2101                         RNA_boolean_set(op->ptr, "compress", U.flag & USER_FILECOMPRESS);
2102         }
2103 }
2104
2105 static int wm_save_as_mainfile_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
2106 {
2107         char name[FILE_MAX];
2108
2109         save_set_compress(op);
2110         
2111         /* if not saved before, get the name of the most recently used .blend file */
2112         if (G.main->name[0] == 0 && G.recent_files.first) {
2113                 struct RecentFile *recent = G.recent_files.first;
2114                 BLI_strncpy(name, recent->filepath, FILE_MAX);
2115         }
2116         else
2117                 BLI_strncpy(name, G.main->name, FILE_MAX);
2118         
2119         untitled(name);
2120         RNA_string_set(op->ptr, "filepath", name);
2121         
2122         WM_event_add_fileselect(C, op);
2123
2124         return OPERATOR_RUNNING_MODAL;
2125 }
2126
2127 /* function used for WM_OT_save_mainfile too */
2128 static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op)
2129 {
2130         char path[FILE_MAX];
2131         int fileflags;
2132
2133         save_set_compress(op);
2134         
2135         if (RNA_struct_property_is_set(op->ptr, "filepath"))
2136                 RNA_string_get(op->ptr, "filepath", path);
2137         else {
2138                 BLI_strncpy(path, G.main->name, FILE_MAX);
2139                 untitled(path);
2140         }
2141         
2142         fileflags = G.fileflags;
2143
2144         /* set compression flag */
2145         BKE_BIT_TEST_SET(fileflags, RNA_boolean_get(op->ptr, "compress"),
2146                          G_FILE_COMPRESS);
2147         BKE_BIT_TEST_SET(fileflags, RNA_boolean_get(op->ptr, "relative_remap"),
2148                          G_FILE_RELATIVE_REMAP);
2149         BKE_BIT_TEST_SET(fileflags,
2150                          (RNA_struct_property_is_set(op->ptr, "copy") &&
2151                           RNA_boolean_get(op->ptr, "copy")),
2152                          G_FILE_SAVE_COPY);
2153
2154 #ifdef USE_BMESH_SAVE_AS_COMPAT
2155         BKE_BIT_TEST_SET(fileflags,
2156                          (RNA_struct_find_property(op->ptr, "use_mesh_compat") &&
2157                           RNA_boolean_get(op->ptr, "use_mesh_compat")),
2158                          G_FILE_MESH_COMPAT);
2159 #endif
2160
2161         if (WM_file_write(C, path, fileflags, op->reports) != 0)
2162                 return OPERATOR_CANCELLED;
2163
2164         WM_event_add_notifier(C, NC_WM | ND_FILESAVE, NULL);
2165
2166         return OPERATOR_FINISHED;
2167 }
2168
2169 /* function used for WM_OT_save_mainfile too */
2170 static int blend_save_check(bContext *UNUSED(C), wmOperator *op)
2171 {
2172         char filepath[FILE_MAX];
2173         RNA_string_get(op->ptr, "filepath", filepath);
2174         if (!BLO_has_bfile_extension(filepath)) {
2175                 /* some users would prefer BLI_replace_extension(),
2176                  * we keep getting knit-picking bug reports about this - campbell */
2177                 BLI_ensure_extension(filepath, FILE_MAX, ".blend");
2178                 RNA_string_set(op->ptr, "filepath", filepath);
2179                 return TRUE;
2180         }
2181         return FALSE;
2182 }
2183
2184 static void WM_OT_save_as_mainfile(wmOperatorType *ot)
2185 {
2186         ot->name = "Save As Blender File";
2187         ot->idname = "WM_OT_save_as_mainfile";
2188         ot->description = "Save the current file in the desired location";
2189         
2190         ot->invoke = wm_save_as_mainfile_invoke;
2191         ot->exec = wm_save_as_mainfile_exec;
2192         ot->check = blend_save_check;
2193         /* ommit window poll so this can work in background mode */
2194
2195         WM_operator_properties_filesel(ot, FOLDERFILE | BLENDERFILE, FILE_BLENDER, FILE_SAVE,
2196                                        WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY);
2197         RNA_def_boolean(ot->srna, "compress", 0, "Compress", "Write compressed .blend file");
2198         RNA_def_boolean(ot->srna, "relative_remap", 1, "Remap Relative",
2199                         "Remap relative paths when saving in a different directory");
2200         RNA_def_boolean(ot->srna, "copy", 0, "Save Copy",
2201                         "Save a copy of the actual working state but does not make saved file active");
2202 #ifdef USE_BMESH_SAVE_AS_COMPAT
2203         RNA_def_boolean(ot->srna, "use_mesh_compat", 0, "Legacy Mesh Format",
2204                         "Save using legacy mesh format (no ngons) - WARNING: only saves tris and quads, other ngons will "
2205                         "be lost (no implicit triangulation)");
2206 #endif
2207 }
2208
2209 /* *************** save file directly ******** */
2210
2211 static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
2212 {
2213         char name[FILE_MAX];
2214         int ret;
2215         
2216         /* cancel if no active window */
2217         if (CTX_wm_window(C) == NULL)
2218                 return OPERATOR_CANCELLED;
2219
2220         save_set_compress(op);
2221
2222         /* if not saved before, get the name of the most recently used .blend file */
2223         if (G.main->name[0] == 0 && G.recent_files.first) {
2224                 struct RecentFile *recent = G.recent_files.first;
2225                 BLI_strncpy(name, recent->filepath, FILE_MAX);
2226         }
2227         else
2228                 BLI_strncpy(name, G.main->name, FILE_MAX);
2229
2230         untitled(name);
2231         
2232         RNA_string_set(op->ptr, "filepath", name);
2233
2234         if (G.save_over) {
2235                 if (BLI_exists(name)) {
2236                         uiPupMenuSaveOver(C, op, name);
2237                         ret = OPERATOR_RUNNING_MODAL;
2238                 }
2239                 else {
2240                         ret = wm_save_as_mainfile_exec(C, op);
2241                 }
2242         }
2243         else {
2244                 WM_event_add_fileselect(C, op);
2245                 ret = OPERATOR_RUNNING_MODAL;
2246         }
2247         
2248         return ret;
2249 }
2250
2251 static void WM_OT_save_mainfile(wmOperatorType *ot)
2252 {
2253         ot->name = "Save Blender File";
2254         ot->idname = "WM_OT_save_mainfile";
2255         ot->description = "Save the current Blender file";
2256         
2257         ot->invoke = wm_save_mainfile_invoke;
2258         ot->exec = wm_save_as_mainfile_exec;
2259         ot->check = blend_save_check;
2260         /* ommit window poll so this can work in background mode */
2261         
2262         WM_operator_properties_filesel(ot, FOLDERFILE | BLENDERFILE, FILE_BLENDER, FILE_SAVE,
2263                                        WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY);
2264         RNA_def_boolean(ot->srna, "compress", 0, "Compress", "Write compressed .blend file");
2265         RNA_def_boolean(ot->srna, "relative_remap", 0, "Remap Relative", "Remap relative paths when saving in a different directory");
2266 }
2267
2268 static void WM_OT_window_fullscreen_toggle(wmOperatorType *ot)
2269 {
2270         ot->name = "Toggle Fullscreen";
2271         ot->idname = "WM_OT_window_fullscreen_toggle";
2272         ot->description = "Toggle the current window fullscreen";
2273
2274         ot->exec = wm_window_fullscreen_toggle_exec;
2275         ot->poll = WM_operator_winactive;
2276 }
2277
2278 static int wm_exit_blender_op(bContext *C, wmOperator *op)
2279 {
2280         WM_operator_free(op);
2281         
2282         WM_exit(C);
2283         
2284         return OPERATOR_FINISHED;
2285 }
2286
2287 static void WM_OT_quit_blender(wmOperatorType *ot)
2288 {
2289         ot->name = "Quit Blender";
2290         ot->idname = "WM_OT_quit_blender";
2291         ot->description = "Quit Blender";
2292
2293         ot->invoke = WM_operator_confirm;
2294         ot->exec = wm_exit_blender_op;
2295         ot->poll = WM_operator_winactive;
2296 }
2297
2298 /* *********************** */
2299
2300 #if defined(WIN32)
2301
2302 static int wm_console_toggle_op(bContext *UNUSED(C), wmOperator *UNUSED(op))
2303 {
2304         GHOST_toggleConsole(2);
2305         return OPERATOR_FINISHED;
2306 }
2307
2308 static void WM_OT_console_toggle(wmOperatorType *ot)
2309 {
2310         /* XXX Have to mark these for xgettext, as under linux they do not exists...
2311          *     And even worth, have to give the context as text, as xgettext doesn't expand macros. :( */
2312         ot->name = CTX_N_("Operator" /* BLF_I18NCONTEXT_OPERATOR_DEFAULT */, "Toggle System Console");
2313         ot->idname = "WM_OT_console_toggle";
2314         ot->description = N_("Toggle System Console");
2315         
2316         ot->exec = wm_console_toggle_op;
2317         ot->poll = WM_operator_winactive;
2318 }
2319
2320 #endif
2321
2322 /* ************ default paint cursors, draw always around cursor *********** */
2323 /*
2324  * - returns handler to free
2325  * - poll(bContext): returns 1 if draw should happen
2326  * - draw(bContext): drawing callback for paint cursor
2327  */
2328
2329 void *WM_paint_cursor_activate(wmWindowManager *wm, int (*poll)(bContext *C),
2330                                wmPaintCursorDraw draw, void *customdata)
2331 {
2332         wmPaintCursor *pc = MEM_callocN(sizeof(wmPaintCursor), "paint cursor");
2333         
2334         BLI_addtail(&wm->paintcursors, pc);
2335         
2336         pc->customdata = customdata;
2337         pc->poll = poll;
2338         pc->draw = draw;
2339         
2340         return pc;
2341 }
2342
2343 void WM_paint_cursor_end(wmWindowManager *wm, void *handle)
2344 {
2345         wmPaintCursor *pc;
2346         
2347         for (pc = wm->paintcursors.first; pc; pc = pc->next) {
2348                 if (pc == (wmPaintCursor *)handle) {
2349                         BLI_remlink(&wm->paintcursors, pc);
2350                         MEM_freeN(pc);
2351                         return;
2352                 }
2353         }
2354 }
2355
2356 /* ************ window gesture operator-callback definitions ************** */
2357 /*
2358  * These are default callbacks for use in operators requiring gesture input
2359  */
2360
2361 /* **************** Border gesture *************** */
2362
2363 /* Border gesture has two types:
2364  * 1) WM_GESTURE_CROSS_RECT: starts a cross, on mouse click it changes to border
2365  * 2) WM_GESTURE_RECT: starts immediate as a border, on mouse click or release it ends
2366  *
2367  * It stores 4 values (xmin, xmax, ymin, ymax) and event it ended with (event_type)
2368  */
2369
2370 static int border_apply_rect(wmOperator *op)
2371 {
2372         wmGesture *gesture = op->customdata;
2373         rcti *rect = gesture->customdata;
2374         
2375         if (rect->xmin == rect->xmax || rect->ymin == rect->ymax)
2376                 return 0;
2377
2378         
2379         /* operator arguments and storage. */
2380         RNA_int_set(op->ptr, "xmin", min_ii(rect->xmin, rect->xmax));
2381         RNA_int_set(op->ptr, "ymin", min_ii(rect->ymin, rect->ymax));
2382         RNA_int_set(op->ptr, "xmax", max_ii(rect->xmin, rect->xmax));
2383         RNA_int_set(op->ptr, "ymax", max_ii(rect->ymin, rect->ymax));
2384
2385         return 1;
2386 }
2387
2388 static int border_apply(bContext *C, wmOperator *op, int gesture_mode)
2389 {
2390         int retval;
2391
2392         if (!border_apply_rect(op))
2393                 return 0;
2394         
2395         /* XXX weak; border should be configured for this without reading event types */
2396         if (RNA_struct_find_property(op->ptr, "gesture_mode") )
2397                 RNA_int_set(op->ptr, "gesture_mode", gesture_mode);
2398
2399         retval = op->type->exec(C, op);
2400         OPERATOR_RETVAL_CHECK(retval);
2401
2402         return 1;
2403 }
2404
2405 static void wm_gesture_end(bContext *C, wmOperator *op)
2406 {
2407         wmGesture *gesture = op->customdata;
2408         
2409         WM_gesture_end(C, gesture); /* frees gesture itself, and unregisters from window */
2410         op->customdata = NULL;
2411
2412         ED_area_tag_redraw(CTX_wm_area(C));
2413         
2414         if (RNA_struct_find_property(op->ptr, "cursor") )
2415                 WM_cursor_restore(CTX_wm_window(C));
2416 }
2417
2418 int WM_border_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
2419 {
2420         if (ISTWEAK(event->type))
2421                 op->customdata = WM_gesture_new(C, event, WM_GESTURE_RECT);
2422         else
2423                 op->customdata = WM_gesture_new(C, event, WM_GESTURE_CROSS_RECT);
2424
2425         /* add modal handler */
2426         WM_event_add_modal_handler(C, op);
2427         
2428         wm_gesture_tag_redraw(C);
2429
2430         return OPERATOR_RUNNING_MODAL;
2431 }
2432
2433 int WM_border_select_modal(bContext *C, wmOperator *op, wmEvent *event)
2434 {
2435         wmGesture *gesture = op->customdata;
2436         rcti *rect = gesture->customdata;
2437         int sx, sy;
2438         
2439         if (event->type == MOUSEMOVE) {
2440                 wm_subwindow_getorigin(CTX_wm_window(C), gesture->swinid, &sx, &sy);
2441
2442                 if (gesture->type == WM_GESTURE_CROSS_RECT && gesture->mode == 0) {
2443                         rect->xmin = rect->xmax = event->x - sx;
2444                         rect->ymin = rect->ymax = event->y - sy;
2445                 }
2446                 else {
2447                         rect->xmax = event->x - sx;
2448                         rect->ymax = event->y - sy;
2449                 }
2450                 border_apply_rect(op);
2451
2452                 wm_gesture_tag_redraw(C);
2453         }
2454         else if (event->type == EVT_MODAL_MAP) {
2455                 switch (event->val) {
2456                         case GESTURE_MODAL_BEGIN:
2457                                 if (gesture->type == WM_GESTURE_CROSS_RECT && gesture->mode == 0) {
2458                                         gesture->mode = 1;
2459                                         wm_gesture_tag_redraw(C);
2460                                 }
2461                                 break;
2462                         case GESTURE_MODAL_SELECT:
2463                         case GESTURE_MODAL_DESELECT:
2464                         case GESTURE_MODAL_IN:
2465                         case GESTURE_MODAL_OUT:
2466                                 if (border_apply(C, op, event->val)) {
2467                                         wm_gesture_end(C, op);
2468                                         return OPERATOR_FINISHED;
2469                                 }
2470                                 wm_gesture_end(C, op);
2471                                 return OPERATOR_CANCELLED;
2472                                 break;
2473
2474                         case GESTURE_MODAL_CANCEL:
2475                                 wm_gesture_end(C, op);
2476                                 return OPERATOR_CANCELLED;
2477                 }
2478
2479         }
2480 //      /* Allow view navigation??? */
2481 //      else {
2482 //              return OPERATOR_PASS_THROUGH;
2483 //      }
2484
2485         return OPERATOR_RUNNING_MODAL;
2486 }
2487
2488 int WM_border_select_cancel(bContext *C, wmOperator *op)
2489 {
2490         wm_gesture_end(C, op);
2491
2492         return OPERATOR_CANCELLED;
2493 }
2494
2495 /* **************** circle gesture *************** */
2496 /* works now only for selection or modal paint stuff, calls exec while hold mouse, exit on release */
2497
2498 #ifdef GESTURE_MEMORY
2499 int circle_select_size = 25; /* XXX - need some operator memory thing! */
2500 #endif
2501
2502 int WM_gesture_circle_invoke(bContext *C, wmOperator *op, wmEvent *event)
2503 {
2504         op->customdata = WM_gesture_new(C, event, WM_GESTURE_CIRCLE);
2505         
2506         /* add modal handler */
2507         WM_event_add_modal_handler(C, op);
2508         
2509         wm_gesture_tag_redraw(C);
2510         
2511         return OPERATOR_RUNNING_MODAL;
2512 }
2513
2514 static void gesture_circle_apply(bContext *C, wmOperator *op)
2515 {
2516         wmGesture *gesture = op->customdata;
2517         rcti *rect = gesture->customdata;
2518         
2519         if (RNA_int_get(op->ptr, "gesture_mode") == GESTURE_MODAL_NOP)
2520                 return;
2521
2522         /* operator arguments and storage. */
2523         RNA_int_set(op->ptr, "x", rect->xmin);
2524         RNA_int_set(op->ptr, "y", rect->ymin);
2525         RNA_int_set(op->ptr, "radius", rect->xmax);
2526         
2527         if (op->type->exec) {
2528                 int retval;
2529                 retval = op->type->exec(C, op);
2530                 OPERATOR_RETVAL_CHECK(retval);
2531         }
2532 #ifdef GESTURE_MEMORY
2533         circle_select_size = rect->xmax;
2534 #endif
2535 }
2536
2537 int WM_gesture_circle_modal(bContext *C, wmOperator *op, wmEvent *event)
2538 {
2539         wmGesture *gesture = op->customdata;
2540         rcti *rect = gesture->customdata;
2541         int sx, sy;
2542
2543         if (event->type == MOUSEMOVE) {
2544                 wm_subwindow_getorigin(CTX_wm_window(C), gesture->swinid, &sx, &sy);
2545
2546                 rect->xmin = event->x - sx;
2547                 rect->ymin = event->y - sy;
2548
2549                 wm_gesture_tag_redraw(C);
2550
2551                 if (gesture->mode)
2552                         gesture_circle_apply(C, op);
2553         }
2554         else if (event->type == EVT_MODAL_MAP) {
2555                 switch (event->val) {
2556                         case GESTURE_MODAL_CIRCLE_ADD:
2557                                 rect->xmax += 2 + rect->xmax / 10;
2558                                 wm_gesture_tag_redraw(C);
2559                                 break;
2560                         case GESTURE_MODAL_CIRCLE_SUB:
2561                                 rect->xmax -= 2 + rect->xmax / 10;
2562                                 if (rect->xmax < 1) rect->xmax = 1;
2563                                 wm_gesture_tag_redraw(C);
2564                                 break;
2565                         case GESTURE_MODAL_SELECT:
2566                         case GESTURE_MODAL_DESELECT:
2567                         case GESTURE_MODAL_NOP:
2568                                 if (RNA_struct_find_property(op->ptr, "gesture_mode"))
2569                                         RNA_int_set(op->ptr, "gesture_mode", event->val);
2570
2571                                 if (event->val != GESTURE_MODAL_NOP) {
2572                                         /* apply first click */
2573                                         gesture_circle_apply(C, op);
2574                                         gesture->mode = 1;
2575                                         wm_gesture_tag_redraw(C);
2576                                 }
2577                                 break;
2578
2579                         case GESTURE_MODAL_CANCEL:
2580                         case GESTURE_MODAL_CONFIRM:
2581                                 wm_gesture_end(C, op);
2582                                 return OPERATOR_FINISHED; /* use finish or we don't get an undo */
2583                 }
2584         }
2585 //      /* Allow view navigation??? */
2586 //      else {
2587 //              return OPERATOR_PASS_THROUGH;
2588 //      }
2589
2590         return OPERATOR_RUNNING_MODAL;
2591 }
2592
2593 int WM_gesture_circle_cancel(bContext *C, wmOperator *op)
2594 {
2595         wm_gesture_end(C, op);
2596
2597         return OPERATOR_CANCELLED;
2598 }
2599
2600 #if 0
2601 /* template to copy from */
2602 void WM_OT_circle_gesture(wmOperatorType *ot)
2603 {
2604         ot->name = "Circle Gesture";
2605         ot->idname = "WM_OT_circle_gesture";
2606         ot->description = "Enter rotate mode with a circular gesture";
2607         
2608         ot->invoke = WM_gesture_circle_invoke;
2609         ot->modal = WM_gesture_circle_modal;
2610         
2611         ot->poll = WM_operator_winactive;
2612         
2613         RNA_def_property(ot->srna, "x", PROP_INT, PROP_NONE);
2614         RNA_def_property(ot->srna, "y", PROP_INT, PROP_NONE);
2615         RNA_def_property(ot->srna, "radius", PROP_INT, PROP_NONE);
2616
2617 }
2618 #endif
2619
2620 /* **************** Tweak gesture *************** */
2621
2622 static void tweak_gesture_modal(bContext *C, wmEvent *event)
2623 {
2624         wmWindow *window = CTX_wm_window(C);
2625         wmGesture *gesture = window->tweak;
2626         rcti *rect = gesture->customdata;
2627         int sx, sy, val;
2628         
2629         switch (event->type) {
2630                 case MOUSEMOVE:
2631                 case INBETWEEN_MOUSEMOVE:
2632                         
2633                         wm_subwindow_getorigin(window, gesture->swinid, &sx, &sy);
2634                         
2635                         rect->xmax = event->x - sx;
2636                         rect->ymax = event->y - sy;
2637                         
2638                         if ((val = wm_gesture_evaluate(gesture))) {
2639                                 wmEvent tevent;
2640
2641                                 tevent = *(window->eventstate);
2642                                 if (gesture->event_type == LEFTMOUSE)
2643                                         tevent.type = EVT_TWEAK_L;
2644                                 else if (gesture->event_type == RIGHTMOUSE)
2645                                         tevent.type = EVT_TWEAK_R;
2646                                 else
2647                                         tevent.type = EVT_TWEAK_M;
2648                                 tevent.val = val;
2649                                 /* mouse coords! */
2650                                 wm_event_add(window, &tevent);
2651                                 
2652                                 WM_gesture_end(C, gesture); /* frees gesture itself, and unregisters from window */
2653                         }
2654                         
2655                         break;
2656                         
2657                 case LEFTMOUSE:
2658                 case RIGHTMOUSE:
2659                 case MIDDLEMOUSE:
2660                         if (gesture->event_type == event->type) {
2661                                 WM_gesture_end(C, gesture);
2662
2663                                 /* when tweak fails we should give the other keymap entries a chance */
2664                                 event->val = KM_RELEASE;
2665                         }
2666                         break;
2667                 default:
2668                         if (!ISTIMER(event->type)) {
2669                                 WM_gesture_end(C, gesture);
2670                         }
2671                         break;
2672         }
2673 }
2674
2675 /* standard tweak, called after window handlers passed on event */
2676 void wm_tweakevent_test(bContext *C, wmEvent *event, int action)
2677 {
2678         wmWindow *win = CTX_wm_window(C);
2679         
2680         if (win->tweak == NULL) {
2681                 if (CTX_wm_region(C)) {
2682                         if (event->val == KM_PRESS) {
2683                                 if (ELEM3(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE) )
2684                                         win->tweak = WM_gesture_new(C, event, WM_GESTURE_TWEAK);
2685                         }
2686                 }
2687         }
2688         else {
2689                 /* no tweaks if event was handled */
2690                 if ((action & WM_HANDLER_BREAK)) {
2691                         WM_gesture_end(C, win->tweak);
2692                 }
2693                 else
2694                         tweak_gesture_modal(C, event);
2695         }
2696 }
2697
2698 /* *********************** lasso gesture ****************** */
2699
2700 int WM_gesture_lasso_invoke(bContext *C, wmOperator *op, wmEvent *event)
2701 {
2702         op->customdata = WM_gesture_new(C, event, WM_GESTURE_LASSO);
2703         
2704         /* add modal handler */
2705         WM_event_add_modal_handler(C, op);
2706         
2707         wm_gesture_tag_redraw(C);
2708         
2709         if (RNA_struct_find_property(op->ptr, "cursor") )
2710                 WM_cursor_modal(CTX_wm_window(C), RNA_int_get(op->ptr, "cursor"));
2711         
2712         return OPERATOR_RUNNING_MODAL;
2713 }
2714
2715 int WM_gesture_lines_invoke(bContext *C, wmOperator *op, wmEvent *event)
2716 {
2717         op->customdata = WM_gesture_new(C, event, WM_GESTURE_LINES);
2718         
2719         /* add modal handler */
2720         WM_event_add_modal_handler(C, op);
2721         
2722         wm_gesture_tag_redraw(C);
2723         
2724         if (RNA_struct_find_property(op->ptr, "cursor") )
2725                 WM_cursor_modal(CTX_wm_window(C), RNA_int_get(op->ptr, "cursor"));
2726         
2727         return OPERATOR_RUNNING_MODAL;
2728 }
2729
2730
2731 static void gesture_lasso_apply(bContext *C, wmOperator *op)
2732 {
2733         wmGesture *gesture = op->customdata;
2734         PointerRNA itemptr;
2735         float loc[2];
2736         int i;
2737         short *lasso = gesture->customdata;
2738         
2739         /* operator storage as path. */
2740
2741         RNA_collection_clear(op->ptr, "path");
2742         for (i = 0; i < gesture->points; i++, lasso += 2) {
2743                 loc[0] = lasso[0];
2744                 loc[1] = lasso[1];
2745                 RNA_collection_add(op->ptr, "path", &itemptr);
2746                 RNA_float_set_array(&itemptr, "loc", loc);
2747         }
2748         
2749         wm_gesture_end(C, op);
2750                 
2751         if (op->type->exec) {
2752                 int retval = op->type->exec(C, op);
2753                 OPERATOR_RETVAL_CHECK(retval);
2754         }
2755 }
2756
2757 int WM_gesture_lasso_modal(bContext *C, wmOperator *op, wmEvent *event)
2758 {
2759         wmGesture *gesture = op->customdata;
2760         int sx, sy;
2761         
2762         switch (event->type) {
2763                 case MOUSEMOVE:
2764                 case INBETWEEN_MOUSEMOVE:
2765                         
2766                         wm_gesture_tag_redraw(C);
2767                         
2768                         wm_subwindow_getorigin(CTX_wm_window(C), gesture->swinid, &sx, &sy);
2769
2770                         if (gesture->points == gesture->size) {
2771                                 short *old_lasso = gesture->customdata;
2772                                 gesture->customdata = MEM_callocN(2 * sizeof(short) * (gesture->size + WM_LASSO_MIN_POINTS), "lasso points");
2773                                 memcpy(gesture->customdata, old_lasso, 2 * sizeof(short) * gesture->size);
2774                                 gesture->size = gesture->size + WM_LASSO_MIN_POINTS;
2775                                 MEM_freeN(old_lasso);
2776                                 // printf("realloc\n");
2777                         }
2778
2779                         {
2780                                 int x, y;
2781                                 short *lasso = gesture->customdata;
2782                                 
2783                                 lasso += (2 * gesture->points - 2);
2784                                 x = (event->x - sx - lasso[0]);
2785                                 y = (event->y - sy - lasso[1]);
2786                                 
2787                                 /* make a simple distance check to get a smoother lasso
2788                                  * add only when at least 2 pixels between this and previous location */
2789                                 if ((x * x + y * y) > 4) {
2790                                         lasso += 2;
2791                                         lasso[0] = event->x - sx;
2792                                         lasso[1] = event->y - sy;
2793                                         gesture->points++;
2794                                 }
2795                         }
2796                         break;
2797                         
2798                 case LEFTMOUSE:
2799                 case MIDDLEMOUSE:
2800                 case RIGHTMOUSE:
2801                         if (event->val == KM_RELEASE) {   /* key release */
2802                                 gesture_lasso_apply(C, op);
2803                                 return OPERATOR_FINISHED;
2804                         }
2805                         break;
2806                 case ESCKEY:
2807                         wm_gesture_end(C, op);
2808                         return OPERATOR_CANCELLED;
2809         }
2810         return OPERATOR_RUNNING_MODAL;
2811 }
2812
2813 int WM_gesture_lines_modal(bContext *C, wmOperator *op, wmEvent *event)
2814 {
2815         return WM_gesture_lasso_modal(C, op, event);
2816 }
2817
2818 int WM_gesture_lasso_cancel(bContext *C, wmOperator *op)
2819 {
2820         wm_gesture_end(C, op);
2821
2822         return OPERATOR_CANCELLED;
2823 }
2824
2825 int WM_gesture_lines_cancel(bContext *C, wmOperator *op)
2826 {
2827         wm_gesture_end(C, op);
2828
2829         return OPERATOR_CANCELLED;
2830 }
2831
2832 /**
2833  * helper function, we may want to add options for conversion to view space
2834  *
2835  * caller must free.
2836  */
2837 const int (*WM_gesture_lasso_path_to_array(bContext *UNUSED(C), wmOperator *op, int *mcords_tot))[2]
2838 {
2839         PropertyRNA *prop = RNA_struct_find_property(op->ptr, "path");
2840         int (*mcords)[2] = NULL;
2841         BLI_assert(prop != NULL);
2842
2843         if (prop) {
2844                 const int len = RNA_property_collection_length(op->ptr, prop);
2845
2846                 if (len) {
2847                         int i = 0;
2848                         mcords = MEM_mallocN(sizeof(int) * 2 * len, __func__);
2849
2850                         RNA_PROP_BEGIN(op->ptr, itemptr, prop)
2851                         {
2852                                 float loc[2];
2853
2854                                 RNA_float_get_array(&itemptr, "loc", loc);
2855                                 mcords[i][0] = (int)loc[0];
2856                                 mcords[i][1] = (int)loc[1];
2857                                 i++;
2858                         }
2859                         RNA_PROP_END;
2860                 }
2861                 *mcords_tot = len;
2862         }
2863         else {
2864                 *mcords_tot = 0;
2865         }
2866
2867         /* cast for 'const' */
2868         return (const int (*)[2])mcords;
2869 }
2870
2871 #if 0
2872 /* template to copy from */
2873
2874 static int gesture_lasso_exec(bContext *C, wmOperator *op)
2875 {
2876         RNA_BEGIN(op->ptr, itemptr, "path")
2877         {
2878                 float loc[2];
2879                 
2880                 RNA_float_get_array(&itemptr, "loc", loc);
2881                 printf("Location: %f %f\n", loc[0], loc[1]);
2882         }
2883         RNA_END;
2884         
2885         return OPERATOR_FINISHED;
2886 }
2887
2888 void WM_OT_lasso_gesture(wmOperatorType *ot)
2889 {
2890         PropertyRNA *prop;
2891         
2892         ot->name = "Lasso Gesture";
2893         ot->idname = "WM_OT_lasso_gesture";
2894         ot->description = "Select objects within the lasso as you move the pointer";
2895         
2896         ot->invoke = WM_gesture_lasso_invoke;
2897         ot->modal = WM_gesture_lasso_modal;
2898         ot->exec = gesture_lasso_exec;
2899         
2900         ot->poll = WM_operator_winactive;
2901         
2902         prop = RNA_def_property(ot->srna, "path", PROP_COLLECTION, PROP_NONE);
2903         RNA_def_property_struct_runtime(prop, &RNA_OperatorMousePath);
2904 }
2905 #endif
2906
2907 /* *********************** straight line gesture ****************** */
2908
2909 static int straightline_apply(bContext *C, wmOperator *op)
2910 {
2911         wmGesture *gesture = op->customdata;
2912         rcti *rect = gesture->customdata;
2913         
2914         if (rect->xmin == rect->xmax && rect->ymin == rect->ymax)
2915                 return 0;
2916         
2917         /* operator arguments and storage. */
2918         RNA_int_set(op->ptr, "xstart", rect->xmin);
2919         RNA_int_set(op->ptr, "ystart", rect->ymin);
2920         RNA_int_set(op->ptr, "xend", rect->xmax);
2921         RNA_int_set(op->ptr, "yend", rect->ymax);
2922
2923         if (op->type->exec) {
2924                 int retval = op->type->exec(C, op);
2925                 OPERATOR_RETVAL_CHECK(retval);
2926         }
2927         
2928         return 1;
2929 }
2930
2931
2932 int WM_gesture_straightline_invoke(bContext *C, wmOperator *op, wmEvent *event)
2933 {
2934         op->customdata = WM_gesture_new(C, event, WM_GESTURE_STRAIGHTLINE);
2935         
2936         /* add modal handler */
2937         WM_event_add_modal_handler(C, op);
2938         
2939         wm_gesture_tag_redraw(C);
2940         
2941         if (RNA_struct_find_property(op->ptr, "cursor") )
2942                 WM_cursor_modal(CTX_wm_window(C), RNA_int_get(op->ptr, "cursor"));
2943                 
2944         return OPERATOR_RUNNING_MODAL;
2945 }
2946
2947 int WM_gesture_straightline_modal(bContext *C, wmOperator *op, wmEvent *event)
2948 {
2949         wmGesture *gesture = op->customdata;
2950         rcti *rect = gesture->customdata;
2951         int sx, sy;
2952         
2953         if (event->type == MOUSEMOVE) {
2954                 wm_subwindow_getorigin(CTX_wm_window(C), gesture->swinid, &sx, &sy);
2955                 
2956                 if (gesture->mode == 0) {
2957                         rect->xmin = rect->xmax = event->x - sx;
2958                         rect->ymin = rect->ymax = event->y - sy;
2959                 }
2960                 else {
2961                         rect->xmax = event->x - sx;
2962                         rect->ymax = event->y - sy;
2963                         straightline_apply(C, op);
2964                 }
2965                 
2966                 wm_gesture_tag_redraw(C);
2967         }
2968         else if (event->type == EVT_MODAL_MAP) {
2969                 switch (event->val) {
2970                         case GESTURE_MODAL_BEGIN:
2971                                 if (gesture->mode == 0) {
2972                                         gesture->mode = 1;
2973                                         wm_gesture_tag_redraw(C);
2974                                 }
2975                                 break;
2976                         case GESTURE_MODAL_SELECT:
2977                                 if (straightline_apply(C, op)) {
2978                                         wm_gesture_end(C, op);
2979                                         return OPERATOR_FINISHED;
2980                                 }
2981                                 wm_gesture_end(C, op);
2982                                 return OPERATOR_CANCELLED;
2983                                 break;
2984                                 
2985                         case GESTURE_MODAL_CANCEL:
2986                                 wm_gesture_end(C, op);
2987                                 return OPERATOR_CANCELLED;
2988                 }
2989                 
2990         }
2991
2992         return OPERATOR_RUNNING_MODAL;
2993 }
2994
2995 int WM_gesture_straightline_cancel(bContext *C, wmOperator *op)
2996 {
2997         wm_gesture_end(C, op);
2998
2999         return OPERATOR_CANCELLED;
3000 }
3001
3002 #if 0
3003 /* template to copy from */
3004 void WM_OT_straightline_gesture(wmOperatorType *ot)
3005 {
3006         PropertyRNA *prop;
3007         
3008         ot->name = "Straight Line Gesture";
3009         ot->idname = "WM_OT_straightline_gesture";
3010         ot->description = "Draw a straight line as you move the pointer";
3011         
3012         ot->invoke = WM_gesture_straightline_invoke;
3013         ot->modal = WM_gesture_straightline_modal;
3014         ot->exec = gesture_straightline_exec;
3015         
3016         ot->poll = WM_operator_winactive;
3017         
3018         WM_operator_properties_gesture_straightline(ot, 0);
3019 }
3020 #endif
3021
3022 /* *********************** radial control ****************** */
3023
3024 static const int WM_RADIAL_CONTROL_DISPLAY_SIZE = 200;
3025
3026 typedef struct {
3027         PropertyType type;
3028         PropertySubType subtype;
3029         PointerRNA ptr, col_ptr, fill_col_ptr, rot_ptr, zoom_ptr, image_id_ptr;
3030         PropertyRNA *prop, *col_prop, *fill_col_prop, *rot_prop, *zoom_prop;
3031         StructRNA *image_id_srna;
3032         float initial_value, current_value, min_value, max_value;
3033         int initial_mouse[2];
3034         unsigned int gltex;
3035         ListBase orig_paintcursors;
3036         void *cursor;
3037 } RadialControl;
3038
3039 static void radial_control_set_initial_mouse(RadialControl *rc, wmEvent *event)
3040 {
3041         float d[2] = {0, 0};
3042         float zoom[2] = {1, 1};
3043
3044         rc->initial_mouse[0] = event->x;
3045         rc->initial_mouse[1] = event->y;
3046
3047         switch (rc->subtype) {
3048                 case PROP_DISTANCE:
3049                         d[0] = rc->initial_value;
3050                         break;
3051                 case PROP_FACTOR:
3052                         d[0] = WM_RADIAL_CONTROL_DISPLAY_SIZE * (1 - rc->initial_value);
3053                         break;
3054                 case PROP_ANGLE:
3055                         d[0] = WM_RADIAL_CONTROL_DISPLAY_SIZE * cos(rc->initial_value);
3056                         d[1] = WM_RADIAL_CONTROL_DISPLAY_SIZE * sin(rc->initial_value);
3057                         break;
3058                 default:
3059                         return;
3060         }
3061
3062         if (rc->zoom_prop) {
3063                 RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom);
3064                 d[0] *= zoom[0];
3065                 d[1] *= zoom[1];
3066         }
3067
3068         rc->initial_mouse[0] -= d[0];
3069         rc->initial_mouse[1] -= d[1];
3070 }
3071
3072 static void radial_control_set_tex(RadialControl *rc)
3073 {
3074         ImBuf *ibuf;
3075
3076         switch (RNA_type_to_ID_code(rc->image_id_ptr.type)) {
3077                 case ID_BR:
3078                         if ((ibuf = BKE_brush_gen_radial_control_imbuf(rc->image_id_ptr.data))) {
3079                                 glGenTextures(1, &rc->gltex);
3080                                 glBindTexture(GL_TEXTURE_2D, rc->gltex);
3081                                 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, ibuf->x, ibuf->y, 0,
3082                                              GL_ALPHA, GL_FLOAT, ibuf->rect_float);
3083                                 MEM_freeN(ibuf->rect_float);
3084                                 MEM_freeN(ibuf);
3085                         }
3086                         break;
3087                 default:
3088                         break;
3089         }
3090 }
3091
3092 static void radial_control_paint_tex(RadialControl *rc, float radius, float alpha)
3093 {
3094         float col[3] = {0, 0, 0};
3095         float rot;
3096
3097         /* set fill color */
3098         if (rc->fill_col_prop)
3099                 RNA_property_float_get_array(&rc->fill_col_ptr, rc->fill_col_prop, col);
3100         glColor4f(col[0], col[1], col[2], alpha);
3101
3102         if (rc->gltex) {
3103                 glBindTexture(GL_TEXTURE_2D, rc->gltex);
3104
3105                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
3106                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
3107
3108                 /* set up rotation if available */
3109                 if (rc->rot_prop) {
3110                         rot = RNA_property_float_get(&rc->rot_ptr, rc->rot_prop);
3111                         glPushMatrix();
3112                         glRotatef(RAD2DEGF(rot), 0, 0, 1);
3113                 }
3114
3115                 /* draw textured quad */
3116                 glEnable(GL_TEXTURE_2D);
3117                 glBegin(GL_QUADS);
3118                 glTexCoord2f(0, 0);
3119                 glVertex2f(-radius, -radius);
3120                 glTexCoord2f(1, 0);
3121                 glVertex2f(radius, -radius);
3122                 glTexCoord2f(1, 1);
3123                 glVertex2f(radius, radius);
3124                 glTexCoord2f(0, 1);
3125                 glVertex2f(-radius, radius);
3126                 glEnd();
3127                 glDisable(GL_TEXTURE_2D);
3128
3129                 /* undo rotation */
3130                 if (rc->rot_prop)
3131                         glPopMatrix();
3132         }
3133         else {
3134                 /* flat color if no texture available */
3135                 glutil_draw_filled_arc(0, M_PI * 2, radius, 40);
3136         }
3137 }
3138
3139 static void radial_control_paint_cursor(bContext *C, int x, int y, void *customdata)
3140 {
3141         RadialControl *rc = customdata;
3142         ARegion *ar = CTX_wm_region(C);
3143         float r1 = 0.0f, r2 = 0.0f, tex_radius, alpha;
3144         float zoom[2], col[3] = {1, 1, 1};
3145
3146         switch (rc->subtype) {
3147                 case PROP_DISTANCE:
3148                         r1 = rc->current_value;
3149                         r2 = rc->initial_value;
3150                         tex_radius = r1;
3151                         alpha = 0.75;
3152                         break;
3153                 case PROP_FACTOR:
3154                         r1 = (1 - rc->current_value) * WM_RADIAL_CONTROL_DISPLAY_SIZE;
3155                         r2 = tex_radius = WM_RADIAL_CONTROL_DISPLAY_SIZE;
3156                         alpha = rc->current_value / 2.0f + 0.5f;
3157                         break;
3158                 case PROP_ANGLE:
3159                         r1 = r2 = tex_radius = WM_RADIAL_CONTROL_DISPLAY_SIZE;
3160                         alpha = 0.75;
3161                         break;
3162                 default:
3163                         tex_radius = WM_RADIAL_CONTROL_DISPLAY_SIZE; /* note, this is a dummy value */
3164                         alpha = 0.75;
3165                         break;
3166         }
3167
3168         /* Keep cursor in the original place */
3169         x = rc->initial_mouse[0] - ar->winrct.xmin;
3170         y = rc->initial_mouse[1] - ar->winrct.ymin;
3171         glTranslatef((float)x, (float)y, 0.0f);
3172
3173         glEnable(GL_BLEND);
3174         glEnable(GL_LINE_SMOOTH);
3175
3176         /* apply zoom if available */
3177         if (rc->zoom_prop) {
3178                 RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom);
3179                 glScalef(zoom[0], zoom[1], 1);
3180         }
3181
3182         /* draw rotated texture */
3183         radial_control_paint_tex(rc, tex_radius, alpha);
3184
3185         /* set line color */
3186         if (rc->col_prop)
3187                 RNA_property_float_get_array(&rc->col_ptr, rc->col_prop, col);
3188         glColor4f(col[0], col[1], col[2], 0.5);
3189
3190         if (rc->subtype == PROP_ANGLE) {
3191                 glPushMatrix();
3192                 /* draw original angle line */
3193                 glRotatef(RAD2DEGF(rc->initial_value), 0, 0, 1);
3194                 fdrawline(0.0f, 0.0f, (float)WM_RADIAL_CONTROL_DISPLAY_SIZE, 0.0f);
3195                 /* draw new angle line */
3196                 glRotatef(RAD2DEGF(rc->current_value - rc->initial_value), 0, 0, 1);
3197                 fdrawline(0.0f, 0.0f, (float)WM_RADIAL_CONTROL_DISPLAY_SIZE, 0.0f);
3198                 glPopMatrix();
3199         }
3200
3201         /* draw circles on top */
3202         glutil_draw_lined_arc(0.0, (float)(M_PI * 2.0), r1, 40);
3203         glutil_draw_lined_arc(0.0, (float)(M_PI * 2.0), r2, 40);
3204
3205         glDisable(GL_BLEND);
3206         glDisable(GL_LINE_SMOOTH);
3207 }
3208
3209 typedef enum {
3210         RC_PROP_ALLOW_MISSING = 1,
3211         RC_PROP_REQUIRE_FLOAT = 2,
3212         RC_PROP_REQUIRE_BOOL = 4,
3213 } RCPropFlags;
3214
3215 /* attempt to retrieve the rna pointer/property from an rna path;
3216  * returns 0 for failure, 1 for success, and also 1 if property is not
3217  * set */
3218 static int radial_control_get_path(PointerRNA *ctx_ptr, wmOperator *op,
3219                                    const char *name, PointerRNA *r_ptr,
3220                                    PropertyRNA **r_prop, int req_length, RCPropFlags flags)
3221 {
3222         PropertyRNA *unused_prop;
3223         int len;
3224         char *str;
3225
3226         /* check flags */
3227         if ((flags & RC_PROP_REQUIRE_BOOL) && (flags & RC_PROP_REQUIRE_FLOAT)) {
3228                 BKE_report(op->reports, RPT_ERROR, "Property cannot be both boolean and float");
3229                 return 0;
3230         }
3231
3232         /* get an rna string path from the operator's properties */
3233         if (!(str = RNA_string_get_alloc(op->ptr, name, NULL, 0)))
3234                 return 1;
3235
3236         if (str[0] == '\0') {
3237                 if (r_prop) *r_prop = NULL;
3238                 MEM_freeN(str);
3239                 return 1;
3240         }
3241
3242         if (!r_prop)
3243                 r_prop = &unused_prop;
3244
3245         /* get rna from path */
3246         if (!RNA_path_resolve(ctx_ptr, str, r_ptr, r_prop)) {
3247                 MEM_freeN(str);
3248                 if (flags & RC_PROP_ALLOW_MISSING)
3249                         return 1;
3250                 else {
3251                         BKE_reportf(op->reports, RPT_ERROR, "Could not resolve path '%s'", name);
3252                         return 0;
3253                 }
3254         }
3255
3256         /* check property type */
3257         if (flags & (RC_PROP_REQUIRE_BOOL | RC_PROP_REQUIRE_FLOAT)) {
3258                 PropertyType prop_type = RNA_property_type(*r_prop);
3259
3260                 if (((flags & RC_PROP_REQUIRE_BOOL) && (prop_type != PROP_BOOLEAN)) ||
3261                     ((flags & RC_PROP_REQUIRE_FLOAT) && prop_type != PROP_FLOAT))
3262                 {
3263