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