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