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