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