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