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