2 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
18 * The Original Code is Copyright (C) 2007 Blender Foundation.
19 * All rights reserved.
22 * Contributor(s): Blender Foundation
24 * ***** END GPL LICENSE BLOCK *****
27 /** \file blender/windowmanager/intern/wm_operators.c
39 #include "GHOST_C-api.h"
41 #include "MEM_guardedalloc.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 */
51 #include "BLF_translation.h"
55 #include "BLI_blenlib.h"
56 #include "BLI_dynstr.h" /*for WM_operator_pystring */
58 #include "BLI_utildefines.h"
59 #include "BLI_ghash.h"
61 #include "BLO_readfile.h"
63 #include "BKE_blender.h"
64 #include "BKE_brush.h"
65 #include "BKE_context.h"
66 #include "BKE_depsgraph.h"
67 #include "BKE_idprop.h"
68 #include "BKE_library.h"
69 #include "BKE_global.h"
71 #include "BKE_report.h"
72 #include "BKE_scene.h"
73 #include "BKE_screen.h" /* BKE_ST_MAXNAME */
75 #include "BKE_idcode.h"
78 #include "BIF_glutil.h" /* for paint cursor */
81 #include "IMB_imbuf_types.h"
82 #include "IMB_imbuf.h"
84 #include "ED_screen.h"
87 #include "RNA_access.h"
88 #include "RNA_define.h"
89 #include "RNA_enum_types.h"
91 #include "UI_interface.h"
92 #include "UI_resources.h"
99 #include "wm_event_system.h"
100 #include "wm_event_types.h"
101 #include "wm_subwindow.h"
102 #include "wm_window.h"
104 static GHash *global_ops_hash= NULL;
106 /* ************ operator API, exported ********** */
109 wmOperatorType *WM_operatortype_find(const char *idname, int quiet)
114 /* needed to support python style names without the _OT_ syntax */
115 char idname_bl[OP_MAX_TYPENAME];
116 WM_operator_bl_idname(idname_bl, idname);
118 ot= BLI_ghash_lookup(global_ops_hash, idname_bl);
124 printf("search for unknown operator '%s', '%s'\n", idname_bl, idname);
129 printf("search for empty operator\n");
136 /* caller must free */
137 GHashIterator *WM_operatortype_iter(void)
139 return BLI_ghashIterator_new(global_ops_hash);
142 /* all ops in 1 list (for time being... needs evaluation later) */
143 void WM_operatortype_append(void (*opfunc)(wmOperatorType*))
147 ot= MEM_callocN(sizeof(wmOperatorType), "operatortype");
148 ot->srna= RNA_def_struct(&BLENDER_RNA, "", "OperatorProperties");
152 fprintf(stderr, "ERROR: Operator %s has no name property!\n", ot->idname);
153 ot->name= IFACE_("Dummy Name");
156 RNA_def_struct_ui_text(ot->srna, ot->name, ot->description ? ot->description:IFACE_("(undocumented operator)")); // XXX All ops should have a description but for now allow them not to.
157 RNA_def_struct_identifier(ot->srna, ot->idname);
159 BLI_ghash_insert(global_ops_hash, (void *)ot->idname, ot);
162 void WM_operatortype_append_ptr(void (*opfunc)(wmOperatorType*, void*), void *userdata)
166 ot= MEM_callocN(sizeof(wmOperatorType), "operatortype");
167 ot->srna= RNA_def_struct(&BLENDER_RNA, "", "OperatorProperties");
168 opfunc(ot, userdata);
169 RNA_def_struct_ui_text(ot->srna, ot->name, ot->description ? ot->description:IFACE_("(undocumented operator)"));
170 RNA_def_struct_identifier(ot->srna, ot->idname);
172 BLI_ghash_insert(global_ops_hash, (void *)ot->idname, ot);
175 /* ********************* macro operator ******************** */
181 static void wm_macro_start(wmOperator *op)
183 if (op->customdata == NULL) {
184 op->customdata = MEM_callocN(sizeof(MacroData), "MacroData");
188 static int wm_macro_end(wmOperator *op, int retval)
190 if (retval & OPERATOR_CANCELLED) {
191 MacroData *md = op->customdata;
193 if (md->retval & OPERATOR_FINISHED) {
194 retval |= OPERATOR_FINISHED;
195 retval &= ~OPERATOR_CANCELLED;
199 /* if modal is ending, free custom data */
200 if (retval & (OPERATOR_FINISHED|OPERATOR_CANCELLED)) {
201 if (op->customdata) {
202 MEM_freeN(op->customdata);
203 op->customdata = NULL;
210 /* macro exec only runs exec calls */
211 static int wm_macro_exec(bContext *C, wmOperator *op)
214 int retval= OPERATOR_FINISHED;
218 for(opm= op->macro.first; opm; opm= opm->next) {
220 if(opm->type->exec) {
221 retval= opm->type->exec(C, opm);
222 OPERATOR_RETVAL_CHECK(retval);
224 if (retval & OPERATOR_FINISHED) {
225 MacroData *md = op->customdata;
226 md->retval = OPERATOR_FINISHED; /* keep in mind that at least one operator finished */
228 break; /* operator didn't finish, end macro */
233 return wm_macro_end(op, retval);
236 static int wm_macro_invoke_internal(bContext *C, wmOperator *op, wmEvent *event, wmOperator *opm)
238 int retval= OPERATOR_FINISHED;
240 /* start from operator received as argument */
241 for( ; opm; opm= opm->next) {
242 if(opm->type->invoke)
243 retval= opm->type->invoke(C, opm, event);
244 else if(opm->type->exec)
245 retval= opm->type->exec(C, opm);
247 OPERATOR_RETVAL_CHECK(retval);
249 BLI_movelisttolist(&op->reports->list, &opm->reports->list);
251 if (retval & OPERATOR_FINISHED) {
252 MacroData *md = op->customdata;
253 md->retval = OPERATOR_FINISHED; /* keep in mind that at least one operator finished */
255 break; /* operator didn't finish, end macro */
259 return wm_macro_end(op, retval);
262 static int wm_macro_invoke(bContext *C, wmOperator *op, wmEvent *event)
265 return wm_macro_invoke_internal(C, op, event, op->macro.first);
268 static int wm_macro_modal(bContext *C, wmOperator *op, wmEvent *event)
270 wmOperator *opm = op->opm;
271 int retval= OPERATOR_FINISHED;
274 printf("%s: macro error, calling NULL modal()\n", __func__);
276 retval = opm->type->modal(C, opm, event);
277 OPERATOR_RETVAL_CHECK(retval);
279 /* if this one is done but it's not the last operator in the macro */
280 if ((retval & OPERATOR_FINISHED) && opm->next) {
281 MacroData *md = op->customdata;
283 md->retval = OPERATOR_FINISHED; /* keep in mind that at least one operator finished */
285 retval = wm_macro_invoke_internal(C, op, event, opm->next);
287 /* if new operator is modal and also added its own handler */
288 if (retval & OPERATOR_RUNNING_MODAL && op->opm != opm) {
289 wmWindow *win = CTX_wm_window(C);
290 wmEventHandler *handler = NULL;
292 for (handler = win->modalhandlers.first; handler; handler = handler->next) {
293 /* first handler in list is the new one */
294 if (handler->op == op)
299 BLI_remlink(&win->modalhandlers, handler);
300 wm_event_free_handler(handler);
303 /* if operator is blocking, grab cursor
304 * This may end up grabbing twice, but we don't care.
306 if(op->opm->type->flag & OPTYPE_BLOCKING) {
307 int bounds[4] = {-1,-1,-1,-1};
308 int wrap = (U.uiflag & USER_CONTINUOUS_MOUSE) && ((op->opm->flag & OP_GRAB_POINTER) || (op->opm->type->flag & OPTYPE_GRAB_POINTER));
311 ARegion *ar= CTX_wm_region(C);
313 bounds[0]= ar->winrct.xmin;
314 bounds[1]= ar->winrct.ymax;
315 bounds[2]= ar->winrct.xmax;
316 bounds[3]= ar->winrct.ymin;
320 WM_cursor_grab(CTX_wm_window(C), wrap, FALSE, bounds);
326 return wm_macro_end(op, retval);
329 static int wm_macro_cancel(bContext *C, wmOperator *op)
331 /* call cancel on the current modal operator, if any */
332 if (op->opm && op->opm->type->cancel) {
333 op->opm->type->cancel(C, op->opm);
336 return wm_macro_end(op, OPERATOR_CANCELLED);
339 /* Names have to be static for now */
340 wmOperatorType *WM_operatortype_append_macro(const char *idname, const char *name, int flag)
344 if(WM_operatortype_find(idname, TRUE)) {
345 printf("%s: macro error: operator %s exists\n", __func__, idname);
349 ot= MEM_callocN(sizeof(wmOperatorType), "operatortype");
350 ot->srna= RNA_def_struct(&BLENDER_RNA, "", "OperatorProperties");
354 ot->flag= OPTYPE_MACRO|flag;
356 ot->exec= wm_macro_exec;
357 ot->invoke= wm_macro_invoke;
358 ot->modal= wm_macro_modal;
359 ot->cancel= wm_macro_cancel;
363 ot->description= IFACE_("(undocumented operator)");
365 RNA_def_struct_ui_text(ot->srna, ot->name, ot->description); // XXX All ops should have a description but for now allow them not to.
366 RNA_def_struct_identifier(ot->srna, ot->idname);
368 BLI_ghash_insert(global_ops_hash, (void *)ot->idname, ot);
373 void WM_operatortype_append_macro_ptr(void (*opfunc)(wmOperatorType*, void*), void *userdata)
377 ot= MEM_callocN(sizeof(wmOperatorType), "operatortype");
378 ot->srna= RNA_def_struct(&BLENDER_RNA, "", "OperatorProperties");
380 ot->flag= OPTYPE_MACRO;
381 ot->exec= wm_macro_exec;
382 ot->invoke= wm_macro_invoke;
383 ot->modal= wm_macro_modal;
384 ot->cancel= wm_macro_cancel;
388 ot->description= IFACE_("(undocumented operator)");
390 opfunc(ot, userdata);
392 RNA_def_struct_ui_text(ot->srna, ot->name, ot->description);
393 RNA_def_struct_identifier(ot->srna, ot->idname);
395 BLI_ghash_insert(global_ops_hash, (void *)ot->idname, ot);
398 wmOperatorTypeMacro *WM_operatortype_macro_define(wmOperatorType *ot, const char *idname)
400 wmOperatorTypeMacro *otmacro= MEM_callocN(sizeof(wmOperatorTypeMacro), "wmOperatorTypeMacro");
402 BLI_strncpy(otmacro->idname, idname, OP_MAX_TYPENAME);
404 /* do this on first use, since operatordefinitions might have been not done yet */
405 WM_operator_properties_alloc(&(otmacro->ptr), &(otmacro->properties), idname);
406 WM_operator_properties_sanitize(otmacro->ptr, 1);
408 BLI_addtail(&ot->macro, otmacro);
411 /* operator should always be found but in the event its not. dont segfault */
412 wmOperatorType *otsub = WM_operatortype_find(idname, 0);
414 RNA_def_pointer_runtime(ot->srna, otsub->idname, otsub->srna,
415 otsub->name, otsub->description);
422 static void wm_operatortype_free_macro(wmOperatorType *ot)
424 wmOperatorTypeMacro *otmacro;
426 for(otmacro= ot->macro.first; otmacro; otmacro= otmacro->next) {
428 WM_operator_properties_free(otmacro->ptr);
429 MEM_freeN(otmacro->ptr);
432 BLI_freelistN(&ot->macro);
436 int WM_operatortype_remove(const char *idname)
438 wmOperatorType *ot = WM_operatortype_find(idname, 0);
443 RNA_struct_free(&BLENDER_RNA, ot->srna);
446 wm_operatortype_free_macro(ot);
448 BLI_ghash_remove(global_ops_hash, (void *)ot->idname, NULL, NULL);
454 /* SOME_OT_op -> some.op */
455 void WM_operator_py_idname(char *to, const char *from)
457 char *sep= strstr(from, "_OT_");
461 /* note, we use ascii tolower instead of system tolower, because the
462 latter depends on the locale, and can lead to idname mistmatch */
463 memcpy(to, from, sizeof(char)*ofs);
464 BLI_ascii_strtolower(to, ofs);
467 BLI_strncpy(to+(ofs+1), sep+4, OP_MAX_TYPENAME);
470 /* should not happen but support just incase */
471 BLI_strncpy(to, from, OP_MAX_TYPENAME);
475 /* some.op -> SOME_OT_op */
476 void WM_operator_bl_idname(char *to, const char *from)
479 char *sep= strchr(from, '.');
484 memcpy(to, from, sizeof(char)*ofs);
485 BLI_ascii_strtoupper(to, ofs);
487 BLI_strncpy(to+ofs, "_OT_", OP_MAX_TYPENAME);
488 BLI_strncpy(to+(ofs+4), sep+1, OP_MAX_TYPENAME);
491 /* should not happen but support just incase */
492 BLI_strncpy(to, from, OP_MAX_TYPENAME);
499 /* print a string representation of the operator, with the args that it runs
500 * so python can run it again,
502 * When calling from an existing wmOperator do.
503 * WM_operator_pystring(op->type, op->ptr);
505 char *WM_operator_pystring(bContext *C, wmOperatorType *ot, PointerRNA *opptr, int all_args)
507 const char *arg_name= NULL;
508 char idname_py[OP_MAX_TYPENAME];
510 PropertyRNA *prop, *iterprop;
512 /* for building the string */
513 DynStr *dynstr= BLI_dynstr_new();
515 int first_iter=1, ok= 1;
518 /* only to get the orginal props for comparisons */
519 PointerRNA opptr_default;
520 PropertyRNA *prop_default;
522 if(all_args==0 || opptr==NULL) {
523 WM_operator_properties_create_ptr(&opptr_default, ot);
526 opptr = &opptr_default;
530 WM_operator_py_idname(idname_py, ot->idname);
531 BLI_dynstr_appendf(dynstr, "bpy.ops.%s(", idname_py);
533 iterprop= RNA_struct_iterator_property(opptr->type);
535 RNA_PROP_BEGIN(opptr, propptr, iterprop) {
537 arg_name= RNA_property_identifier(prop);
539 if (strcmp(arg_name, "rna_type")==0) continue;
541 buf= RNA_property_as_string(C, opptr, prop);
546 /* not verbose, so only add in attributes that use non-default values
547 * slow but good for tooltips */
548 prop_default= RNA_struct_find_property(&opptr_default, arg_name);
551 buf_default= RNA_property_as_string(C, &opptr_default, prop_default);
553 if(strcmp(buf, buf_default)==0)
554 ok= 0; /* values match, dont bother printing */
556 MEM_freeN(buf_default);
561 BLI_dynstr_appendf(dynstr, first_iter?"%s=%s":", %s=%s", arg_name, buf);
570 if(all_args==0 || opptr==&opptr_default )
571 WM_operator_properties_free(&opptr_default);
573 BLI_dynstr_append(dynstr, ")");
575 cstring = BLI_dynstr_get_cstring(dynstr);
576 BLI_dynstr_free(dynstr);
580 void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
582 RNA_pointer_create(NULL, ot->srna, NULL, ptr);
585 void WM_operator_properties_create(PointerRNA *ptr, const char *opstring)
587 wmOperatorType *ot= WM_operatortype_find(opstring, 0);
590 WM_operator_properties_create_ptr(ptr, ot);
592 RNA_pointer_create(NULL, &RNA_OperatorProperties, NULL, ptr);
595 /* similar to the function above except its uses ID properties
596 * used for keymaps and macros */
597 void WM_operator_properties_alloc(PointerRNA **ptr, IDProperty **properties, const char *opstring)
599 if(*properties==NULL) {
600 IDPropertyTemplate val = {0};
601 *properties= IDP_New(IDP_GROUP, &val, "wmOpItemProp");
605 *ptr= MEM_callocN(sizeof(PointerRNA), "wmOpItemPtr");
606 WM_operator_properties_create(*ptr, opstring);
609 (*ptr)->data= *properties;
613 void WM_operator_properties_sanitize(PointerRNA *ptr, const short no_context)
615 RNA_STRUCT_BEGIN(ptr, prop) {
616 switch(RNA_property_type(prop)) {
619 RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT);
621 RNA_def_property_clear_flag(prop, PROP_ENUM_NO_CONTEXT);
625 StructRNA *ptype= RNA_property_pointer_type(ptr, prop);
627 /* recurse into operator properties */
628 if (RNA_struct_is_a(ptype, &RNA_OperatorProperties)) {
629 PointerRNA opptr = RNA_property_pointer_get(ptr, prop);
630 WM_operator_properties_sanitize(&opptr, no_context);
641 void WM_operator_properties_free(PointerRNA *ptr)
643 IDProperty *properties= ptr->data;
646 IDP_FreeProperty(properties);
647 MEM_freeN(properties);
648 ptr->data= NULL; /* just incase */
652 /* ************ default op callbacks, exported *********** */
654 /* invoke callback, uses enum property named "type" */
655 int WM_menu_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
657 PropertyRNA *prop= op->type->prop;
662 printf("%s: %s has no enum property set\n", __func__, op->type->idname);
664 else if (RNA_property_type(prop) != PROP_ENUM) {
665 printf("%s: %s \"%s\" is not an enum property\n",
666 __func__, op->type->idname, RNA_property_identifier(prop));
668 else if (RNA_property_is_set(op->ptr, RNA_property_identifier(prop))) {
669 const int retval= op->type->exec(C, op);
670 OPERATOR_RETVAL_CHECK(retval);
674 pup= uiPupMenuBegin(C, op->type->name, ICON_NONE);
675 layout= uiPupMenuLayout(pup);
676 uiItemsFullEnumO(layout, op->type->idname, RNA_property_identifier(prop), op->ptr->data, WM_OP_EXEC_REGION_WIN, 0);
677 uiPupMenuEnd(C, pup);
680 return OPERATOR_CANCELLED;
684 /* generic enum search invoke popup */
685 static void operator_enum_search_cb(const struct bContext *C, void *arg_ot, const char *str, uiSearchItems *items)
687 wmOperatorType *ot = (wmOperatorType *)arg_ot;
688 PropertyRNA *prop= ot->prop;
691 printf("%s: %s has no enum property set\n",
692 __func__, ot->idname);
694 else if (RNA_property_type(prop) != PROP_ENUM) {
695 printf("%s: %s \"%s\" is not an enum property\n",
696 __func__, ot->idname, RNA_property_identifier(prop));
701 EnumPropertyItem *item, *item_array;
704 RNA_pointer_create(NULL, ot->srna, NULL, &ptr);
705 RNA_property_enum_items((bContext *)C, &ptr, prop, &item_array, NULL, &do_free);
707 for(item= item_array; item->identifier; item++) {
708 /* note: need to give the intex rather than the dientifier because the enum can be freed */
709 if(BLI_strcasestr(item->name, str))
710 if(0==uiSearchItemAdd(items, item->name, SET_INT_IN_POINTER(item->value), 0))
715 MEM_freeN(item_array);
719 static void operator_enum_call_cb(struct bContext *C, void *arg1, void *arg2)
721 wmOperatorType *ot= arg1;
725 PointerRNA props_ptr;
726 WM_operator_properties_create_ptr(&props_ptr, ot);
727 RNA_property_enum_set(&props_ptr, ot->prop, GET_INT_FROM_POINTER(arg2));
728 WM_operator_name_call(C, ot->idname, WM_OP_EXEC_DEFAULT, &props_ptr);
729 WM_operator_properties_free(&props_ptr);
732 printf("%s: op->prop for '%s' is NULL\n", __func__, ot->idname);
737 static uiBlock *wm_enum_search_menu(bContext *C, ARegion *ar, void *arg_op)
739 static char search[256]= "";
741 wmWindow *win= CTX_wm_window(C);
744 wmOperator *op= (wmOperator *)arg_op;
746 block= uiBeginBlock(C, ar, "_popup", UI_EMBOSS);
747 uiBlockSetFlag(block, UI_BLOCK_LOOP|UI_BLOCK_RET_1|UI_BLOCK_MOVEMOUSE_QUIT);
749 //uiDefBut(block, LABEL, 0, op->type->name, 10, 10, 180, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, ""); // ok, this isnt so easy...
750 but= uiDefSearchBut(block, search, 0, ICON_VIEWZOOM, 256, 10, 10, 9*UI_UNIT_X, UI_UNIT_Y, 0, 0, "");
751 uiButSetSearchFunc(but, operator_enum_search_cb, op->type, operator_enum_call_cb, NULL);
753 /* fake button, it holds space for search items */
754 uiDefBut(block, LABEL, 0, "", 10, 10 - uiSearchBoxhHeight(), 9*UI_UNIT_X, uiSearchBoxhHeight(), NULL, 0, 0, 0, 0, NULL);
756 uiPopupBoundsBlock(block, 6, 0, -UI_UNIT_Y); /* move it downwards, mouse over button */
757 uiEndBlock(C, block);
759 event= *(win->eventstate); /* XXX huh huh? make api call */
760 event.type= EVT_BUT_OPEN;
762 event.customdata= but;
763 event.customdatafree= FALSE;
764 wm_event_add(win, &event);
770 int WM_enum_search_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
772 uiPupBlock(C, wm_enum_search_menu, op);
773 return OPERATOR_CANCELLED;
776 /* Can't be used as an invoke directly, needs message arg (can be NULL) */
777 int WM_operator_confirm_message(bContext *C, wmOperator *op, const char *message)
781 IDProperty *properties= op->ptr->data;
783 if(properties && properties->len)
784 properties= IDP_CopyProperty(op->ptr->data);
788 pup= uiPupMenuBegin(C, IFACE_("OK?"), ICON_QUESTION);
789 layout= uiPupMenuLayout(pup);
790 uiItemFullO(layout, op->type->idname, message, ICON_NONE, properties, WM_OP_EXEC_REGION_WIN, 0);
791 uiPupMenuEnd(C, pup);
793 return OPERATOR_CANCELLED;
797 int WM_operator_confirm(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
799 return WM_operator_confirm_message(C, op, NULL);
802 /* op->invoke, opens fileselect if path property not set, otherwise executes */
803 int WM_operator_filesel(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
805 if (RNA_property_is_set(op->ptr, "filepath")) {
806 return WM_operator_call_notest(C, op); /* call exec direct */
809 WM_event_add_fileselect(C, op);
810 return OPERATOR_RUNNING_MODAL;
814 /* default properties for fileselect */
815 void WM_operator_properties_filesel(wmOperatorType *ot, int filter, short type, short action, short flag)
820 if(flag & WM_FILESEL_FILEPATH)
821 RNA_def_string_file_path(ot->srna, "filepath", "", FILE_MAX, "File Path", "Path to file");
823 if(flag & WM_FILESEL_DIRECTORY)
824 RNA_def_string_dir_path(ot->srna, "directory", "", FILE_MAX, "Directory", "Directory of the file");
826 if(flag & WM_FILESEL_FILENAME)
827 RNA_def_string_file_name(ot->srna, "filename", "", FILE_MAX, "File Name", "Name of the file");
829 if(flag & WM_FILESEL_FILES)
830 RNA_def_collection_runtime(ot->srna, "files", &RNA_OperatorFileListElement, "Files", "");
832 if (action == FILE_SAVE) {
833 prop= RNA_def_boolean(ot->srna, "check_existing", 1, "Check Existing", "Check and warn on overwriting existing files");
834 RNA_def_property_flag(prop, PROP_HIDDEN);
837 prop= RNA_def_boolean(ot->srna, "filter_blender", (filter & BLENDERFILE), "Filter .blend files", "");
838 RNA_def_property_flag(prop, PROP_HIDDEN);
839 prop= RNA_def_boolean(ot->srna, "filter_image", (filter & IMAGEFILE), "Filter image files", "");
840 RNA_def_property_flag(prop, PROP_HIDDEN);
841 prop= RNA_def_boolean(ot->srna, "filter_movie", (filter & MOVIEFILE), "Filter movie files", "");
842 RNA_def_property_flag(prop, PROP_HIDDEN);
843 prop= RNA_def_boolean(ot->srna, "filter_python", (filter & PYSCRIPTFILE), "Filter python files", "");
844 RNA_def_property_flag(prop, PROP_HIDDEN);
845 prop= RNA_def_boolean(ot->srna, "filter_font", (filter & FTFONTFILE), "Filter font files", "");
846 RNA_def_property_flag(prop, PROP_HIDDEN);
847 prop= RNA_def_boolean(ot->srna, "filter_sound", (filter & SOUNDFILE), "Filter sound files", "");
848 RNA_def_property_flag(prop, PROP_HIDDEN);
849 prop= RNA_def_boolean(ot->srna, "filter_text", (filter & TEXTFILE), "Filter text files", "");
850 RNA_def_property_flag(prop, PROP_HIDDEN);
851 prop= RNA_def_boolean(ot->srna, "filter_btx", (filter & BTXFILE), "Filter btx files", "");
852 RNA_def_property_flag(prop, PROP_HIDDEN);
853 prop= RNA_def_boolean(ot->srna, "filter_collada", (filter & COLLADAFILE), "Filter COLLADA files", "");
854 RNA_def_property_flag(prop, PROP_HIDDEN);
855 prop= RNA_def_boolean(ot->srna, "filter_folder", (filter & FOLDERFILE), "Filter folders", "");
856 RNA_def_property_flag(prop, PROP_HIDDEN);
858 prop= RNA_def_int(ot->srna, "filemode", type, FILE_LOADLIB, FILE_SPECIAL,
859 "File Browser Mode", "The setting for the file browser mode to load a .blend file, a library or a special file",
860 FILE_LOADLIB, FILE_SPECIAL);
861 RNA_def_property_flag(prop, PROP_HIDDEN);
863 if(flag & WM_FILESEL_RELPATH)
864 RNA_def_boolean(ot->srna, "relative_path", TRUE, "Relative Path", "Select the file relative to the blend file");
867 void WM_operator_properties_select_all(wmOperatorType *ot)
869 static EnumPropertyItem select_all_actions[] = {
870 {SEL_TOGGLE, "TOGGLE", 0, "Toggle", "Toggle selection for all elements"},
871 {SEL_SELECT, "SELECT", 0, "Select", "Select all elements"},
872 {SEL_DESELECT, "DESELECT", 0, "Deselect", "Deselect all elements"},
873 {SEL_INVERT, "INVERT", 0, "Invert", "Invert selection of all elements"},
874 {0, NULL, 0, NULL, NULL}
877 RNA_def_enum(ot->srna, "action", select_all_actions, SEL_TOGGLE, "Action", "Selection action to execute");
880 void WM_operator_properties_gesture_border(wmOperatorType *ot, int extend)
882 RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Gesture Mode", "", INT_MIN, INT_MAX);
883 RNA_def_int(ot->srna, "xmin", 0, INT_MIN, INT_MAX, "X Min", "", INT_MIN, INT_MAX);
884 RNA_def_int(ot->srna, "xmax", 0, INT_MIN, INT_MAX, "X Max", "", INT_MIN, INT_MAX);
885 RNA_def_int(ot->srna, "ymin", 0, INT_MIN, INT_MAX, "Y Min", "", INT_MIN, INT_MAX);
886 RNA_def_int(ot->srna, "ymax", 0, INT_MIN, INT_MAX, "Y Max", "", INT_MIN, INT_MAX);
889 RNA_def_boolean(ot->srna, "extend", 1, "Extend", "Extend selection instead of deselecting everything first");
892 void WM_operator_properties_gesture_straightline(wmOperatorType *ot, int cursor)
894 RNA_def_int(ot->srna, "xstart", 0, INT_MIN, INT_MAX, "X Start", "", INT_MIN, INT_MAX);
895 RNA_def_int(ot->srna, "xend", 0, INT_MIN, INT_MAX, "X End", "", INT_MIN, INT_MAX);
896 RNA_def_int(ot->srna, "ystart", 0, INT_MIN, INT_MAX, "Y Start", "", INT_MIN, INT_MAX);
897 RNA_def_int(ot->srna, "yend", 0, INT_MIN, INT_MAX, "Y End", "", INT_MIN, INT_MAX);
900 RNA_def_int(ot->srna, "cursor", cursor, 0, INT_MAX, "Cursor", "Mouse cursor style to use during the modal operator", 0, INT_MAX);
905 int WM_operator_winactive(bContext *C)
907 if(CTX_wm_window(C)==NULL) return 0;
911 /* return FALSE, if the UI should be disabled */
912 int WM_operator_check_ui_enabled(const bContext *C, const char *idname)
914 wmWindowManager *wm= CTX_wm_manager(C);
915 Scene *scene= CTX_data_scene(C);
917 return !(ED_undo_valid(C, idname)==0 || WM_jobs_test(wm, scene));
920 wmOperator *WM_operator_last_redo(const bContext *C)
922 wmWindowManager *wm= CTX_wm_manager(C);
925 /* only for operators that are registered and did an undo push */
926 for(op= wm->operators.last; op; op= op->prev)
927 if((op->type->flag & OPTYPE_REGISTER) && (op->type->flag & OPTYPE_UNDO))
933 static uiBlock *wm_block_create_redo(bContext *C, ARegion *ar, void *arg_op)
935 wmOperator *op= arg_op;
938 uiStyle *style= UI_GetStyle();
942 block= uiBeginBlock(C, ar, __func__, UI_EMBOSS);
943 uiBlockClearFlag(block, UI_BLOCK_LOOP);
944 uiBlockSetFlag(block, UI_BLOCK_KEEP_OPEN|UI_BLOCK_RET_1|UI_BLOCK_MOVEMOUSE_QUIT);
946 /* if register is not enabled, the operator gets freed on OPERATOR_FINISHED
947 * ui_apply_but_funcs_after calls ED_undo_operator_repeate_cb and crashes */
948 assert(op->type->flag & OPTYPE_REGISTER);
950 uiBlockSetHandleFunc(block, ED_undo_operator_repeat_cb_evt, arg_op);
951 layout= uiBlockLayout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, width, UI_UNIT_Y, style);
953 if (!WM_operator_check_ui_enabled(C, op->type->name))
954 uiLayoutSetEnabled(layout, 0);
956 if(op->type->flag & OPTYPE_MACRO) {
957 for(op= op->macro.first; op; op= op->next) {
958 uiItemL(layout, op->type->name, ICON_NONE);
959 uiLayoutOperatorButs(C, layout, op, NULL, 'H', UI_LAYOUT_OP_SHOW_TITLE);
963 uiLayoutOperatorButs(C, layout, op, NULL, 'H', UI_LAYOUT_OP_SHOW_TITLE);
967 uiPopupBoundsBlock(block, 4, 0, 0);
968 uiEndBlock(C, block);
973 typedef struct wmOpPopUp
981 /* Only invoked by OK button in popups created with wm_block_dialog_create() */
982 static void dialog_exec_cb(bContext *C, void *arg1, void *arg2)
984 wmOpPopUp *data= arg1;
985 uiBlock *block= arg2;
987 WM_operator_call(C, data->op);
989 /* let execute handle freeing it */
990 //data->free_op= FALSE;
993 /* in this case, wm_operator_ui_popup_cancel wont run */
996 uiPupBlockClose(C, block);
999 static void dialog_check_cb(bContext *C, void *op_ptr, void *UNUSED(arg))
1001 wmOperator *op= op_ptr;
1002 if(op->type->check) {
1003 if(op->type->check(C, op)) {
1009 /* Dialogs are popups that require user verification (click OK) before exec */
1010 static uiBlock *wm_block_dialog_create(bContext *C, ARegion *ar, void *userData)
1012 wmOpPopUp *data= userData;
1013 wmOperator *op= data->op;
1016 uiStyle *style= UI_GetStyle();
1018 block = uiBeginBlock(C, ar, __func__, UI_EMBOSS);
1019 uiBlockClearFlag(block, UI_BLOCK_LOOP);
1020 uiBlockSetFlag(block, UI_BLOCK_KEEP_OPEN|UI_BLOCK_RET_1|UI_BLOCK_MOVEMOUSE_QUIT);
1022 layout= uiBlockLayout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, data->width, data->height, style);
1024 uiBlockSetFunc(block, dialog_check_cb, op, NULL);
1026 uiLayoutOperatorButs(C, layout, op, NULL, 'H', UI_LAYOUT_OP_SHOW_TITLE);
1028 /* clear so the OK button is left alone */
1029 uiBlockSetFunc(block, NULL, NULL, NULL);
1031 /* new column so as not to interfear with custom layouts [#26436] */
1037 col= uiLayoutColumn(layout, FALSE);
1038 col_block= uiLayoutGetBlock(col);
1039 /* Create OK button, the callback of which will execute op */
1040 btn= uiDefBut(col_block, BUT, 0, IFACE_("OK"), 0, -30, 0, UI_UNIT_Y, NULL, 0, 0, 0, 0, "");
1041 uiButSetFunc(btn, dialog_exec_cb, data, col_block);
1044 /* center around the mouse */
1045 uiPopupBoundsBlock(block, 4, data->width/-2, data->height/2);
1046 uiEndBlock(C, block);
1051 static uiBlock *wm_operator_ui_create(bContext *C, ARegion *ar, void *userData)
1053 wmOpPopUp *data= userData;
1054 wmOperator *op= data->op;
1057 uiStyle *style= UI_GetStyle();
1059 block= uiBeginBlock(C, ar, __func__, UI_EMBOSS);
1060 uiBlockClearFlag(block, UI_BLOCK_LOOP);
1061 uiBlockSetFlag(block, UI_BLOCK_KEEP_OPEN|UI_BLOCK_RET_1|UI_BLOCK_MOVEMOUSE_QUIT);
1063 layout= uiBlockLayout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, data->width, data->height, style);
1065 /* since ui is defined the auto-layout args are not used */
1066 uiLayoutOperatorButs(C, layout, op, NULL, 'V', 0);
1068 uiPopupBoundsBlock(block, 4, 0, 0);
1069 uiEndBlock(C, block);
1074 static void wm_operator_ui_popup_cancel(void *userData)
1076 wmOpPopUp *data= userData;
1077 if(data->free_op && data->op) {
1078 wmOperator *op= data->op;
1079 WM_operator_free(op);
1085 static void wm_operator_ui_popup_ok(struct bContext *C, void *arg, int retval)
1087 wmOpPopUp *data= arg;
1088 wmOperator *op= data->op;
1090 if(op && retval > 0)
1091 WM_operator_call(C, op);
1094 int WM_operator_ui_popup(bContext *C, wmOperator *op, int width, int height)
1096 wmOpPopUp *data= MEM_callocN(sizeof(wmOpPopUp), "WM_operator_ui_popup");
1099 data->height= height;
1100 data->free_op= TRUE; /* if this runs and gets registered we may want not to free it */
1101 uiPupBlockEx(C, wm_operator_ui_create, NULL, wm_operator_ui_popup_cancel, data);
1102 return OPERATOR_RUNNING_MODAL;
1105 /* operator menu needs undo, for redo callback */
1106 int WM_operator_props_popup(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1109 if((op->type->flag & OPTYPE_REGISTER)==0) {
1110 BKE_reportf(op->reports, RPT_ERROR, "Operator '%s' does not have register enabled, incorrect invoke function.", op->type->idname);
1111 return OPERATOR_CANCELLED;
1114 ED_undo_push_op(C, op);
1115 wm_operator_register(C, op);
1117 uiPupBlock(C, wm_block_create_redo, op);
1119 return OPERATOR_RUNNING_MODAL;
1122 int WM_operator_props_dialog_popup(bContext *C, wmOperator *op, int width, int height)
1124 wmOpPopUp *data= MEM_callocN(sizeof(wmOpPopUp), "WM_operator_props_dialog_popup");
1128 data->height= height;
1129 data->free_op= TRUE; /* if this runs and gets registered we may want not to free it */
1131 /* op is not executed until popup OK but is clicked */
1132 uiPupBlockEx(C, wm_block_dialog_create, wm_operator_ui_popup_ok, wm_operator_ui_popup_cancel, data);
1134 return OPERATOR_RUNNING_MODAL;
1137 int WM_operator_redo_popup(bContext *C, wmOperator *op)
1139 /* CTX_wm_reports(C) because operator is on stack, not active in event system */
1140 if((op->type->flag & OPTYPE_REGISTER)==0) {
1141 BKE_reportf(CTX_wm_reports(C), RPT_ERROR, "Operator redo '%s' does not have register enabled, incorrect invoke function.", op->type->idname);
1142 return OPERATOR_CANCELLED;
1144 if(op->type->poll && op->type->poll(C)==0) {
1145 BKE_reportf(CTX_wm_reports(C), RPT_ERROR, "Operator redo '%s': wrong context.", op->type->idname);
1146 return OPERATOR_CANCELLED;
1149 uiPupBlock(C, wm_block_create_redo, op);
1151 return OPERATOR_CANCELLED;
1154 /* ***************** Debug menu ************************* */
1156 static int wm_debug_menu_exec(bContext *C, wmOperator *op)
1158 G.rt= RNA_int_get(op->ptr, "debug_value");
1159 ED_screen_refresh(CTX_wm_manager(C), CTX_wm_window(C));
1160 WM_event_add_notifier(C, NC_WINDOW, NULL);
1162 return OPERATOR_FINISHED;
1165 static int wm_debug_menu_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1167 RNA_int_set(op->ptr, "debug_value", G.rt);
1168 return WM_operator_props_dialog_popup(C, op, 9*UI_UNIT_X, UI_UNIT_Y);
1171 static void WM_OT_debug_menu(wmOperatorType *ot)
1173 ot->name= "Debug Menu";
1174 ot->idname= "WM_OT_debug_menu";
1175 ot->description= "Open a popup to set the debug level";
1177 ot->invoke= wm_debug_menu_invoke;
1178 ot->exec= wm_debug_menu_exec;
1179 ot->poll= WM_operator_winactive;
1181 RNA_def_int(ot->srna, "debug_value", 0, -10000, 10000, "Debug Value", "", INT_MIN, INT_MAX);
1185 /* ***************** Splash Screen ************************* */
1187 static void wm_block_splash_close(bContext *C, void *arg_block, void *UNUSED(arg))
1189 uiPupBlockClose(C, arg_block);
1192 static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *arg_unused);
1194 /* XXX: hack to refresh splash screen with updated prest menu name,
1195 * since popup blocks don't get regenerated like panels do */
1196 static void wm_block_splash_refreshmenu (bContext *UNUSED(C), void *UNUSED(arg_block), void *UNUSED(arg))
1198 /* ugh, causes crashes in other buttons, disabling for now until
1200 uiPupBlockClose(C, arg_block);
1201 uiPupBlock(C, wm_block_create_splash, NULL);
1205 static int wm_resource_check_prev(void)
1208 char *res= BLI_get_folder_version(BLENDER_RESOURCE_PATH_USER, BLENDER_VERSION, TRUE);
1210 // if(res) printf("USER: %s\n", res);
1212 #if 0 /* ignore the local folder */
1214 /* with a local dir, copying old files isnt useful since local dir get priority for config */
1215 res= BLI_get_folder_version(BLENDER_RESOURCE_PATH_LOCAL, BLENDER_VERSION, TRUE);
1219 // if(res) printf("LOCAL: %s\n", res);
1224 return (BLI_get_folder_version(BLENDER_RESOURCE_PATH_USER, BLENDER_VERSION - 1, TRUE) != NULL);
1228 static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(arg))
1232 uiLayout *layout, *split, *col;
1233 uiStyle *style= UI_GetStyle();
1234 struct RecentFile *recent;
1236 MenuType *mt= WM_menutype_find("USERPREF_MT_splash", TRUE);
1239 #ifndef WITH_HEADLESS
1240 extern char datatoc_splash_png[];
1241 extern int datatoc_splash_png_size;
1243 ImBuf *ibuf= IMB_ibImageFromMemory((unsigned char*)datatoc_splash_png, datatoc_splash_png_size, IB_rect, "<splash screen>");
1249 #ifdef WITH_BUILDINFO
1250 int ver_width, rev_width;
1251 char *version_str = NULL;
1252 char *revision_str = NULL;
1253 char version_buf[128];
1254 char revision_buf[128];
1255 extern char build_rev[];
1257 version_str = &version_buf[0];
1258 revision_str = &revision_buf[0];
1260 BLI_snprintf(version_str, sizeof(version_str),
1261 "%d.%02d.%d", BLENDER_VERSION/100, BLENDER_VERSION%100, BLENDER_SUBVERSION);
1262 BLI_snprintf(revision_str, sizeof(revision_str), "r%s", build_rev);
1264 BLF_size(style->widgetlabel.uifont_id, style->widgetlabel.points, U.dpi);
1265 ver_width = (int)BLF_width(style->widgetlabel.uifont_id, version_str) + 5;
1266 rev_width = (int)BLF_width(style->widgetlabel.uifont_id, revision_str) + 5;
1267 #endif //WITH_BUILDINFO
1269 block= uiBeginBlock(C, ar, "_popup", UI_EMBOSS);
1270 uiBlockSetFlag(block, UI_BLOCK_KEEP_OPEN);
1272 but= uiDefBut(block, BUT_IMAGE, 0, "", 0, 10, 501, 282, ibuf, 0.0, 0.0, 0, 0, ""); /* button owns the imbuf now */
1273 uiButSetFunc(but, wm_block_splash_close, block, NULL);
1274 uiBlockSetFunc(block, wm_block_splash_refreshmenu, block, NULL);
1276 #ifdef WITH_BUILDINFO
1277 uiDefBut(block, LABEL, 0, version_str, 494-ver_width, 282-24, ver_width, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL);
1278 uiDefBut(block, LABEL, 0, revision_str, 494-rev_width, 282-36, rev_width, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL);
1279 #endif //WITH_BUILDINFO
1281 layout= uiBlockLayout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 10, 2, 480, 110, style);
1283 uiBlockSetEmboss(block, UI_EMBOSS);
1284 /* show the splash menu (containing interaction presets), using python */
1287 menu.layout= layout;
1291 // wmWindowManager *wm= CTX_wm_manager(C);
1292 // uiItemM(layout, C, "USERPREF_MT_keyconfigs", U.keyconfigstr, ICON_NONE);
1295 uiBlockSetEmboss(block, UI_EMBOSSP);
1296 uiLayoutSetOperatorContext(layout, WM_OP_EXEC_REGION_WIN);
1298 split = uiLayoutSplit(layout, 0, 0);
1299 col = uiLayoutColumn(split, 0);
1300 uiItemL(col, "Links", ICON_NONE);
1301 uiItemStringO(col, IFACE_("Donations"), ICON_URL, "WM_OT_url_open", "url", "http://www.blender.org/blenderorg/blender-foundation/donation-payment");
1302 uiItemStringO(col, IFACE_("Credits"), ICON_URL, "WM_OT_url_open", "url", "http://www.blender.org/development/credits");
1303 uiItemStringO(col, IFACE_("Release Log"), ICON_URL, "WM_OT_url_open", "url", "http://www.blender.org/development/release-logs/blender-261");
1304 uiItemStringO(col, IFACE_("Manual"), ICON_URL, "WM_OT_url_open", "url", "http://wiki.blender.org/index.php/Doc:2.5/Manual");
1305 uiItemStringO(col, IFACE_("Blender Website"), ICON_URL, "WM_OT_url_open", "url", "http://www.blender.org");
1306 uiItemStringO(col, IFACE_("User Community"), ICON_URL, "WM_OT_url_open", "url", "http://www.blender.org/community/user-community");
1307 if(strcmp(STRINGIFY(BLENDER_VERSION_CYCLE), "release")==0) {
1308 BLI_snprintf(url, sizeof(url), "http://www.blender.org/documentation/blender_python_api_%d_%d" STRINGIFY(BLENDER_VERSION_CHAR) "_release", BLENDER_VERSION/100, BLENDER_VERSION%100);
1311 BLI_snprintf(url, sizeof(url), "http://www.blender.org/documentation/blender_python_api_%d_%d_%d", BLENDER_VERSION/100, BLENDER_VERSION%100, BLENDER_SUBVERSION);
1313 uiItemStringO(col, IFACE_("Python API Reference"), ICON_URL, "WM_OT_url_open", "url", url);
1314 uiItemL(col, "", ICON_NONE);
1316 col = uiLayoutColumn(split, 0);
1318 if(wm_resource_check_prev()) {
1319 uiItemO(col, NULL, ICON_NEW, "WM_OT_copy_prev_settings");
1323 uiItemL(col, IFACE_("Recent"), ICON_NONE);
1324 for(recent = G.recent_files.first, i=0; (i<5) && (recent); recent = recent->next, i++) {
1325 uiItemStringO(col, BLI_path_basename(recent->filepath), ICON_FILE_BLEND, "WM_OT_open_mainfile", "filepath", recent->filepath);
1329 uiItemO(col, NULL, ICON_RECOVER_LAST, "WM_OT_recover_last_session");
1330 uiItemL(col, "", ICON_NONE);
1332 uiCenteredBoundsBlock(block, 0);
1333 uiEndBlock(C, block);
1338 static int wm_splash_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *UNUSED(event))
1340 uiPupBlock(C, wm_block_create_splash, NULL);
1342 return OPERATOR_FINISHED;
1345 static void WM_OT_splash(wmOperatorType *ot)
1347 ot->name= "Splash Screen";
1348 ot->idname= "WM_OT_splash";
1349 ot->description= "Opens a blocking popup region with release info";
1351 ot->invoke= wm_splash_invoke;
1352 ot->poll= WM_operator_winactive;
1356 /* ***************** Search menu ************************* */
1357 static void operator_call_cb(struct bContext *C, void *UNUSED(arg1), void *arg2)
1359 wmOperatorType *ot= arg2;
1362 WM_operator_name_call(C, ot->idname, WM_OP_INVOKE_DEFAULT, NULL);
1365 static void operator_search_cb(const struct bContext *C, void *UNUSED(arg), const char *str, uiSearchItems *items)
1367 GHashIterator *iter= WM_operatortype_iter();
1369 for( ; !BLI_ghashIterator_isDone(iter); BLI_ghashIterator_step(iter)) {
1370 wmOperatorType *ot= BLI_ghashIterator_getValue(iter);
1372 if((ot->flag & OPTYPE_INTERNAL) && (G.f & G_DEBUG) == 0)
1375 if(BLI_strcasestr(ot->name, str)) {
1376 if(WM_operator_poll((bContext*)C, ot)) {
1378 int len= strlen(ot->name);
1380 /* display name for menu, can hold hotkey */
1381 BLI_strncpy(name, ot->name, 256);
1383 /* check for hotkey */
1385 if(WM_key_event_operator_string(C, ot->idname, WM_OP_EXEC_DEFAULT, NULL, TRUE, &name[len+1], 256-len-1))
1389 if(0==uiSearchItemAdd(items, name, ot, 0))
1394 BLI_ghashIterator_free(iter);
1397 static uiBlock *wm_block_search_menu(bContext *C, ARegion *ar, void *UNUSED(arg_op))
1399 static char search[256]= "";
1401 wmWindow *win= CTX_wm_window(C);
1405 block= uiBeginBlock(C, ar, "_popup", UI_EMBOSS);
1406 uiBlockSetFlag(block, UI_BLOCK_LOOP|UI_BLOCK_RET_1|UI_BLOCK_MOVEMOUSE_QUIT);
1408 but= uiDefSearchBut(block, search, 0, ICON_VIEWZOOM, 256, 10, 10, 9*UI_UNIT_X, UI_UNIT_Y, 0, 0, "");
1409 uiButSetSearchFunc(but, operator_search_cb, NULL, operator_call_cb, NULL);
1411 /* fake button, it holds space for search items */
1412 uiDefBut(block, LABEL, 0, "", 10, 10 - uiSearchBoxhHeight(), 9*UI_UNIT_X, uiSearchBoxhHeight(), NULL, 0, 0, 0, 0, NULL);
1414 uiPopupBoundsBlock(block, 6, 0, -UI_UNIT_Y); /* move it downwards, mouse over button */
1415 uiEndBlock(C, block);
1417 event= *(win->eventstate); /* XXX huh huh? make api call */
1418 event.type= EVT_BUT_OPEN;
1419 event.val= KM_PRESS;
1420 event.customdata= but;
1421 event.customdatafree= FALSE;
1422 wm_event_add(win, &event);
1427 static int wm_search_menu_exec(bContext *UNUSED(C), wmOperator *UNUSED(op))
1429 return OPERATOR_FINISHED;
1432 static int wm_search_menu_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1434 uiPupBlock(C, wm_block_search_menu, op);
1436 return OPERATOR_CANCELLED;
1440 static int wm_search_menu_poll(bContext *C)
1442 if(CTX_wm_window(C)==NULL) {
1446 ScrArea *sa= CTX_wm_area(C);
1448 if(sa->spacetype==SPACE_CONSOLE) return 0; // XXX - so we can use the shortcut in the console
1449 if(sa->spacetype==SPACE_TEXT) return 0; // XXX - so we can use the spacebar in the text editor
1452 Object *editob= CTX_data_edit_object(C);
1453 if(editob && editob->type==OB_FONT) return 0; // XXX - so we can use the spacebar for entering text
1459 static void WM_OT_search_menu(wmOperatorType *ot)
1461 ot->name= "Search Menu";
1462 ot->idname= "WM_OT_search_menu";
1464 ot->invoke= wm_search_menu_invoke;
1465 ot->exec= wm_search_menu_exec;
1466 ot->poll= wm_search_menu_poll;
1469 static int wm_call_menu_exec(bContext *C, wmOperator *op)
1471 char idname[BKE_ST_MAXNAME];
1472 RNA_string_get(op->ptr, "name", idname);
1474 uiPupMenuInvoke(C, idname);
1476 return OPERATOR_CANCELLED;
1479 static void WM_OT_call_menu(wmOperatorType *ot)
1481 ot->name= "Call Menu";
1482 ot->idname= "WM_OT_call_menu";
1484 ot->exec= wm_call_menu_exec;
1485 ot->poll= WM_operator_winactive;
1487 ot->flag= OPTYPE_INTERNAL;
1489 RNA_def_string(ot->srna, "name", "", BKE_ST_MAXNAME, "Name", "Name of the menu");
1492 /* ************ window / screen operator definitions ************** */
1494 /* this poll functions is needed in place of WM_operator_winactive
1495 * while it crashes on full screen */
1496 static int wm_operator_winactive_normal(bContext *C)
1498 wmWindow *win= CTX_wm_window(C);
1500 if(win==NULL || win->screen==NULL || win->screen->full != SCREENNORMAL)
1506 static void WM_OT_window_duplicate(wmOperatorType *ot)
1508 ot->name= "Duplicate Window";
1509 ot->idname= "WM_OT_window_duplicate";
1510 ot->description="Duplicate the current Blender window";
1512 ot->exec= wm_window_duplicate_exec;
1513 ot->poll= wm_operator_winactive_normal;
1516 static void WM_OT_save_homefile(wmOperatorType *ot)
1518 ot->name= "Save User Settings";
1519 ot->idname= "WM_OT_save_homefile";
1520 ot->description="Make the current file the default .blend file";
1522 ot->invoke= WM_operator_confirm;
1523 ot->exec= WM_write_homefile;
1524 ot->poll= WM_operator_winactive;
1527 static void WM_OT_read_homefile(wmOperatorType *ot)
1529 ot->name= "Reload Start-Up File";
1530 ot->idname= "WM_OT_read_homefile";
1531 ot->description="Open the default file (doesn't save the current file)";
1533 ot->invoke= WM_operator_confirm;
1534 ot->exec= WM_read_homefile_exec;
1535 /* ommit poll to run in background mode */
1538 static void WM_OT_read_factory_settings(wmOperatorType *ot)
1540 ot->name= "Load Factory Settings";
1541 ot->idname= "WM_OT_read_factory_settings";
1542 ot->description="Load default file and user preferences";
1544 ot->invoke= WM_operator_confirm;
1545 ot->exec= WM_read_homefile_exec;
1546 /* ommit poll to run in background mode */
1549 /* *************** open file **************** */
1551 static void open_set_load_ui(wmOperator *op)
1553 if(!RNA_property_is_set(op->ptr, "load_ui"))
1554 RNA_boolean_set(op->ptr, "load_ui", !(U.flag & USER_FILENOUI));
1557 static void open_set_use_scripts(wmOperator *op)
1559 if(!RNA_property_is_set(op->ptr, "use_scripts")) {
1560 /* use G_SCRIPT_AUTOEXEC rather than the userpref because this means if
1561 * the flag has been disabled from the command line, then opening
1562 * from the menu wont enable this setting. */
1563 RNA_boolean_set(op->ptr, "use_scripts", (G.f & G_SCRIPT_AUTOEXEC));
1567 static int wm_open_mainfile_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1569 const char *openname= G.main->name;
1571 if(CTX_wm_window(C) == NULL) {
1572 /* in rare cases this could happen, when trying to invoke in background
1573 * mode on load for example. Don't use poll for this because exec()
1574 * can still run without a window */
1575 BKE_report(op->reports, RPT_ERROR, "Context window not set");
1576 return OPERATOR_CANCELLED;
1579 /* if possible, get the name of the most recently used .blend file */
1580 if (G.recent_files.first) {
1581 struct RecentFile *recent = G.recent_files.first;
1582 openname = recent->filepath;
1585 RNA_string_set(op->ptr, "filepath", openname);
1586 open_set_load_ui(op);
1587 open_set_use_scripts(op);
1589 WM_event_add_fileselect(C, op);
1591 return OPERATOR_RUNNING_MODAL;
1594 static int wm_open_mainfile_exec(bContext *C, wmOperator *op)
1596 char path[FILE_MAX];
1598 RNA_string_get(op->ptr, "filepath", path);
1599 open_set_load_ui(op);
1600 open_set_use_scripts(op);
1602 if(RNA_boolean_get(op->ptr, "load_ui"))
1603 G.fileflags &= ~G_FILE_NO_UI;
1605 G.fileflags |= G_FILE_NO_UI;
1607 if(RNA_boolean_get(op->ptr, "use_scripts"))
1608 G.f |= G_SCRIPT_AUTOEXEC;
1610 G.f &= ~G_SCRIPT_AUTOEXEC;
1612 // XXX wm in context is not set correctly after WM_read_file -> crash
1613 // do it before for now, but is this correct with multiple windows?
1614 WM_event_add_notifier(C, NC_WINDOW, NULL);
1616 WM_read_file(C, path, op->reports);
1618 return OPERATOR_FINISHED;
1621 static void WM_OT_open_mainfile(wmOperatorType *ot)
1623 ot->name= "Open Blender File";
1624 ot->idname= "WM_OT_open_mainfile";
1625 ot->description="Open a Blender file";
1627 ot->invoke= wm_open_mainfile_invoke;
1628 ot->exec= wm_open_mainfile_exec;
1629 /* ommit window poll so this can work in background mode */
1631 WM_operator_properties_filesel(ot, FOLDERFILE|BLENDERFILE, FILE_BLENDER, FILE_OPENFILE, WM_FILESEL_FILEPATH);
1633 RNA_def_boolean(ot->srna, "load_ui", 1, "Load UI", "Load user interface setup in the .blend file");
1634 RNA_def_boolean(ot->srna, "use_scripts", 1, "Trusted Source", "Allow blend file execute scripts automatically, default available from system preferences");
1637 /* **************** link/append *************** */
1639 int wm_link_append_poll(bContext *C)
1641 if(WM_operator_winactive(C)) {
1642 /* linking changes active object which is pretty useful in general,
1643 but which totally confuses edit mode (i.e. it becoming not so obvious
1644 to leave from edit mode and inwalid tools in toolbar might be displayed)
1645 so disable link/append when in edit mode (sergey) */
1646 if(CTX_data_edit_object(C))
1655 static int wm_link_append_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1657 if(RNA_property_is_set(op->ptr, "filepath")) {
1658 return WM_operator_call_notest(C, op);
1661 /* XXX TODO solve where to get last linked library from */
1662 if(G.lib[0] != '\0') {
1663 RNA_string_set(op->ptr, "filepath", G.lib);
1665 else if(G.relbase_valid) {
1666 char path[FILE_MAX];
1667 BLI_strncpy(path, G.main->name, sizeof(G.main->name));
1668 BLI_parent_dir(path);
1669 RNA_string_set(op->ptr, "filepath", path);
1671 WM_event_add_fileselect(C, op);
1672 return OPERATOR_RUNNING_MODAL;
1676 static short wm_link_append_flag(wmOperator *op)
1680 if(RNA_boolean_get(op->ptr, "autoselect")) flag |= FILE_AUTOSELECT;
1681 if(RNA_boolean_get(op->ptr, "active_layer")) flag |= FILE_ACTIVELAY;
1682 if(RNA_boolean_get(op->ptr, "relative_path")) flag |= FILE_RELPATH;
1683 if(RNA_boolean_get(op->ptr, "link")) flag |= FILE_LINK;
1684 if(RNA_boolean_get(op->ptr, "instance_groups")) flag |= FILE_GROUP_INSTANCE;
1689 static int wm_link_append_exec(bContext *C, wmOperator *op)
1691 Main *bmain= CTX_data_main(C);
1692 Scene *scene= CTX_data_scene(C);
1696 char name[FILE_MAX], dir[FILE_MAX], libname[FILE_MAX], group[GROUP_MAX];
1697 int idcode, totfiles=0;
1700 RNA_string_get(op->ptr, "filename", name);
1701 RNA_string_get(op->ptr, "directory", dir);
1703 /* test if we have a valid data */
1704 if(BLO_is_a_library(dir, libname, group) == 0) {
1705 BKE_report(op->reports, RPT_ERROR, "Not a library");
1706 return OPERATOR_CANCELLED;
1708 else if(group[0] == 0) {
1709 BKE_report(op->reports, RPT_ERROR, "Nothing indicated");
1710 return OPERATOR_CANCELLED;
1712 else if(BLI_path_cmp(bmain->name, libname) == 0) {
1713 BKE_report(op->reports, RPT_ERROR, "Cannot use current file as library");
1714 return OPERATOR_CANCELLED;
1717 /* check if something is indicated for append/link */
1718 prop = RNA_struct_find_property(op->ptr, "files");
1720 totfiles= RNA_property_collection_length(op->ptr, prop);
1722 if(name[0] == '\0') {
1723 BKE_report(op->reports, RPT_ERROR, "Nothing indicated");
1724 return OPERATOR_CANCELLED;
1728 else if(name[0] == '\0') {
1729 BKE_report(op->reports, RPT_ERROR, "Nothing indicated");
1730 return OPERATOR_CANCELLED;
1733 bh = BLO_blendhandle_from_file(libname, op->reports);
1736 /* unlikely since we just browsed it, but possible
1737 * error reports will have been made by BLO_blendhandle_from_file() */
1738 return OPERATOR_CANCELLED;
1742 /* from here down, no error returns */
1744 idcode = BKE_idcode_from_name(group);
1746 /* now we have or selected, or an indicated file */
1747 if(RNA_boolean_get(op->ptr, "autoselect"))
1748 scene_deselect_all(scene);
1751 flag = wm_link_append_flag(op);
1753 /* sanity checks for flag */
1754 if(scene->id.lib && (flag & FILE_GROUP_INSTANCE)) {
1755 /* TODO, user never gets this message */
1756 BKE_reportf(op->reports, RPT_WARNING, "Scene '%s' is linked, group instance disabled", scene->id.name+2);
1757 flag &= ~FILE_GROUP_INSTANCE;
1761 /* tag everything, all untagged data can be made local
1762 * its also generally useful to know what is new
1764 * take extra care flag_all_listbases_ids(LIB_LINK_TAG, 0) is called after! */
1765 flag_all_listbases_ids(LIB_PRE_EXISTING, 1);
1767 /* here appending/linking starts */
1768 mainl = BLO_library_append_begin(bmain, &bh, libname);
1770 BLO_library_append_named_part_ex(C, mainl, &bh, name, idcode, flag);
1773 RNA_BEGIN(op->ptr, itemptr, "files") {
1774 RNA_string_get(&itemptr, "name", name);
1775 BLO_library_append_named_part_ex(C, mainl, &bh, name, idcode, flag);
1779 BLO_library_append_end(C, mainl, &bh, idcode, flag);
1781 /* mark all library linked objects to be updated */
1782 recalc_all_library_objects(bmain);
1784 /* append, rather than linking */
1785 if((flag & FILE_LINK)==0) {
1786 Library *lib= BLI_findstring(&bmain->library, libname, offsetof(Library, filepath));
1787 if(lib) BKE_library_make_local(bmain, lib, 1);
1788 else BLI_assert(!"cant find name of just added library!");
1791 /* important we unset, otherwise these object wont
1792 * link into other scenes from this blend file */
1793 flag_all_listbases_ids(LIB_PRE_EXISTING, 0);
1795 /* recreate dependency graph to include new objects */
1796 DAG_scene_sort(bmain, scene);
1797 DAG_ids_flush_update(bmain, 0);
1799 BLO_blendhandle_close(bh);
1801 /* XXX TODO: align G.lib with other directory storage (like last opened image etc...) */
1802 BLI_strncpy(G.lib, dir, FILE_MAX);
1804 WM_event_add_notifier(C, NC_WINDOW, NULL);
1806 return OPERATOR_FINISHED;
1809 static void WM_OT_link_append(wmOperatorType *ot)
1811 ot->name= "Link/Append from Library";
1812 ot->idname= "WM_OT_link_append";
1813 ot->description= "Link or Append from a Library .blend file";
1815 ot->invoke= wm_link_append_invoke;
1816 ot->exec= wm_link_append_exec;
1817 ot->poll= wm_link_append_poll;
1819 ot->flag |= OPTYPE_UNDO;
1821 WM_operator_properties_filesel(ot, FOLDERFILE|BLENDERFILE, FILE_LOADLIB, FILE_OPENFILE, WM_FILESEL_FILEPATH|WM_FILESEL_DIRECTORY|WM_FILESEL_FILENAME| WM_FILESEL_RELPATH|WM_FILESEL_FILES);
1823 RNA_def_boolean(ot->srna, "link", 1, "Link", "Link the objects or datablocks rather than appending");
1824 RNA_def_boolean(ot->srna, "autoselect", 1, "Select", "Select the linked objects");
1825 RNA_def_boolean(ot->srna, "active_layer", 1, "Active Layer", "Put the linked objects on the active layer");
1826 RNA_def_boolean(ot->srna, "instance_groups", 1, "Instance Groups", "Create instances for each group as a DupliGroup");
1829 /* *************** recover last session **************** */
1831 static int wm_recover_last_session_exec(bContext *C, wmOperator *op)
1833 char filename[FILE_MAX];
1835 G.fileflags |= G_FILE_RECOVER;
1837 // XXX wm in context is not set correctly after WM_read_file -> crash
1838 // do it before for now, but is this correct with multiple windows?
1839 WM_event_add_notifier(C, NC_WINDOW, NULL);
1842 BLI_make_file_string("/", filename, BLI_temporary_dir(), "quit.blend");
1843 WM_read_file(C, filename, op->reports);
1845 G.fileflags &= ~G_FILE_RECOVER;
1846 return OPERATOR_FINISHED;
1849 static void WM_OT_recover_last_session(wmOperatorType *ot)
1851 ot->name= "Recover Last Session";
1852 ot->idname= "WM_OT_recover_last_session";
1853 ot->description="Open the last closed file (\"quit.blend\")";
1855 ot->exec= wm_recover_last_session_exec;
1856 ot->poll= WM_operator_winactive;
1859 /* *************** recover auto save **************** */
1861 static int wm_recover_auto_save_exec(bContext *C, wmOperator *op)
1863 char path[FILE_MAX];
1865 RNA_string_get(op->ptr, "filepath", path);
1867 G.fileflags |= G_FILE_RECOVER;
1869 // XXX wm in context is not set correctly after WM_read_file -> crash
1870 // do it before for now, but is this correct with multiple windows?
1871 WM_event_add_notifier(C, NC_WINDOW, NULL);
1874 WM_read_file(C, path, op->reports);
1876 G.fileflags &= ~G_FILE_RECOVER;
1878 return OPERATOR_FINISHED;
1881 static int wm_recover_auto_save_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1883 char filename[FILE_MAX];
1885 wm_autosave_location(filename);
1886 RNA_string_set(op->ptr, "filepath", filename);
1887 WM_event_add_fileselect(C, op);
1889 return OPERATOR_RUNNING_MODAL;
1892 static void WM_OT_recover_auto_save(wmOperatorType *ot)
1894 ot->name= "Recover Auto Save";
1895 ot->idname= "WM_OT_recover_auto_save";
1896 ot->description="Open an automatically saved file to recover it";
1898 ot->exec= wm_recover_auto_save_exec;
1899 ot->invoke= wm_recover_auto_save_invoke;
1900 ot->poll= WM_operator_winactive;
1902 WM_operator_properties_filesel(ot, BLENDERFILE, FILE_BLENDER, FILE_OPENFILE, WM_FILESEL_FILEPATH);
1905 /* *************** save file as **************** */
1907 static void untitled(char *name)
1909 if(G.save_over == 0 && strlen(name) < FILE_MAX-16) {
1910 char *c= BLI_last_slash(name);
1913 strcpy(&c[1], "untitled.blend");
1915 strcpy(name, "untitled.blend");
1919 static void save_set_compress(wmOperator *op)
1921 if(!RNA_property_is_set(op->ptr, "compress")) {
1922 if(G.save_over) /* keep flag for existing file */
1923 RNA_boolean_set(op->ptr, "compress", G.fileflags & G_FILE_COMPRESS);
1924 else /* use userdef for new file */
1925 RNA_boolean_set(op->ptr, "compress", U.flag & USER_FILECOMPRESS);
1929 static int wm_save_as_mainfile_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1931 char name[FILE_MAX];
1933 save_set_compress(op);
1935 /* if not saved before, get the name of the most recently used .blend file */
1936 if(G.main->name[0]==0 && G.recent_files.first) {
1937 struct RecentFile *recent = G.recent_files.first;
1938 BLI_strncpy(name, recent->filepath, FILE_MAX);
1941 BLI_strncpy(name, G.main->name, FILE_MAX);
1944 RNA_string_set(op->ptr, "filepath", name);
1946 WM_event_add_fileselect(C, op);
1948 return OPERATOR_RUNNING_MODAL;
1951 /* function used for WM_OT_save_mainfile too */
1952 static int wm_save_as_mainfile_exec(bContext *C, wmOperator *op)
1954 char path[FILE_MAX];
1958 save_set_compress(op);
1960 if(RNA_property_is_set(op->ptr, "filepath"))
1961 RNA_string_get(op->ptr, "filepath", path);
1963 BLI_strncpy(path, G.main->name, FILE_MAX);
1967 if(RNA_property_is_set(op->ptr, "copy"))
1968 copy = RNA_boolean_get(op->ptr, "copy");
1970 fileflags= G.fileflags;
1972 /* set compression flag */
1973 if(RNA_boolean_get(op->ptr, "compress")) fileflags |= G_FILE_COMPRESS;
1974 else fileflags &= ~G_FILE_COMPRESS;
1975 if(RNA_boolean_get(op->ptr, "relative_remap")) fileflags |= G_FILE_RELATIVE_REMAP;
1976 else fileflags &= ~G_FILE_RELATIVE_REMAP;
1977 #ifdef USE_BMESH_SAVE_AS_COMPAT
1978 if(RNA_boolean_get(op->ptr, "use_mesh_compat")) fileflags |= G_FILE_MESH_COMPAT;
1979 else fileflags &= ~G_FILE_MESH_COMPAT;
1982 if ( WM_write_file(C, path, fileflags, op->reports, copy) != 0)
1983 return OPERATOR_CANCELLED;
1985 WM_event_add_notifier(C, NC_WM|ND_FILESAVE, NULL);
1987 return OPERATOR_FINISHED;
1990 /* function used for WM_OT_save_mainfile too */
1991 static int blend_save_check(bContext *UNUSED(C), wmOperator *op)
1993 char filepath[FILE_MAX];
1994 RNA_string_get(op->ptr, "filepath", filepath);
1995 if(!BLO_has_bfile_extension(filepath)) {
1996 /* some users would prefer BLI_replace_extension(),
1997 * we keep getting knit-picking bug reports about this - campbell */
1998 BLI_ensure_extension(filepath, FILE_MAX, ".blend");
1999 RNA_string_set(op->ptr, "filepath", filepath);
2005 static void WM_OT_save_as_mainfile(wmOperatorType *ot)
2007 ot->name= "Save As Blender File";
2008 ot->idname= "WM_OT_save_as_mainfile";
2009 ot->description="Save the current file in the desired location";
2011 ot->invoke= wm_save_as_mainfile_invoke;
2012 ot->exec= wm_save_as_mainfile_exec;
2013 ot->check= blend_save_check;
2014 /* ommit window poll so this can work in background mode */
2016 WM_operator_properties_filesel(ot, FOLDERFILE|BLENDERFILE, FILE_BLENDER, FILE_SAVE, WM_FILESEL_FILEPATH);
2017 RNA_def_boolean(ot->srna, "compress", 0, "Compress", "Write compressed .blend file");
2018 RNA_def_boolean(ot->srna, "relative_remap", 1, "Remap Relative", "Remap relative paths when saving in a different directory");
2019 RNA_def_boolean(ot->srna, "copy", 0, "Save Copy", "Save a copy of the actual working state but does not make saved file active");
2020 #ifdef USE_BMESH_SAVE_AS_COMPAT
2021 RNA_def_boolean(ot->srna, "use_mesh_compat", 0, "Legacy Mesh Format", "Save using legacy mesh format (no ngons)");
2025 /* *************** save file directly ******** */
2027 static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
2029 char name[FILE_MAX];
2030 int check_existing=1;
2033 /* cancel if no active window */
2034 if (CTX_wm_window(C) == NULL)
2035 return OPERATOR_CANCELLED;
2037 save_set_compress(op);
2039 /* if not saved before, get the name of the most recently used .blend file */
2040 if(G.main->name[0]==0 && G.recent_files.first) {
2041 struct RecentFile *recent = G.recent_files.first;
2042 BLI_strncpy(name, recent->filepath, FILE_MAX);
2045 BLI_strncpy(name, G.main->name, FILE_MAX);
2049 RNA_string_set(op->ptr, "filepath", name);
2051 if (RNA_struct_find_property(op->ptr, "check_existing"))
2052 if (RNA_boolean_get(op->ptr, "check_existing")==0)
2056 if (check_existing && BLI_exists(name)) {
2057 uiPupMenuSaveOver(C, op, name);
2058 ret= OPERATOR_RUNNING_MODAL;
2061 ret= wm_save_as_mainfile_exec(C, op);
2065 WM_event_add_fileselect(C, op);
2066 ret= OPERATOR_RUNNING_MODAL;
2072 static void WM_OT_save_mainfile(wmOperatorType *ot)
2074 ot->name= "Save Blender File";
2075 ot->idname= "WM_OT_save_mainfile";
2076 ot->description="Save the current Blender file";
2078 ot->invoke= wm_save_mainfile_invoke;
2079 ot->exec= wm_save_as_mainfile_exec;
2080 ot->check= blend_save_check;
2081 /* ommit window poll so this can work in background mode */
2083 WM_operator_properties_filesel(ot, FOLDERFILE|BLENDERFILE, FILE_BLENDER, FILE_SAVE, WM_FILESEL_FILEPATH);
2084 RNA_def_boolean(ot->srna, "compress", 0, "Compress", "Write compressed .blend file");
2085 RNA_def_boolean(ot->srna, "relative_remap", 0, "Remap Relative", "Remap relative paths when saving in a different directory");
2088 /* XXX: move these collada operators to a more appropriate place */
2091 #include "../../collada/collada.h"
2093 static int wm_collada_export_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
2095 if(!RNA_property_is_set(op->ptr, "filepath")) {
2096 char filepath[FILE_MAX];
2097 BLI_strncpy(filepath, G.main->name, sizeof(filepath));
2098 BLI_replace_extension(filepath, sizeof(filepath), ".dae");
2099 RNA_string_set(op->ptr, "filepath", filepath);
2102 WM_event_add_fileselect(C, op);
2104 return OPERATOR_RUNNING_MODAL;
2107 /* function used for WM_OT_save_mainfile too */
2108 static int wm_collada_export_exec(bContext *C, wmOperator *op)
2110 char filename[FILE_MAX];
2113 if(!RNA_property_is_set(op->ptr, "filepath")) {
2114 BKE_report(op->reports, RPT_ERROR, "No filename given");
2115 return OPERATOR_CANCELLED;
2118 RNA_string_get(op->ptr, "filepath", filename);
2119 selected = RNA_boolean_get(op->ptr, "selected");
2120 if(collada_export(CTX_data_scene(C), filename, selected)) {
2121 return OPERATOR_FINISHED;
2124 return OPERATOR_CANCELLED;
2128 static void WM_OT_collada_export(wmOperatorType *ot)
2130 ot->name= "Export COLLADA";
2131 ot->idname= "WM_OT_collada_export";
2133 ot->invoke= wm_collada_export_invoke;
2134 ot->exec= wm_collada_export_exec;
2135 ot->poll= WM_operator_winactive;
2137 WM_operator_properties_filesel(ot, FOLDERFILE|COLLADAFILE, FILE_BLENDER, FILE_SAVE, WM_FILESEL_FILEPATH);
2138 RNA_def_boolean(ot->srna, "selected", 0, "Export only selected",
2139 "Export only selected elements");
2142 /* function used for WM_OT_save_mainfile too */
2143 static int wm_collada_import_exec(bContext *C, wmOperator *op)
2145 char filename[FILE_MAX];
2147 if(!RNA_property_is_set(op->ptr, "filepath")) {
2148 BKE_report(op->reports, RPT_ERROR, "No filename given");
2149 return OPERATOR_CANCELLED;
2152 RNA_string_get(op->ptr, "filepath", filename);
2153 if(collada_import(C, filename)) return OPERATOR_FINISHED;
2155 BKE_report(op->reports, RPT_ERROR, "Errors found during parsing COLLADA document. Please see console for error log.");
2157 return OPERATOR_FINISHED;
2160 static void WM_OT_collada_import(wmOperatorType *ot)
2162 ot->name= "Import COLLADA";
2163 ot->idname= "WM_OT_collada_import";
2165 ot->invoke= WM_operator_filesel;
2166 ot->exec= wm_collada_import_exec;
2167 ot->poll= WM_operator_winactive;
2169 WM_operator_properties_filesel(ot, FOLDERFILE|COLLADAFILE, FILE_BLENDER, FILE_OPENFILE, WM_FILESEL_FILEPATH);
2175 /* *********************** */
2177 static void WM_OT_window_fullscreen_toggle(wmOperatorType *ot)
2179 ot->name= "Toggle Fullscreen";
2180 ot->idname= "WM_OT_window_fullscreen_toggle";
2181 ot->description="Toggle the current window fullscreen";
2183 ot->exec= wm_window_fullscreen_toggle_exec;
2184 ot->poll= WM_operator_winactive;
2187 static int wm_exit_blender_op(bContext *C, wmOperator *op)
2189 WM_operator_free(op);
2193 return OPERATOR_FINISHED;
2196 static void WM_OT_quit_blender(wmOperatorType *ot)
2198 ot->name= "Quit Blender";
2199 ot->idname= "WM_OT_quit_blender";
2200 ot->description= "Quit Blender";
2202 ot->invoke= WM_operator_confirm;
2203 ot->exec= wm_exit_blender_op;
2204 ot->poll= WM_operator_winactive;
2207 /* *********************** */
2211 static int wm_console_toggle_op(bContext *UNUSED(C), wmOperator *UNUSED(op))
2213 GHOST_toggleConsole(2);
2214 return OPERATOR_FINISHED;
2217 static void WM_OT_console_toggle(wmOperatorType *ot)
2219 ot->name= "Toggle System Console";
2220 ot->idname= "WM_OT_console_toggle";
2221 ot->description= "Toggle System Console";
2223 ot->exec= wm_console_toggle_op;
2224 ot->poll= WM_operator_winactive;
2229 /* ************ default paint cursors, draw always around cursor *********** */
2231 - returns handler to free
2232 - poll(bContext): returns 1 if draw should happen
2233 - draw(bContext): drawing callback for paint cursor
2236 void *WM_paint_cursor_activate(wmWindowManager *wm, int (*poll)(bContext *C),
2237 wmPaintCursorDraw draw, void *customdata)
2239 wmPaintCursor *pc= MEM_callocN(sizeof(wmPaintCursor), "paint cursor");
2241 BLI_addtail(&wm->paintcursors, pc);
2243 pc->customdata = customdata;
2250 void WM_paint_cursor_end(wmWindowManager *wm, void *handle)
2254 for(pc= wm->paintcursors.first; pc; pc= pc->next) {
2255 if(pc == (wmPaintCursor *)handle) {
2256 BLI_remlink(&wm->paintcursors, pc);
2263 /* ************ window gesture operator-callback definitions ************** */
2265 * These are default callbacks for use in operators requiring gesture input
2268 /* **************** Border gesture *************** */
2270 /* Border gesture has two types:
2271 1) WM_GESTURE_CROSS_RECT: starts a cross, on mouse click it changes to border
2272 2) WM_GESTURE_RECT: starts immediate as a border, on mouse click or release it ends
2274 It stores 4 values (xmin, xmax, ymin, ymax) and event it ended with (event_type)
2277 static int border_apply_rect(wmOperator *op)
2279 wmGesture *gesture= op->customdata;
2280 rcti *rect= gesture->customdata;
2282 if(rect->xmin==rect->xmax || rect->ymin==rect->ymax)
2286 /* operator arguments and storage. */
2287 RNA_int_set(op->ptr, "xmin", MIN2(rect->xmin, rect->xmax) );
2288 RNA_int_set(op->ptr, "ymin", MIN2(rect->ymin, rect->ymax) );
2289 RNA_int_set(op->ptr, "xmax", MAX2(rect->xmin, rect->xmax) );
2290 RNA_int_set(op->ptr, "ymax", MAX2(rect->ymin, rect->ymax) );
2295 static int border_apply(bContext *C, wmOperator *op, int gesture_mode)
2297 if (!border_apply_rect(op))
2300 /* XXX weak; border should be configured for this without reading event types */
2301 if( RNA_struct_find_property(op->ptr, "gesture_mode") )
2302 RNA_int_set(op->ptr, "gesture_mode", gesture_mode);
2304 op->type->exec(C, op);
2308 static void wm_gesture_end(bContext *C, wmOperator *op)
2310 wmGesture *gesture= op->customdata;
2312 WM_gesture_end(C, gesture); /* frees gesture itself, and unregisters from window */
2313 op->customdata= NULL;
2315 ED_area_tag_redraw(CTX_wm_area(C));
2317 if( RNA_struct_find_property(op->ptr, "cursor") )
2318 WM_cursor_restore(CTX_wm_window(C));
2321 int WM_border_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
2323 if(ISTWEAK(event->type))
2324 op->customdata= WM_gesture_new(C, event, WM_GESTURE_RECT);
2326 op->customdata= WM_gesture_new(C, event, WM_GESTURE_CROSS_RECT);
2328 /* add modal handler */
2329 WM_event_add_modal_handler(C, op);
2331 wm_gesture_tag_redraw(C);
2333 return OPERATOR_RUNNING_MODAL;
2336 int WM_border_select_modal(bContext *C, wmOperator *op, wmEvent *event)
2338 wmGesture *gesture= op->customdata;
2339 rcti *rect= gesture->customdata;
2342 if(event->type== MOUSEMOVE) {
2343 wm_subwindow_getorigin(CTX_wm_window(C), gesture->swinid, &sx, &sy);
2345 if(gesture->type==WM_GESTURE_CROSS_RECT && gesture->mode==0) {
2346 rect->xmin= rect->xmax= event->x - sx;
2347 rect->ymin= rect->ymax= event->y - sy;
2350 rect->xmax= event->x - sx;
2351 rect->ymax= event->y - sy;
2353 border_apply_rect(op);
2355 wm_gesture_tag_redraw(C);
2357 else if (event->type==EVT_MODAL_MAP) {
2358 switch (event->val) {
2359 case GESTURE_MODAL_BEGIN:
2360 if(gesture->type==WM_GESTURE_CROSS_RECT && gesture->mode==0) {
2362 wm_gesture_tag_redraw(C);
2365 case GESTURE_MODAL_SELECT:
2366 case GESTURE_MODAL_DESELECT:
2367 case GESTURE_MODAL_IN:
2368 case GESTURE_MODAL_OUT:
2369 if(border_apply(C, op, event->val)) {
2370 wm_gesture_end(C, op);
2371 return OPERATOR_FINISHED;
2373 wm_gesture_end(C, op);
2374 return OPERATOR_CANCELLED;
2377 case GESTURE_MODAL_CANCEL:
2378 wm_gesture_end(C, op);
2379 return OPERATOR_CANCELLED;
2383 // // Allow view navigation???
2385 // return OPERATOR_PASS_THROUGH;
2388 return OPERATOR_RUNNING_MODAL;
2391 int WM_border_select_cancel(bContext *C, wmOperator *op)
2393 wm_gesture_end(C, op);
2395 return OPERATOR_CANCELLED;
2398 /* **************** circle gesture *************** */
2399 /* works now only for selection or modal paint stuff, calls exec while hold mouse, exit on release */
2401 #ifdef GESTURE_MEMORY
2402 int circle_select_size= 25; // XXX - need some operator memory thing\!
2405 int WM_gesture_circle_invoke(bContext *C, wmOperator *op, wmEvent *event)
2407 op->customdata= WM_gesture_new(C, event, WM_GESTURE_CIRCLE);
2409 /* add modal handler */
2410 WM_event_add_modal_handler(C, op);
2412 wm_gesture_tag_redraw(C);
2414 return OPERATOR_RUNNING_MODAL;
2417 static void gesture_circle_apply(bContext *C, wmOperator *op)
2419 wmGesture *gesture= op->customdata;
2420 rcti *rect= gesture->customdata;
2422 if(RNA_int_get(op->ptr, "gesture_mode")==GESTURE_MODAL_NOP)
2425 /* operator arguments and storage. */
2426 RNA_int_set(op->ptr, "x", rect->xmin);
2427 RNA_int_set(op->ptr, "y", rect->ymin);
2428 RNA_int_set(op->ptr, "radius", rect->xmax);
2431 op->type->exec(C, op);
2432 #ifdef GESTURE_MEMORY
2433 circle_select_size= rect->xmax;
2437 int WM_gesture_circle_modal(bContext *C, wmOperator *op, wmEvent *event)
2439 wmGesture *gesture= op->customdata;
2440 rcti *rect= gesture->customdata;
2443 if(event->type== MOUSEMOVE) {
2444 wm_subwindow_getorigin(CTX_wm_window(C), gesture->swinid, &sx, &sy);
2446 rect->xmin= event->x - sx;
2447 rect->ymin= event->y - sy;
2449 wm_gesture_tag_redraw(C);
2452 gesture_circle_apply(C, op);
2454 else if (event->type==EVT_MODAL_MAP) {
2455 switch (event->val) {
2456 case GESTURE_MODAL_CIRCLE_ADD:
2457 rect->xmax += 2 + rect->xmax/10;
2458 wm_gesture_tag_redraw(C);
2460 case GESTURE_MODAL_CIRCLE_SUB:
2461 rect->xmax -= 2 + rect->xmax/10;
2462 if(rect->xmax < 1) rect->xmax= 1;
2463 wm_gesture_tag_redraw(C);
2465 case GESTURE_MODAL_SELECT:
2466 case GESTURE_MODAL_DESELECT:
2467 case GESTURE_MODAL_NOP:
2468 if(RNA_struct_find_property(op->ptr, "gesture_mode"))
2469 RNA_int_set(op->ptr, "gesture_mode", event->val);
2471 if(event->val != GESTURE_MODAL_NOP) {
2472 /* apply first click */
2473 gesture_circle_apply(C, op);
2475 wm_gesture_tag_redraw(C);
2479 case GESTURE_MODAL_CANCEL:
2480 case GESTURE_MODAL_CONFIRM:
2481 wm_gesture_end(C, op);
2482 return OPERATOR_FINISHED; /* use finish or we dont get an undo */
2485 // // Allow view navigation???
2487 // return OPERATOR_PASS_THROUGH;
2490 return OPERATOR_RUNNING_MODAL;
2493 int WM_gesture_circle_cancel(bContext *C, wmOperator *op)
2495 wm_gesture_end(C, op);
2497 return OPERATOR_CANCELLED;
2501 /* template to copy from */
2502 void WM_OT_circle_gesture(wmOperatorType *ot)
2504 ot->name= "Circle Gesture";
2505 ot->idname= "WM_OT_circle_gesture";
2506 ot->description="Enter rotate mode with a circular gesture";
2508 ot->invoke= WM_gesture_circle_invoke;
2509 ot->modal= WM_gesture_circle_modal;
2511 ot->poll= WM_operator_winactive;
2513 RNA_def_property(ot->srna, "x", PROP_INT, PROP_NONE);
2514 RNA_def_property(ot->srna, "y", PROP_INT, PROP_NONE);
2515 RNA_def_property(ot->srna, "radius", PROP_INT, PROP_NONE);
2520 /* **************** Tweak gesture *************** */
2522 static void tweak_gesture_modal(bContext *C, wmEvent *event)
2524 wmWindow *window= CTX_wm_window(C);
2525 wmGesture *gesture= window->tweak;
2526 rcti *rect= gesture->customdata;
2529 switch(event->type) {
2531 case INBETWEEN_MOUSEMOVE:
2533 wm_subwindow_getorigin(window, gesture->swinid, &sx, &sy);
2535 rect->xmax= event->x - sx;
2536 rect->ymax= event->y - sy;
2538 if((val= wm_gesture_evaluate(gesture))) {
2541 tevent= *(window->eventstate);
2542 if(gesture->event_type==LEFTMOUSE)
2543 tevent.type= EVT_TWEAK_L;
2544 else if(gesture->event_type==RIGHTMOUSE)
2545 tevent.type= EVT_TWEAK_R;
2547 tevent.type= EVT_TWEAK_M;
2550 wm_event_add(window, &tevent);
2552 WM_gesture_end(C, gesture); /* frees gesture itself, and unregisters from window */
2560 if(gesture->event_type==event->type) {
2561 WM_gesture_end(C, gesture);
2563 /* when tweak fails we should give the other keymap entries a chance */
2564 event->val= KM_RELEASE;
2568 if(!ISTIMER(event->type)) {
2569 WM_gesture_end(C, gesture);
2575 /* standard tweak, called after window handlers passed on event */
2576 void wm_tweakevent_test(bContext *C, wmEvent *event, int action)
2578 wmWindow *win= CTX_wm_window(C);
2580 if(win->tweak==NULL) {
2581 if(CTX_wm_region(C)) {
2582 if(event->val==KM_PRESS) {
2583 if( ELEM3(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE) )
2584 win->tweak= WM_gesture_new(C, event, WM_GESTURE_TWEAK);
2589 /* no tweaks if event was handled */
2590 if((action & WM_HANDLER_BREAK)) {
2591 WM_gesture_end(C, win->tweak);
2594 tweak_gesture_modal(C, event);
2598 /* *********************** lasso gesture ****************** */
2600 int WM_gesture_lasso_invoke(bContext *C, wmOperator *op, wmEvent *event)
2602 op->customdata= WM_gesture_new(C, event, WM_GESTURE_LASSO);
2604 /* add modal handler */
2605 WM_event_add_modal_handler(C, op);
2607 wm_gesture_tag_redraw(C);
2609 if( RNA_struct_find_property(op->ptr, "cursor") )
2610 WM_cursor_modal(CTX_wm_window(C), RNA_int_get(op->ptr, "cursor"));
2612 return OPERATOR_RUNNING_MODAL;
2615 int WM_gesture_lines_invoke(bContext *C, wmOperator *op, wmEvent *event)
2617 op->customdata= WM_gesture_new(C, event, WM_GESTURE_LINES);
2619 /* add modal handler */
2620 WM_event_add_modal_handler(C, op);
2622 wm_gesture_tag_redraw(C);
2624 if( RNA_struct_find_property(op->ptr, "cursor") )
2625 WM_cursor_modal(CTX_wm_window(C), RNA_int_get(op->ptr, "cursor"));
2627 return OPERATOR_RUNNING_MODAL;
2631 static void gesture_lasso_apply(bContext *C, wmOperator *op)
2633 wmGesture *gesture= op->customdata;
2637 short *lasso= gesture->customdata;
2639 /* operator storage as path. */
2641 for(i=0; i<gesture->points; i++, lasso+=2) {
2644 RNA_collection_add(op->ptr, "path", &itemptr);
2645 RNA_float_set_array(&itemptr, "loc", loc);
2648 wm_gesture_end(C, op);
2651 op->type->exec(C, op);
2654 int WM_gesture_lasso_modal(bContext *C, wmOperator *op, wmEvent *event)
2656 wmGesture *gesture= op->customdata;
2659 switch(event->type) {
2661 case INBETWEEN_MOUSEMOVE:
2663 wm_gesture_tag_redraw(C);
2665 wm_subwindow_getorigin(CTX_wm_window(C), gesture->swinid, &sx, &sy);
2667 if(gesture->points == gesture->size) {
2668 short *old_lasso = gesture->customdata;
2669 gesture->customdata= MEM_callocN(2*sizeof(short)*(gesture->size + WM_LASSO_MIN_POINTS), "lasso points");
2670 memcpy(gesture->customdata, old_lasso, 2*sizeof(short)*gesture->size);
2671 gesture->size = gesture->size + WM_LASSO_MIN_POINTS;
2672 MEM_freeN(old_lasso);
2673 // printf("realloc\n");
2678 short *lasso= gesture->customdata;
2680 lasso += (2 * gesture->points - 2);
2681 x = (event->x - sx - lasso[0]);
2682 y = (event->y - sy - lasso[1]);
2684 /* make a simple distance check to get a smoother lasso
2685 add only when at least 2 pixels between this and previous location */
2688 lasso[0] = event->x - sx;
2689 lasso[1] = event->y - sy;
2698 if(event->val==KM_RELEASE) { /* key release */
2699 gesture_lasso_apply(C, op);
2700 return OPERATOR_FINISHED;
2704 wm_gesture_end(C, op);
2705 return OPERATOR_CANCELLED;
2707 return OPERATOR_RUNNING_MODAL;
2710 int WM_gesture_lines_modal(bContext *C, wmOperator *op, wmEvent *event)
2712 return WM_gesture_lasso_modal(C, op, event);
2715 int WM_gesture_lasso_cancel(bContext *C, wmOperator *op)
2717 wm_gesture_end(C, op);
2719 return OPERATOR_CANCELLED;
2722 int WM_gesture_lines_cancel(bContext *C, wmOperator *op)
2724 wm_gesture_end(C, op);
2726 return OPERATOR_CANCELLED;
2730 /* template to copy from */
2732 static int gesture_lasso_exec(bContext *C, wmOperator *op)
2734 RNA_BEGIN(op->ptr, itemptr, "path") {
2737 RNA_float_get_array(&itemptr, "loc", loc);
2738 printf("Location: %f %f\n", loc[0], loc[1]);
2742 return OPERATOR_FINISHED;
2745 void WM_OT_lasso_gesture(wmOperatorType *ot)
2749 ot->name= "Lasso Gesture";
2750 ot->idname= "WM_OT_lasso_gesture";
2751 ot->description="Select objects within the lasso as you move the pointer";
2753 ot->invoke= WM_gesture_lasso_invoke;
2754 ot->modal= WM_gesture_lasso_modal;
2755 ot->exec= gesture_lasso_exec;
2757 ot->poll= WM_operator_winactive;
2759 prop= RNA_def_property(ot->srna, "path", PROP_COLLECTION, PROP_NONE);
2760 RNA_def_property_struct_runtime(prop, &RNA_OperatorMousePath);
2764 /* *********************** straight line gesture ****************** */
2766 static int straightline_apply(bContext *C, wmOperator *op)
2768 wmGesture *gesture= op->customdata;
2769 rcti *rect= gesture->customdata;
2771 if(rect->xmin==rect->xmax && rect->ymin==rect->ymax)
2774 /* operator arguments and storage. */
2775 RNA_int_set(op->ptr, "xstart", rect->xmin);
2776 RNA_int_set(op->ptr, "ystart", rect->ymin);
2777 RNA_int_set(op->ptr, "xend", rect->xmax);
2778 RNA_int_set(op->ptr, "yend", rect->ymax);
2781 op->type->exec(C, op);
2787 int WM_gesture_straightline_invoke(bContext *C, wmOperator *op, wmEvent *event)
2789 op->customdata= WM_gesture_new(C, event, WM_GESTURE_STRAIGHTLINE);
2791 /* add modal handler */
2792 WM_event_add_modal_handler(C, op);
2794 wm_gesture_tag_redraw(C);
2796 if( RNA_struct_find_property(op->ptr, "cursor") )
2797 WM_cursor_modal(CTX_wm_window(C), RNA_int_get(op->ptr, "cursor"));
2799 return OPERATOR_RUNNING_MODAL;
2802 int WM_gesture_straightline_modal(bContext *C, wmOperator *op, wmEvent *event)
2804 wmGesture *gesture= op->customdata;
2805 rcti *rect= gesture->customdata;
2808 if(event->type== MOUSEMOVE) {
2809 wm_subwindow_getorigin(CTX_wm_window(C), gesture->swinid, &sx, &sy);
2811 if(gesture->mode==0) {
2812 rect->xmin= rect->xmax= event->x - sx;
2813 rect->ymin= rect->ymax= event->y - sy;
2816 rect->xmax= event->x - sx;
2817 rect->ymax= event->y - sy;
2818 straightline_apply(C, op);
2821 wm_gesture_tag_redraw(C);
2823 else if (event->type==EVT_MODAL_MAP) {
2824 switch (event->val) {
2825 case GESTURE_MODAL_BEGIN:
2826 if(gesture->mode==0) {
2828 wm_gesture_tag_redraw(C);
2831 case GESTURE_MODAL_SELECT:
2832 if(straightline_apply(C, op)) {
2833 wm_gesture_end(C, op);
2834 return OPERATOR_FINISHED;
2836 wm_gesture_end(C, op);
2837 return OPERATOR_CANCELLED;
2840 case GESTURE_MODAL_CANCEL:
2841 wm_gesture_end(C, op);
2842 return OPERATOR_CANCELLED;
2847 return OPERATOR_RUNNING_MODAL;
2850 int WM_gesture_straightline_cancel(bContext *C, wmOperator *op)
2852 wm_gesture_end(C, op);
2854 return OPERATOR_CANCELLED;
2858 /* template to copy from */
2859 void WM_OT_straightline_gesture(wmOperatorType *ot)
2863 ot->name= "Straight Line Gesture";
2864 ot->idname= "WM_OT_straightline_gesture";
2865 ot->description="Draw a straight line as you move the pointer";
2867 ot->invoke= WM_gesture_straightline_invoke;
2868 ot->modal= WM_gesture_straightline_modal;
2869 ot->exec= gesture_straightline_exec;
2871 ot->poll= WM_operator_winactive;
2873 WM_operator_properties_gesture_straightline(ot, 0);
2877 /* *********************** radial control ****************** */
2879 static const int WM_RADIAL_CONTROL_DISPLAY_SIZE = 200;
2883 PropertySubType subtype;
2884 PointerRNA ptr, col_ptr, fill_col_ptr, rot_ptr, zoom_ptr, image_id_ptr;
2885 PropertyRNA *prop, *col_prop, *fill_col_prop, *rot_prop, *zoom_prop;
2886 StructRNA *image_id_srna;
2887 float initial_value, current_value, min_value, max_value;
2888 int initial_mouse[2];
2890 ListBase orig_paintcursors;
2894 static void radial_control_set_initial_mouse(RadialControl *rc, wmEvent *event)
2896 float d[2] = {0, 0};
2897 float zoom[2] = {1, 1};
2899 rc->initial_mouse[0]= event->x;
2900 rc->initial_mouse[1]= event->y;
2902 switch(rc->subtype) {
2904 d[0] = rc->initial_value;
2907 d[0] = WM_RADIAL_CONTROL_DISPLAY_SIZE * (1 - rc->initial_value);
2910 d[0] = WM_RADIAL_CONTROL_DISPLAY_SIZE * cos(rc->initial_value);
2911 d[1] = WM_RADIAL_CONTROL_DISPLAY_SIZE * sin(rc->initial_value);
2918 RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom);
2923 rc->initial_mouse[0]-= d[0];
2924 rc->initial_mouse[1]-= d[1];
2927 static void radial_control_set_tex(RadialControl *rc)
2931 switch(RNA_type_to_ID_code(rc->image_id_ptr.type)) {
2933 if((ibuf = brush_gen_radial_control_imbuf(rc->image_id_ptr.data))) {
2934 glGenTextures(1, &rc->gltex);
2935 glBindTexture(GL_TEXTURE_2D, rc->gltex);
2936 glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, ibuf->x, ibuf->y, 0,
2937 GL_ALPHA, GL_FLOAT, ibuf->rect_float);
2938 MEM_freeN(ibuf->rect_float);
2947 static void radial_control_paint_tex(RadialControl *rc, float radius, float alpha)
2949 float col[3] = {0, 0, 0};
2952 /* set fill color */
2953 if(rc->fill_col_prop)
2954 RNA_property_float_get_array(&rc->fill_col_ptr, rc->fill_col_prop, col);
2955 glColor4f(col[0], col[1], col[2], alpha);
2958 glBindTexture(GL_TEXTURE_2D, rc->gltex);
2960 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
2961 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
2963 /* set up rotation if available */
2965 rot = RNA_property_float_get(&rc->rot_ptr, rc->rot_prop);
2967 glRotatef(RAD2DEGF(rot), 0, 0, 1);
2970 /* draw textured quad */
2971 glEnable(GL_TEXTURE_2D);
2974 glVertex2f(-radius, -radius);
2976 glVertex2f(radius, -radius);
2978 glVertex2f(radius, radius);
2980 glVertex2f(-radius, radius);
2982 glDisable(GL_TEXTURE_2D);
2989 /* flat color if no texture available */
2990 glutil_draw_filled_arc(0, M_PI * 2, radius, 40);
2994 static void radial_control_paint_cursor(bContext *C, int x, int y, void *customdata)
2996 RadialControl *rc = customdata;
2997 ARegion *ar = CTX_wm_region(C);
2998 float r1=0.0f, r2=0.0f, tex_radius, alpha;
2999 float zoom[2], col[3] = {1, 1, 1};
3001 switch(rc->subtype) {
3003 r1= rc->current_value;
3004 r2= rc->initial_value;
3009 r1= (1 - rc->current_value) * WM_RADIAL_CONTROL_DISPLAY_SIZE;
3010 r2= tex_radius= WM_RADIAL_CONTROL_DISPLAY_SIZE;
3011 alpha = rc->current_value / 2.0f + 0.5f;
3014 r1= r2= tex_radius= WM_RADIAL_CONTROL_DISPLAY_SIZE;
3018 tex_radius= WM_RADIAL_CONTROL_DISPLAY_SIZE; /* note, this is a dummy value */
3023 /* Keep cursor in the original place */
3024 x = rc->initial_mouse[0] - ar->winrct.xmin;
3025 y = rc->initial_mouse[1] - ar->winrct.ymin;
3026 glTranslatef((float)x, (float)y, 0.0f);
3029 glEnable(GL_LINE_SMOOTH);
3031 /* apply zoom if available */
3033 RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom);
3034 glScalef(zoom[0], zoom[1], 1);
3037 /* draw rotated texture */
3038 radial_control_paint_tex(rc, tex_radius, alpha);
3040 /* set line color */
3042 RNA_property_float_get_array(&rc->col_ptr, rc->col_prop, col);
3043 glColor4f(col[0], col[1], col[2], 0.5);
3045 if(rc->subtype == PROP_ANGLE) {
3047 /* draw original angle line */
3048 glRotatef(RAD2DEGF(rc->initial_value), 0, 0, 1);
3049 fdrawline(0.0f, 0.0f, (float)WM_RADIAL_CONTROL_DISPLAY_SIZE, 0.0f);
3050 /* draw new angle line */
3051 glRotatef(RAD2DEGF(rc->current_value - rc->initial_value), 0, 0, 1);
3052 fdrawline(0.0f, 0.0f, (float)WM_RADIAL_CONTROL_DISPLAY_SIZE, 0.0f);
3056 /* draw circles on top */
3057 glutil_draw_lined_arc(0.0, (float)(M_PI*2.0), r1, 40);
3058 glutil_draw_lined_arc(0.0, (float)(M_PI*2.0), r2, 40);
3060 glDisable(GL_BLEND);
3061 glDisable(GL_LINE_SMOOTH);
3064 /* attempt to retrieve the rna pointer/property from an rna path;
3065 returns 0 for failure, 1 for success, and also 1 if property is not
3067 static int radial_control_get_path(PointerRNA *ctx_ptr, wmOperator *op,
3068 const char *name, PointerRNA *r_ptr,
3069 PropertyRNA **r_prop, int req_float,
3070 int req_length, int allow_missing)
3072 PropertyRNA *unused_prop;
3076 /* get an rna string path from the operator's properties */
3077 if(!(str = RNA_string_get_alloc(op->ptr, name, NULL, 0)))
3080 if(str[0] == '\0') {
3086 r_prop = &unused_prop;
3088 /* get rna from path */
3089 if(!RNA_path_resolve(ctx_ptr, str, r_ptr, r_prop)) {
3094 BKE_reportf(op->reports, RPT_ERROR, "Couldn't resolve path %s", name);
3099 /* if property is expected to be a float, check its type */
3101 if(!(*r_prop) || (RNA_property_type(*r_prop) != PROP_FLOAT)) {
3103 BKE_reportf(op->reports, RPT_ERROR,
3104 "Property from path %s is not a float", name);
3109 /* check property's array length */
3110 if(*r_prop && (len = RNA_property_array_length(r_ptr, *r_prop)) != req_length) {
3112 BKE_reportf(op->reports, RPT_ERROR,
3113 "Property from path %s has length %d instead of %d",
3114 name, len, req_length);
3123 /* initialize the rna pointers and properties using rna paths */
3124 static int radial_control_get_properties(bContext *C, wmOperator *op)
3126 RadialControl *rc = op->customdata;
3129 RNA_pointer_create(NULL, &RNA_Context, C, &ctx_ptr);
3131 if(!radial_control_get_path(&ctx_ptr, op, "data_path", &rc->ptr, &rc->prop, 0, 0, 0))
3134 /* data path is required */
3138 if(!radial_control_get_path(&ctx_ptr, op, "rotation_path", &rc->rot_ptr, &rc->rot_prop, 1, 0, 0))
3140 if(!radial_control_get_path(&ctx_ptr, op, "color_path", &rc->col_ptr, &rc->col_prop, 1, 3, 0))
3142 if(!radial_control_get_path(&ctx_ptr, op, "fill_color_path", &rc->fill_col_ptr, &rc->fill_col_prop, 1, 3, 0))
3145 /* slightly ugly; allow this property to not resolve
3146 correctly. needed because 3d texture paint shares the same
3147 keymap as 2d image paint */
3148 if(!radial_control_get_path(&ctx_ptr, op, "zoom_path", &rc->zoom_ptr, &rc->zoom_prop, 1, 2, 1))
3151 if(!radial_control_get_path(&ctx_ptr, op, "image_id", &rc->image_id_ptr, NULL, 0, 0, 0))
3153 else if(rc->image_id_ptr.data) {
3154 /* extra check, pointer must be to an ID */
3155 if(!RNA_struct_is_ID(rc->image_id_ptr.type)) {
3156 BKE_report(op->reports, RPT_ERROR,
3157 "Pointer from path image_id is not an ID");
3165 static int radial_control_invoke(bContext *C, wmOperator *op, wmEvent *event)
3167 wmWindowManager *wm;
3169 int min_value_int, max_value_int, step_int;
3170 float step_float, precision;
3172 if(!(op->customdata = rc = MEM_callocN(sizeof(RadialControl), "RadialControl")))
3173 return OPERATOR_CANCELLED;
3175 if(!radial_control_get_properties(C, op)) {
3177 return OPERATOR_CANCELLED;
3180 /* get type, initial, min, and max values of the property */
3181 switch((rc->type = RNA_property_type(rc->prop))) {
3183 rc->initial_value = RNA_property_int_get(&rc->ptr, rc->prop);
3184 RNA_property_int_ui_range(&rc->ptr, rc->prop, &min_value_int,
3185 &max_value_int, &step_int);
3186 rc->min_value = min_value_int;
3187 rc->max_value = max_value_int;
3190 rc->initial_value = RNA_property_float_get(&rc->ptr, rc->prop);
3191 RNA_property_float_ui_range(&rc->ptr, rc->prop, &rc->min_value,
3192 &rc->max_value, &step_float, &precision);
3195 BKE_report(op->reports, RPT_ERROR, "Property must be an integer or a float");
3197 return OPERATOR_CANCELLED;
3200 /* get subtype of property */
3201 rc->subtype = RNA_property_subtype(rc->prop);
3202 if(!ELEM3(rc->subtype, PROP_DISTANCE, PROP_FACTOR, PROP_ANGLE)) {
3203 BKE_report(op->reports, RPT_ERROR, "Property must be a distance, a factor, or an angle");
3205 return OPERATOR_CANCELLED;
3208 rc->current_value = rc->initial_value;
3209 radial_control_set_initial_mouse(rc, event);
3210 radial_control_set_tex(rc);
3212 /* temporarily disable other paint cursors */
3213 wm = CTX_wm_manager(C);
3214 rc->orig_paintcursors = wm->paintcursors;
3215 wm->paintcursors.first = wm->paintcursors.last = NULL;
3217 /* add radial control paint cursor */
3218 rc->cursor = WM_paint_cursor_activate(wm, op->type->poll,
3219 radial_control_paint_cursor, rc);
3221 WM_event_add_modal_handler(C, op);
3223 return OPERATOR_RUNNING_MODAL;
3226 static void radial_control_set_value(RadialControl *rc, float val)
3230 RNA_property_int_set(&rc->ptr, rc->prop, val);
3233 RNA_property_float_set(&rc->ptr, rc->prop, val);
3240 static int radial_control_cancel(bContext *C, wmOperator *op)
3242 RadialControl *rc = op->customdata;
3243 wmWindowManager *wm = CTX_wm_manager(C);
3245 WM_paint_cursor_end(wm, rc->cursor);
3247 /* restore original paint cursors */
3248 wm->paintcursors = rc->orig_paintcursors;
3250 /* not sure if this is a good notifier to use;
3251 intended purpose is to update the UI so that the
3252 new value is displayed in sliders/numfields */
3253 WM_event_add_notifier(C, NC_WINDOW, NULL);
3255 glDeleteTextures(1, &rc->gltex);
3259 return OPERATOR_CANCELLED;
3262 static int radial_control_modal(bContext *C, wmOperator *op, wmEvent *event)
3264 RadialControl *rc = op->customdata;
3265 float new_value, dist, zoom[2];
3266 float delta[2], snap, ret = OPERATOR_RUNNING_MODAL;
3268 /* TODO: fix hardcoded events */
3272 switch(event->type) {
3274 delta[0]= rc->initial_mouse[0] - event->x;
3275 delta[1]= rc->initial_mouse[1] - event->y;
3278 RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom);
3279 delta[0] /= zoom[0];
3280 delta[1] /= zoom[1];
3283 dist= sqrt(delta[0]*delta[0]+delta[1]*delta[1]);
3285 /* calculate new value and apply snapping */
3286 switch(rc->subtype) {
3289 if(snap) new_value = ((int)new_value + 5) / 10*10;
3292 new_value = 1 - dist / WM_RADIAL_CONTROL_DISPLAY_SIZE;
3293 if(snap) new_value = ((int)ceil(new_value * 10.f) * 10.0f) / 100.f;