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