RNA
[blender.git] / source / blender / windowmanager / intern / wm_operators.c
1 /**
2  * $Id:
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. 
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2007 Blender Foundation.
21  * All rights reserved.
22  *
23  * 
24  * Contributor(s): Blender Foundation
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 #include <string.h>
30
31 #include "DNA_ID.h"
32 #include "DNA_screen_types.h"
33 #include "DNA_userdef_types.h"
34 #include "DNA_windowmanager_types.h"
35
36 #include "MEM_guardedalloc.h"
37
38 #include "BLI_blenlib.h"
39 #include "BLI_dynstr.h" /*for WM_operator_pystring */
40
41 #include "BKE_blender.h"
42 #include "BKE_context.h"
43 #include "BKE_idprop.h"
44 #include "BKE_library.h"
45 #include "BKE_global.h"
46 #include "BKE_main.h"
47 #include "BKE_utildefines.h"
48
49 #include "RNA_access.h"
50 #include "RNA_define.h"
51
52 #include "UI_interface.h"
53 #include "UI_resources.h"
54
55 #include "WM_api.h"
56 #include "WM_types.h"
57
58 #include "wm.h"
59 #include "wm_window.h"
60 #include "wm_subwindow.h"
61 #include "wm_event_system.h"
62
63 #include "ED_screen.h"
64
65 static ListBase global_ops= {NULL, NULL};
66
67 /* ************ operator API, exported ********** */
68
69 wmOperatorType *WM_operatortype_find(const char *idname)
70 {
71         wmOperatorType *ot;
72         
73         for(ot= global_ops.first; ot; ot= ot->next) {
74                 if(strncmp(ot->idname, idname, OP_MAX_TYPENAME)==0)
75                    return ot;
76         }
77         printf("search for unknown operator %s\n", idname);
78         return NULL;
79 }
80
81 wmOperatorType *WM_operatortype_first(void)
82 {
83         return global_ops.first;
84 }
85
86 /* all ops in 1 list (for time being... needs evaluation later) */
87 void WM_operatortype_append(void (*opfunc)(wmOperatorType*))
88 {
89         wmOperatorType *ot;
90         
91         ot= MEM_callocN(sizeof(wmOperatorType), "operatortype");
92         ot->srna= RNA_def_struct(&BLENDER_RNA, "", "OperatorProperties");
93         opfunc(ot);
94         RNA_def_struct_ui_text(ot->srna, ot->name, "DOC_BROKEN"); /* TODO - add a discription to wmOperatorType? */
95         RNA_def_struct_identifier(ot->srna, ot->idname);
96         BLI_addtail(&global_ops, ot);
97 }
98
99 void WM_operatortype_append_ptr(void (*opfunc)(wmOperatorType*, void*), void *userdata)
100 {
101         wmOperatorType *ot;
102
103         ot= MEM_callocN(sizeof(wmOperatorType), "operatortype");
104         ot->srna= RNA_def_struct(&BLENDER_RNA, "", "OperatorProperties");
105         opfunc(ot, userdata);
106         RNA_def_struct_ui_text(ot->srna, ot->name, "DOC_BROKEN"); /* TODO - add a discription to wmOperatorType? */
107         RNA_def_struct_identifier(ot->srna, ot->idname);
108         BLI_addtail(&global_ops, ot);
109 }
110
111 int WM_operatortype_remove(const char *idname)
112 {
113         wmOperatorType *ot = WM_operatortype_find(idname);
114
115         if (ot==NULL)
116                 return 0;
117         
118         BLI_remlink(&global_ops, ot);
119         RNA_struct_free(&BLENDER_RNA, ot->srna);
120         MEM_freeN(ot);
121
122         return 1;
123 }
124
125 /* print a string representation of the operator, with the args that it runs 
126  * so python can run it again */
127 char *WM_operator_pystring(wmOperator *op)
128 {
129         const char *arg_name= NULL;
130
131         PropertyRNA *prop, *iterprop;
132         CollectionPropertyIterator iter;
133
134         /* for building the string */
135         DynStr *dynstr= BLI_dynstr_new();
136         char *cstring, *buf;
137         int first_iter=1;
138
139         BLI_dynstr_appendf(dynstr, "%s(", op->idname);
140
141         iterprop= RNA_struct_iterator_property(op->ptr);
142         RNA_property_collection_begin(op->ptr, iterprop, &iter);
143
144         for(; iter.valid; RNA_property_collection_next(&iter)) {
145                 prop= iter.ptr.data;
146                 arg_name= RNA_property_identifier(&iter.ptr, prop);
147
148                 if (strcmp(arg_name, "rna_type")==0) continue;
149
150                 buf= RNA_property_as_string(op->ptr, prop);
151                 
152                 BLI_dynstr_appendf(dynstr, first_iter?"%s=%s":", %s=%s", arg_name, buf);
153
154                 MEM_freeN(buf);
155                 first_iter = 0;
156         }
157
158         RNA_property_collection_end(&iter);
159
160         BLI_dynstr_append(dynstr, ")");
161
162         cstring = BLI_dynstr_get_cstring(dynstr);
163         BLI_dynstr_free(dynstr);
164         return cstring;
165 }
166
167 void WM_operator_properties_create(PointerRNA *ptr, const char *opstring)
168 {
169         wmOperatorType *ot= WM_operatortype_find(opstring);
170
171         if(ot)
172                 RNA_pointer_create(NULL, NULL, ot->srna, NULL, ptr);
173         else
174                 memset(ptr, 0, sizeof(*ptr));
175 }
176
177 void WM_operator_properties_free(PointerRNA *ptr)
178 {
179         IDProperty *properties= ptr->data;
180
181         if(properties) {
182                 IDP_FreeProperty(properties);
183                 MEM_freeN(properties);
184         }
185 }
186
187 /* ************ default op callbacks, exported *********** */
188
189 /* invoke callback, uses enum property named "type" */
190 /* only weak thing is the fixed property name... */
191 int WM_menu_invoke(bContext *C, wmOperator *op, wmEvent *event)
192 {
193         PropertyRNA *prop= RNA_struct_find_property(op->ptr, "type");
194         const EnumPropertyItem *item;
195         int totitem, i, len= strlen(op->type->name) + 5;
196         char *menu, *p;
197         
198         if(prop) {
199                 RNA_property_enum_items(op->ptr, prop, &item, &totitem);
200                 
201                 for (i=0; i<totitem; i++)
202                         len+= strlen(item[i].name) + 5;
203                 
204                 menu= MEM_callocN(len, "string");
205                 
206                 p= menu + sprintf(menu, "%s %%t", op->type->name);
207                 for (i=0; i<totitem; i++)
208                         p+= sprintf(p, "|%s %%x%d", item[i].name, item[i].value);
209                 
210                 uiPupmenuOperator(C, totitem/30, op, "type", menu);
211                 MEM_freeN(menu);
212                 
213                 return OPERATOR_RUNNING_MODAL;
214         }
215         return OPERATOR_CANCELLED;
216 }
217
218 /* op->invoke */
219 int WM_operator_confirm(bContext *C, wmOperator *op, wmEvent *event)
220 {
221         char buf[512];
222         
223         sprintf(buf, "OK? %%i%d%%t|%s", ICON_HELP, op->type->name);
224         uiPupmenuOperator(C, 0, op, NULL, buf);
225         
226         return OPERATOR_RUNNING_MODAL;
227 }
228
229 /* op->poll */
230 int WM_operator_winactive(bContext *C)
231 {
232         if(CTX_wm_window(C)==NULL) return 0;
233         return 1;
234 }
235
236 /* ************ window / screen operator definitions ************** */
237
238 static void WM_OT_window_duplicate(wmOperatorType *ot)
239 {
240         ot->name= "Duplicate Window";
241         ot->idname= "WM_OT_window_duplicate";
242         
243         ot->invoke= WM_operator_confirm;
244         ot->exec= wm_window_duplicate_op;
245         ot->poll= WM_operator_winactive;
246 }
247
248 static void WM_OT_save_homefile(wmOperatorType *ot)
249 {
250         ot->name= "Save User Settings";
251         ot->idname= "WM_OT_save_homefile";
252         
253         ot->invoke= WM_operator_confirm;
254         ot->exec= WM_write_homefile;
255         ot->poll= WM_operator_winactive;
256         
257         ot->flag= OPTYPE_REGISTER;
258 }
259
260 /* ********* recent file *********** */
261
262 static void recent_filelist(char *pup)
263 {
264         struct RecentFile *recent;
265         int i, ofs= 0;
266         char *p;
267         
268         p= pup + sprintf(pup, "Open Recent%%t");
269         
270         if (G.sce[0]) {
271                 p+= sprintf(p, "|%s %%x%d", G.sce, 1);
272                 ofs = 1;
273         }
274         
275         for (recent = G.recent_files.first, i=0; (i<U.recent_files) && (recent); recent = recent->next, i++) {
276                 if (strcmp(recent->filename, G.sce)) {
277                         p+= sprintf(p, "|%s %%x%d", recent->filename, i+ofs+1);
278                 }
279         }
280 }
281
282 static int recentfile_exec(bContext *C, wmOperator *op)
283 {
284         int event= RNA_enum_get(op->ptr, "nr");
285         
286         if(event>0) {
287                 if (G.sce[0] && (event==1))
288                         WM_read_file(C, G.sce, op->reports);
289                 else {
290                         struct RecentFile *recent = BLI_findlink(&(G.recent_files), event-2);
291                         if(recent) {
292                                 WM_read_file(C, recent->filename, op->reports);
293                         }
294                 }
295         }
296         return 0;
297 }
298
299 static int wm_recentfile_invoke(bContext *C, wmOperator *op, wmEvent *event)
300 {
301         char pup[2048];
302         
303         recent_filelist(pup);
304         uiPupmenuOperator(C, 0, op, "nr", pup);
305         
306         return OPERATOR_RUNNING_MODAL;
307 }
308
309 static void WM_OT_open_recentfile(wmOperatorType *ot)
310 {
311         ot->name= "Open Recent File";
312         ot->idname= "WM_OT_open_recentfile";
313         
314         ot->invoke= wm_recentfile_invoke;
315         ot->exec= recentfile_exec;
316         ot->poll= WM_operator_winactive;
317         
318         ot->flag= OPTYPE_REGISTER;
319         
320         RNA_def_property(ot->srna, "nr", PROP_ENUM, PROP_NONE);
321
322 }
323
324 /* *********************** */
325
326 static void WM_OT_window_fullscreen_toggle(wmOperatorType *ot)
327 {
328     ot->name= "Toggle Fullscreen";
329     ot->idname= "WM_OT_window_fullscreen_toggle";
330
331     ot->invoke= WM_operator_confirm;
332     ot->exec= wm_window_fullscreen_toggle_op;
333     ot->poll= WM_operator_winactive;
334 }
335
336 static void WM_OT_exit_blender(wmOperatorType *ot)
337 {
338         ot->name= "Exit Blender";
339         ot->idname= "WM_OT_exit_blender";
340
341         ot->invoke= WM_operator_confirm;
342         ot->exec= wm_exit_blender_op;
343         ot->poll= WM_operator_winactive;
344 }
345
346 /* ************ window gesture operator-callback definitions ************** */
347 /*
348  * These are default callbacks for use in operators requiring gesture input
349  */
350
351 /* **************** Border gesture *************** */
352
353 /* Border gesture has two types:
354    1) WM_GESTURE_CROSS_RECT: starts a cross, on mouse click it changes to border 
355    2) WM_GESTURE_RECT: starts immediate as a border, on mouse click or release it ends
356
357    It stores 4 values (xmin, xmax, ymin, ymax) and event it ended with (event_type)
358 */
359
360 static void border_apply(bContext *C, wmOperator *op, int event_type)
361 {
362         wmGesture *gesture= op->customdata;
363         rcti *rect= gesture->customdata;
364         
365         if(rect->xmin > rect->xmax)
366                 SWAP(int, rect->xmin, rect->xmax);
367         if(rect->ymin > rect->ymax)
368                 SWAP(int, rect->ymin, rect->ymax);
369         
370         /* operator arguments and storage. */
371         RNA_int_set(op->ptr, "xmin", rect->xmin);
372         RNA_int_set(op->ptr, "ymin", rect->ymin);
373         RNA_int_set(op->ptr, "xmax", rect->xmax);
374         RNA_int_set(op->ptr, "ymax", rect->ymax);
375         if( RNA_struct_find_property(op->ptr, "event_type") )
376                 RNA_int_set(op->ptr, "event_type", event_type);
377         
378         op->type->exec(C, op);
379 }
380
381 static void border_end(bContext *C, wmOperator *op)
382 {
383         wmGesture *gesture= op->customdata;
384         
385         WM_gesture_end(C, gesture);     /* frees gesture itself, and unregisters from window */
386         op->customdata= NULL;
387
388         ED_area_tag_redraw(CTX_wm_area(C));
389         
390 }
391
392 int WM_border_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
393 {
394         op->customdata= WM_gesture_new(C, event, WM_GESTURE_CROSS_RECT);
395
396         /* add modal handler */
397         WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
398         
399         wm_gesture_tag_redraw(C);
400
401         return OPERATOR_RUNNING_MODAL;
402 }
403
404 int WM_border_select_modal(bContext *C, wmOperator *op, wmEvent *event)
405 {
406         wmGesture *gesture= op->customdata;
407         rcti *rect= gesture->customdata;
408         int sx, sy;
409         
410         switch(event->type) {
411                 case MOUSEMOVE:
412                         
413                         wm_subwindow_getorigin(CTX_wm_window(C), gesture->swinid, &sx, &sy);
414                         
415                         if(gesture->type==WM_GESTURE_CROSS_RECT && gesture->mode==0) {
416                                 rect->xmin= rect->xmax= event->x - sx;
417                                 rect->ymin= rect->ymax= event->y - sy;
418                         }
419                         else {
420                                 rect->xmax= event->x - sx;
421                                 rect->ymax= event->y - sy;
422                         }
423                         
424                         wm_gesture_tag_redraw(C);
425
426                         break;
427                         
428                 case LEFTMOUSE:
429                 case MIDDLEMOUSE:
430                 case RIGHTMOUSE:
431                         if(event->val==1) {
432                                 if(gesture->type==WM_GESTURE_CROSS_RECT && gesture->mode==0) {
433                                         gesture->mode= 1;
434                                         wm_gesture_tag_redraw(C);
435                                 }
436                         }
437                         else {
438                                 border_apply(C, op, event->type);
439                                 border_end(C, op);
440                                 return OPERATOR_FINISHED;
441                         }
442                         break;
443                 case ESCKEY:
444                         border_end(C, op);
445                         return OPERATOR_CANCELLED;
446         }
447         return OPERATOR_RUNNING_MODAL;
448 }
449
450 /* **************** circle gesture *************** */
451 /* works now only for selection or modal paint stuff, calls exec while hold mouse */
452
453 int WM_gesture_circle_invoke(bContext *C, wmOperator *op, wmEvent *event)
454 {
455         op->customdata= WM_gesture_new(C, event, WM_GESTURE_CIRCLE);
456         
457         /* add modal handler */
458         WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
459         
460         wm_gesture_tag_redraw(C);
461         
462         return OPERATOR_RUNNING_MODAL;
463 }
464
465 static void gesture_circle_end(bContext *C, wmOperator *op)
466 {
467         wmGesture *gesture= op->customdata;
468         
469         WM_gesture_end(C, gesture);     /* frees gesture itself, and unregisters from window */
470         op->customdata= NULL;
471         
472         ED_area_tag_redraw(CTX_wm_area(C));
473 }
474
475 static void gesture_circle_apply(bContext *C, wmOperator *op, int event_type)
476 {
477         wmGesture *gesture= op->customdata;
478         rcti *rect= gesture->customdata;
479         
480         /* operator arguments and storage. */
481         RNA_int_set(op->ptr, "x", rect->xmin);
482         RNA_int_set(op->ptr, "y", rect->ymin);
483         RNA_int_set(op->ptr, "radius", rect->xmax);
484         if( RNA_struct_find_property(op->ptr, "event_type") )
485                 RNA_int_set(op->ptr, "event_type", event_type);
486         
487         if(op->type->exec)
488                 op->type->exec(C, op);
489 }
490
491 int WM_gesture_circle_modal(bContext *C, wmOperator *op, wmEvent *event)
492 {
493         wmGesture *gesture= op->customdata;
494         rcti *rect= gesture->customdata;
495         int sx, sy;
496         
497         switch(event->type) {
498                 case MOUSEMOVE:
499                         
500                         wm_subwindow_getorigin(CTX_wm_window(C), gesture->swinid, &sx, &sy);
501                         
502                         rect->xmin= event->x - sx;
503                         rect->ymin= event->y - sy;
504                         
505                         wm_gesture_tag_redraw(C);
506                         
507                         if(gesture->mode)
508                                 gesture_circle_apply(C, op, event->type);
509
510                         break;
511                 case WHEELUPMOUSE:
512                         rect->xmax += 2 + rect->xmax/10;
513                         wm_gesture_tag_redraw(C);
514                         break;
515                 case WHEELDOWNMOUSE:
516                         rect->xmax -= 2 + rect->xmax/10;
517                         if(rect->xmax < 1) rect->xmax= 1;
518                         wm_gesture_tag_redraw(C);
519                         break;
520                 case LEFTMOUSE:
521                 case MIDDLEMOUSE:
522                 case RIGHTMOUSE:
523                         if(event->val==0) {     /* key release */
524                                 gesture_circle_end(C, op);
525                                 return OPERATOR_FINISHED;
526                         }
527                         else
528                                 gesture->mode= 1;
529
530                         break;
531                 case ESCKEY:
532                         gesture_circle_end(C, op);
533                         return OPERATOR_CANCELLED;
534         }
535         return OPERATOR_RUNNING_MODAL;
536 }
537
538 #if 0
539 /* template to copy from */
540 void WM_OT_circle_gesture(wmOperatorType *ot)
541 {
542         ot->name= "Circle Gesture";
543         ot->idname= "WM_OT_circle_gesture";
544         
545         ot->invoke= WM_gesture_circle_invoke;
546         ot->modal= WM_gesture_circle_modal;
547         
548         ot->poll= WM_operator_winactive;
549         
550         RNA_def_property(ot->srna, "x", PROP_INT, PROP_NONE);
551         RNA_def_property(ot->srna, "y", PROP_INT, PROP_NONE);
552         RNA_def_property(ot->srna, "radius", PROP_INT, PROP_NONE);
553
554 }
555 #endif
556
557 /* **************** Tweak gesture *************** */
558
559 static int tweak_gesture_invoke(bContext *C, wmOperator *op, wmEvent *event)
560 {
561         op->customdata= WM_gesture_new(C, event, WM_GESTURE_TWEAK);
562         
563         /* add modal handler */
564         WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
565         
566         wm_gesture_tag_redraw(C);
567         
568         return OPERATOR_RUNNING_MODAL;
569 }
570
571 static void tweak_gesture_end(bContext *C, wmOperator *op)
572 {
573         wmGesture *gesture= op->customdata;
574         
575         WM_gesture_end(C, gesture);     /* frees gesture itself, and unregisters from window */
576         op->customdata= NULL;
577
578         ED_area_tag_redraw(CTX_wm_area(C));
579         
580 }
581
582 static int tweak_gesture_modal(bContext *C, wmOperator *op, wmEvent *event)
583 {
584         wmWindow *window= CTX_wm_window(C);
585         wmGesture *gesture= op->customdata;
586         rcti *rect= gesture->customdata;
587         int sx, sy, val;
588         
589         switch(event->type) {
590                 case MOUSEMOVE:
591                         
592                         wm_subwindow_getorigin(window, gesture->swinid, &sx, &sy);
593                         
594                         rect->xmax= event->x - sx;
595                         rect->ymax= event->y - sy;
596                         
597                         if((val= wm_gesture_evaluate(C, gesture))) {
598                                 wmEvent event;
599                                         
600                                 event= *(window->eventstate);
601                                 if(gesture->event_type==LEFTMOUSE)
602                                         event.type= EVT_TWEAK_L;
603                                 else if(gesture->event_type==RIGHTMOUSE)
604                                         event.type= EVT_TWEAK_R;
605                                 else
606                                         event.type= EVT_TWEAK_M;
607                                 event.val= val;
608                                 /* mouse coords! */
609                                 wm_event_add(window, &event);
610                                 
611                                 tweak_gesture_end(C, op);
612                                 return OPERATOR_FINISHED;
613                         }
614                         else
615                                 wm_gesture_tag_redraw(C);
616                         
617                         break;
618                         
619                 case LEFTMOUSE:
620                 case RIGHTMOUSE:
621                 case MIDDLEMOUSE:
622                         if(gesture->event_type==event->type) {
623                                 wm_gesture_evaluate(C, gesture);
624                                 tweak_gesture_end(C, op);
625                                 return OPERATOR_FINISHED;
626                         }
627                         break;
628         }
629         return OPERATOR_RUNNING_MODAL;
630 }
631
632 void WM_OT_tweak_gesture(wmOperatorType *ot)
633 {
634         ot->name= "Tweak Gesture";
635         ot->idname= "WM_OT_tweak_gesture";
636         
637         ot->invoke= tweak_gesture_invoke;
638         ot->modal= tweak_gesture_modal;
639
640         ot->poll= WM_operator_winactive;
641 }
642
643
644 /* ******************************************************* */
645  
646 /* called on initialize WM_exit() */
647 void wm_operatortype_free(void)
648 {
649         BLI_freelistN(&global_ops);
650 }
651
652 /* called on initialize WM_init() */
653 void wm_operatortype_init(void)
654 {
655         WM_operatortype_append(WM_OT_window_duplicate);
656         WM_operatortype_append(WM_OT_save_homefile);
657         WM_operatortype_append(WM_OT_window_fullscreen_toggle);
658         WM_operatortype_append(WM_OT_exit_blender);
659         WM_operatortype_append(WM_OT_tweak_gesture);
660         WM_operatortype_append(WM_OT_open_recentfile);
661 }
662
663 /* default keymap for windows and screens, only call once per WM */
664 void wm_window_keymap(wmWindowManager *wm)
665 {
666         ListBase *keymap= WM_keymap_listbase(wm, "Window", 0, 0);
667         
668         /* note, this doesn't replace existing keymap items */
669         WM_keymap_verify_item(keymap, "WM_OT_window_duplicate", AKEY, KM_PRESS, KM_CTRL|KM_ALT, 0);
670         WM_keymap_verify_item(keymap, "WM_OT_save_homefile", UKEY, KM_PRESS, KM_CTRL, 0);
671         WM_keymap_verify_item(keymap, "WM_OT_open_recentfile", OKEY, KM_PRESS, KM_CTRL, 0);
672         WM_keymap_verify_item(keymap, "WM_OT_window_fullscreen_toggle", FKEY, KM_PRESS, 0, 0);
673         WM_keymap_verify_item(keymap, "WM_OT_exit_blender", QKEY, KM_PRESS, KM_CTRL, 0);
674
675 }
676