added "description" and "readonly" properties to RNA Structs (also accessible via...
[blender-staging.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_windowmanager_types.h"
34
35 #include "MEM_guardedalloc.h"
36
37 #include "BLI_blenlib.h"
38
39 #include "BKE_blender.h"
40 #include "BKE_context.h"
41 #include "BKE_idprop.h"
42 #include "BKE_library.h"
43 #include "BKE_main.h"
44 #include "BKE_utildefines.h"
45
46 #include "RNA_access.h"
47 #include "RNA_define.h"
48
49 #include "UI_interface.h"
50 #include "UI_resources.h"
51
52 #include "WM_api.h"
53 #include "WM_types.h"
54
55 #include "wm.h"
56 #include "wm_window.h"
57 #include "wm_subwindow.h"
58 #include "wm_event_system.h"
59
60 #include "ED_screen.h"
61
62 static ListBase global_ops= {NULL, NULL};
63
64 /* ************ operator API, exported ********** */
65
66 wmOperatorType *WM_operatortype_find(const char *idname)
67 {
68         wmOperatorType *ot;
69         
70         for(ot= global_ops.first; ot; ot= ot->next) {
71                 if(strncmp(ot->idname, idname, OP_MAX_TYPENAME)==0)
72                    return ot;
73         }
74         printf("search for unknown operator %s\n", idname);
75         return NULL;
76 }
77
78 /* all ops in 1 list (for time being... needs evaluation later) */
79 void WM_operatortype_append(void (*opfunc)(wmOperatorType*))
80 {
81         wmOperatorType *ot;
82         
83         ot= MEM_callocN(sizeof(wmOperatorType), "operatortype");
84         ot->srna= RNA_def_struct(&BLENDER_RNA, "", "OperatorProperties");
85         opfunc(ot);
86         RNA_def_struct_ui_text(ot->srna, ot->name, "DOC_BROKEN"); /* TODO - add a discription to wmOperatorType? */
87         RNA_def_struct_identifier(ot->srna, ot->idname);
88         BLI_addtail(&global_ops, ot);
89 }
90
91 /* ************ default op callbacks, exported *********** */
92
93 static void operator_callback(bContext *C, void *arg, int retval)
94 {
95         wmOperator *op= arg;
96         
97         if(retval > 0)
98                 op->type->exec(C, op);
99 }
100
101 int WM_operator_confirm(bContext *C, wmOperator *op, wmEvent *event)
102 {
103         char buf[512];
104         
105         sprintf(buf, "OK? %%i%d%%t|%s", ICON_HELP, op->type->name);
106         uiPupmenu(C, 0, operator_callback, op, buf);
107         
108         return 1;
109 }
110
111 int WM_operator_winactive(bContext *C)
112 {
113         if(CTX_wm_window(C)==NULL) return 0;
114         return 1;
115 }
116
117 /* ************ window / screen operator definitions ************** */
118
119 static void WM_OT_window_duplicate(wmOperatorType *ot)
120 {
121         ot->name= "Duplicate Window";
122         ot->idname= "WM_OT_window_duplicate";
123         
124         ot->invoke= WM_operator_confirm;
125         ot->exec= wm_window_duplicate_op;
126         ot->poll= WM_operator_winactive;
127 }
128
129 static void WM_OT_save_homefile(wmOperatorType *ot)
130 {
131         ot->name= "Save User Settings";
132         ot->idname= "WM_OT_save_homefile";
133         
134         ot->invoke= WM_operator_confirm;
135         ot->exec= WM_write_homefile;
136         ot->poll= WM_operator_winactive;
137         
138         ot->flag= OPTYPE_REGISTER;
139 }
140
141 static void WM_OT_window_fullscreen_toggle(wmOperatorType *ot)
142 {
143     ot->name= "Toggle Fullscreen";
144     ot->idname= "WM_OT_window_fullscreen_toggle";
145
146     ot->invoke= WM_operator_confirm;
147     ot->exec= wm_window_fullscreen_toggle_op;
148     ot->poll= WM_operator_winactive;
149 }
150
151 static void WM_OT_exit_blender(wmOperatorType *ot)
152 {
153         ot->name= "Exit Blender";
154         ot->idname= "WM_OT_exit_blender";
155
156         ot->invoke= WM_operator_confirm;
157         ot->exec= wm_exit_blender_op;
158         ot->poll= WM_operator_winactive;
159 }
160
161 /* ************ window gesture operator-callback definitions ************** */
162 /*
163  * These are default callbacks for use in operators requiring gesture input
164  */
165
166 /* **************** Border gesture *************** */
167
168 /* Border gesture has two types:
169    1) WM_GESTURE_CROSS_RECT: starts a cross, on mouse click it changes to border 
170    2) WM_GESTURE_RECT: starts immediate as a border, on mouse click or release it ends
171
172    It stores 4 values (xmin, xmax, ymin, ymax) and event it ended with (event_type)
173 */
174
175 static void border_select_apply(bContext *C, wmOperator *op, int event_type)
176 {
177         wmGesture *gesture= op->customdata;
178         rcti *rect= gesture->customdata;
179         
180         if(rect->xmin > rect->xmax)
181                 SWAP(int, rect->xmin, rect->xmax);
182         if(rect->ymin > rect->ymax)
183                 SWAP(int, rect->ymin, rect->ymax);
184         
185         /* operator arguments and storage. */
186         RNA_int_set(op->ptr, "xmin", rect->xmin);
187         RNA_int_set(op->ptr, "ymin", rect->ymin);
188         RNA_int_set(op->ptr, "xmax", rect->xmax);
189         RNA_int_set(op->ptr, "ymax", rect->ymax);
190         
191         RNA_int_set(op->ptr, "event_type", event_type);
192         
193         op->type->exec(C, op);
194 }
195
196 static void border_select_end(bContext *C, wmOperator *op)
197 {
198         wmGesture *gesture= op->customdata;
199         
200         WM_gesture_end(C, gesture);     /* frees gesture itself, and unregisters from window */
201         op->customdata= NULL;
202
203         ED_area_tag_redraw(CTX_wm_area(C));
204         
205 }
206
207 int WM_border_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
208 {
209         op->customdata= WM_gesture_new(C, event, WM_GESTURE_CROSS_RECT);
210
211         /* add modal handler */
212         WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
213         
214         WM_event_add_notifier(C, WM_NOTE_GESTURE_REDRAW, 0, NULL);
215
216         return OPERATOR_RUNNING_MODAL;
217 }
218
219 int WM_border_select_modal(bContext *C, wmOperator *op, wmEvent *event)
220 {
221         wmGesture *gesture= op->customdata;
222         rcti *rect= gesture->customdata;
223         int sx, sy;
224         
225         switch(event->type) {
226                 case MOUSEMOVE:
227                         
228                         wm_subwindow_getorigin(CTX_wm_window(C), gesture->swinid, &sx, &sy);
229                         
230                         if(gesture->type==WM_GESTURE_CROSS_RECT && gesture->mode==0) {
231                                 rect->xmin= rect->xmax= event->x - sx;
232                                 rect->ymin= rect->ymax= event->y - sy;
233                         }
234                         else {
235                                 rect->xmax= event->x - sx;
236                                 rect->ymax= event->y - sy;
237                         }
238                         
239                         WM_event_add_notifier(C, WM_NOTE_GESTURE_REDRAW, 0, NULL);
240
241                         break;
242                         
243                 case LEFTMOUSE:
244                 case MIDDLEMOUSE:
245                 case RIGHTMOUSE:
246                         if(event->val==1) {
247                                 if(gesture->type==WM_GESTURE_CROSS_RECT && gesture->mode==0) {
248                                         gesture->mode= 1;
249                                         WM_event_add_notifier(C, WM_NOTE_GESTURE_REDRAW, 0, NULL);
250                                 }
251                         }
252                         else {
253                                 border_select_apply(C, op, event->type);
254                                 border_select_end(C, op);
255                                 return OPERATOR_FINISHED;
256                         }
257                         break;
258                 case ESCKEY:
259                         border_select_end(C, op);
260                         return OPERATOR_CANCELLED;
261         }
262         return OPERATOR_RUNNING_MODAL;
263 }
264
265 /* **************** Tweak gesture *************** */
266
267 static int tweak_gesture_invoke(bContext *C, wmOperator *op, wmEvent *event)
268 {
269         op->customdata= WM_gesture_new(C, event, WM_GESTURE_TWEAK);
270         
271         /* add modal handler */
272         WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
273         
274         WM_event_add_notifier(C, WM_NOTE_GESTURE_REDRAW, 0, NULL);
275         
276         return OPERATOR_RUNNING_MODAL;
277 }
278
279 static void tweak_gesture_end(bContext *C, wmOperator *op)
280 {
281         wmGesture *gesture= op->customdata;
282         
283         WM_gesture_end(C, gesture);     /* frees gesture itself, and unregisters from window */
284         op->customdata= NULL;
285
286         ED_area_tag_redraw(CTX_wm_area(C));
287         
288 }
289
290 static int tweak_gesture_modal(bContext *C, wmOperator *op, wmEvent *event)
291 {
292         wmWindow *window= CTX_wm_window(C);
293         wmGesture *gesture= op->customdata;
294         rcti *rect= gesture->customdata;
295         int sx, sy, val;
296         
297         switch(event->type) {
298                 case MOUSEMOVE:
299                         
300                         wm_subwindow_getorigin(window, gesture->swinid, &sx, &sy);
301                         
302                         rect->xmax= event->x - sx;
303                         rect->ymax= event->y - sy;
304                         
305                         if((val= wm_gesture_evaluate(C, gesture))) {
306                                 wmEvent event;
307                                         
308                                 event= *(window->eventstate);
309                                 if(gesture->event_type==LEFTMOUSE)
310                                         event.type= EVT_TWEAK_L;
311                                 else if(gesture->event_type==RIGHTMOUSE)
312                                         event.type= EVT_TWEAK_R;
313                                 else
314                                         event.type= EVT_TWEAK_M;
315                                 event.val= val;
316                                 /* mouse coords! */
317                                 wm_event_add(window, &event);
318                                 
319                                 tweak_gesture_end(C, op);
320                                 return OPERATOR_FINISHED;
321                         }
322                         else
323                                 WM_event_add_notifier(C, WM_NOTE_GESTURE_REDRAW, 0, NULL);
324                         
325                         break;
326                         
327                 case LEFTMOUSE:
328                 case RIGHTMOUSE:
329                 case MIDDLEMOUSE:
330                         if(gesture->event_type==event->type) {
331                                 wm_gesture_evaluate(C, gesture);
332                                 tweak_gesture_end(C, op);
333                                 return OPERATOR_FINISHED;
334                         }
335                         break;
336         }
337         return OPERATOR_RUNNING_MODAL;
338 }
339
340 void WM_OT_tweak_gesture(wmOperatorType *ot)
341 {
342         ot->name= "Tweak Gesture";
343         ot->idname= "WM_OT_tweak_gesture";
344         
345         ot->invoke= tweak_gesture_invoke;
346         ot->modal= tweak_gesture_modal;
347
348         ot->poll= WM_operator_winactive;
349 }
350
351
352 /* ******************************************************* */
353  
354 /* called on initialize WM_exit() */
355 void wm_operatortype_free(void)
356 {
357         BLI_freelistN(&global_ops);
358 }
359
360 /* called on initialize WM_init() */
361 void wm_operatortype_init(void)
362 {
363         WM_operatortype_append(WM_OT_window_duplicate);
364         WM_operatortype_append(WM_OT_save_homefile);
365         WM_operatortype_append(WM_OT_window_fullscreen_toggle);
366         WM_operatortype_append(WM_OT_exit_blender);
367         WM_operatortype_append(WM_OT_tweak_gesture);
368 }
369
370 /* default keymap for windows and screens, only call once per WM */
371 void wm_window_keymap(wmWindowManager *wm)
372 {
373         ListBase *keymap= WM_keymap_listbase(wm, "Window", 0, 0);
374         
375         /* note, this doesn't replace existing keymap items */
376         WM_keymap_verify_item(keymap, "WM_OT_window_duplicate", AKEY, KM_PRESS, KM_CTRL|KM_ALT, 0);
377         WM_keymap_verify_item(keymap, "WM_OT_save_homefile", UKEY, KM_PRESS, KM_CTRL, 0);
378         WM_keymap_verify_item(keymap, "WM_OT_window_fullscreen_toggle", FKEY, KM_PRESS, 0, 0);
379         WM_keymap_verify_item(keymap, "WM_OT_exit_blender", QKEY, KM_PRESS, KM_CTRL, 0);
380 }
381