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