Merging r40345 through r40365 from trunk into soc-2011-garlic
[blender.git] / source / blender / editors / interface / interface_ops.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2009 Blender Foundation.
21  * All rights reserved.
22  * 
23  * Contributor(s): Blender Foundation, Joshua Leung
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/interface/interface_ops.c
29  *  \ingroup edinterface
30  */
31
32
33 #include <stdio.h>
34 #include <math.h>
35 #include <string.h>
36
37 #include "MEM_guardedalloc.h"
38
39 #include "DNA_scene_types.h"
40 #include "DNA_screen_types.h"
41 #include "DNA_text_types.h" /* for UI_OT_reports_to_text */
42
43 #include "BLI_blenlib.h"
44 #include "BLI_math_color.h"
45 #include "BLI_utildefines.h"
46
47 #include "BKE_context.h"
48 #include "BKE_global.h"
49 #include "BKE_text.h" /* for UI_OT_reports_to_text */
50 #include "BKE_report.h"
51
52 #include "RNA_access.h"
53 #include "RNA_define.h"
54
55 #include "BIF_gl.h"
56
57 #include "UI_interface.h"
58
59 #include "interface_intern.h"
60
61 #include "WM_api.h"
62 #include "WM_types.h"
63
64
65
66 /* ********************************************************** */
67
68 typedef struct Eyedropper {
69         PointerRNA ptr;
70         PropertyRNA *prop;
71         int index;
72 } Eyedropper;
73
74 static int eyedropper_init(bContext *C, wmOperator *op)
75 {
76         Eyedropper *eye;
77         
78         op->customdata= eye= MEM_callocN(sizeof(Eyedropper), "Eyedropper");
79         
80         uiContextActiveProperty(C, &eye->ptr, &eye->prop, &eye->index);
81         
82         return (eye->ptr.data && eye->prop && RNA_property_editable(&eye->ptr, eye->prop));
83 }
84  
85 static void eyedropper_exit(bContext *C, wmOperator *op)
86 {
87         WM_cursor_restore(CTX_wm_window(C));
88         
89         if(op->customdata)
90                 MEM_freeN(op->customdata);
91         op->customdata= NULL;
92 }
93
94 static int eyedropper_cancel(bContext *C, wmOperator *op)
95 {
96         eyedropper_exit(C, op);
97         return OPERATOR_CANCELLED;
98 }
99
100 static void eyedropper_sample(bContext *C, Eyedropper *eye, int mx, int my)
101 {
102         if(RNA_property_type(eye->prop) == PROP_FLOAT) {
103                 const int color_manage = CTX_data_scene(C)->r.color_mgt_flag & R_COLOR_MANAGEMENT;
104                 float col[4];
105         
106                 RNA_property_float_get_array(&eye->ptr, eye->prop, col);
107                 
108                 glReadBuffer(GL_FRONT);
109                 glReadPixels(mx, my, 1, 1, GL_RGB, GL_FLOAT, col);
110                 glReadBuffer(GL_BACK);
111         
112                 if (RNA_property_array_length(&eye->ptr, eye->prop) < 3) return;
113
114                 /* convert from screen (srgb) space to linear rgb space */
115                 if (color_manage && RNA_property_subtype(eye->prop) == PROP_COLOR)
116                         srgb_to_linearrgb_v3_v3(col, col);
117                 
118                 RNA_property_float_set_array(&eye->ptr, eye->prop, col);
119                 
120                 RNA_property_update(C, &eye->ptr, eye->prop);
121         }
122 }
123
124 /* main modal status check */
125 static int eyedropper_modal(bContext *C, wmOperator *op, wmEvent *event)
126 {
127         Eyedropper *eye = (Eyedropper *)op->customdata;
128         
129         switch(event->type) {
130                 case ESCKEY:
131                 case RIGHTMOUSE:
132                         return eyedropper_cancel(C, op);
133                 case LEFTMOUSE:
134                         if(event->val==KM_RELEASE) {
135                                 eyedropper_sample(C, eye, event->x, event->y);
136                                 eyedropper_exit(C, op);
137                                 return OPERATOR_FINISHED;
138                         }
139                         break;
140         }
141         
142         return OPERATOR_RUNNING_MODAL;
143 }
144
145 /* Modal Operator init */
146 static int eyedropper_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
147 {
148         /* init */
149         if (eyedropper_init(C, op)) {
150                 WM_cursor_modal(CTX_wm_window(C), BC_EYEDROPPER_CURSOR);
151
152                 /* add temp handler */
153                 WM_event_add_modal_handler(C, op);
154                 
155                 return OPERATOR_RUNNING_MODAL;
156         } else {
157                 eyedropper_exit(C, op);
158                 return OPERATOR_CANCELLED;
159         }
160 }
161
162 /* Repeat operator */
163 static int eyedropper_exec (bContext *C, wmOperator *op)
164 {
165         /* init */
166         if (eyedropper_init(C, op)) {
167                 
168                 /* do something */
169                 
170                 /* cleanup */
171                 eyedropper_exit(C, op);
172                 
173                 return OPERATOR_FINISHED;
174         } else {
175                 return OPERATOR_CANCELLED;
176         }
177 }
178
179 static int eyedropper_poll(bContext *C)
180 {
181         if (!CTX_wm_window(C)) return 0;
182         else return 1;
183 }
184
185 static void UI_OT_eyedropper(wmOperatorType *ot)
186 {
187         /* identifiers */
188         ot->name= "Eyedropper";
189         ot->idname= "UI_OT_eyedropper";
190         ot->description= "Sample a color from the Blender Window to store in a property";
191         
192         /* api callbacks */
193         ot->invoke= eyedropper_invoke;
194         ot->modal= eyedropper_modal;
195         ot->cancel= eyedropper_cancel;
196         ot->exec= eyedropper_exec;
197         ot->poll= eyedropper_poll;
198         
199         /* flags */
200         ot->flag= OPTYPE_BLOCKING;
201         
202         /* properties */
203 }
204
205 /* Reset Default Theme ------------------------ */
206
207 static int reset_default_theme_exec(bContext *C, wmOperator *UNUSED(op))
208 {
209         ui_theme_init_default();
210         WM_event_add_notifier(C, NC_WINDOW, NULL);
211         
212         return OPERATOR_FINISHED;
213 }
214
215 static void UI_OT_reset_default_theme(wmOperatorType *ot)
216 {
217         /* identifiers */
218         ot->name= "Reset to Default Theme";
219         ot->idname= "UI_OT_reset_default_theme";
220         ot->description= "Reset to the default theme colors";
221         
222         /* callbacks */
223         ot->exec= reset_default_theme_exec;
224         
225         /* flags */
226         ot->flag= OPTYPE_REGISTER;
227 }
228
229 /* Copy Data Path Operator ------------------------ */
230
231 static int copy_data_path_button_exec(bContext *C, wmOperator *UNUSED(op))
232 {
233         PointerRNA ptr;
234         PropertyRNA *prop;
235         char *path;
236         int success= 0;
237         int index;
238
239         /* try to create driver using property retrieved from UI */
240         uiContextActiveProperty(C, &ptr, &prop, &index);
241
242         if (ptr.id.data && ptr.data && prop) {
243                 path= RNA_path_from_ID_to_property(&ptr, prop);
244                 
245                 if (path) {
246                         WM_clipboard_text_set(path, FALSE);
247                         MEM_freeN(path);
248                 }
249         }
250
251         /* since we're just copying, we don't really need to do anything else...*/
252         return (success)? OPERATOR_FINISHED: OPERATOR_CANCELLED;
253 }
254
255 static void UI_OT_copy_data_path_button(wmOperatorType *ot)
256 {
257         /* identifiers */
258         ot->name= "Copy Data Path";
259         ot->idname= "UI_OT_copy_data_path_button";
260         ot->description= "Copy the RNA data path for this property to the clipboard";
261
262         /* callbacks */
263         ot->exec= copy_data_path_button_exec;
264         //op->poll= ??? // TODO: need to have some valid property before this can be done
265
266         /* flags */
267         ot->flag= OPTYPE_REGISTER;
268 }
269
270 /* Reset to Default Values Button Operator ------------------------ */
271
272 static int reset_default_button_poll(bContext *C)
273 {
274         PointerRNA ptr;
275         PropertyRNA *prop;
276         int index;
277
278         uiContextActiveProperty(C, &ptr, &prop, &index);
279         
280         return (ptr.data && prop && RNA_property_editable(&ptr, prop));
281 }
282
283 static int reset_default_button_exec(bContext *C, wmOperator *op)
284 {
285         PointerRNA ptr;
286         PropertyRNA *prop;
287         int success= 0;
288         int index, all = RNA_boolean_get(op->ptr, "all");
289
290         /* try to reset the nominated setting to its default value */
291         uiContextActiveProperty(C, &ptr, &prop, &index);
292         
293         /* if there is a valid property that is editable... */
294         if (ptr.data && prop && RNA_property_editable(&ptr, prop)) {
295                 if(RNA_property_reset(&ptr, prop, (all)? -1: index)) {
296                         /* perform updates required for this property */
297                         RNA_property_update(C, &ptr, prop);
298                         success= 1;
299                 }
300         }
301         
302         return (success)? OPERATOR_FINISHED: OPERATOR_CANCELLED;
303 }
304
305 static void UI_OT_reset_default_button(wmOperatorType *ot)
306 {
307         /* identifiers */
308         ot->name= "Reset to Default Value";
309         ot->idname= "UI_OT_reset_default_button";
310         ot->description= "Reset this property's value to its default value";
311
312         /* callbacks */
313         ot->poll= reset_default_button_poll;
314         ot->exec= reset_default_button_exec;
315
316         /* flags */
317         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
318         
319         /* properties */
320         RNA_def_boolean(ot->srna, "all", 1, "All", "Reset to default values all elements of the array");
321 }
322
323 /* Copy To Selected Operator ------------------------ */
324
325 static int copy_to_selected_list(bContext *C, PointerRNA *ptr, ListBase *lb)
326 {
327         if(RNA_struct_is_a(ptr->type, &RNA_Object))
328                 *lb = CTX_data_collection_get(C, "selected_editable_objects");
329         else if(RNA_struct_is_a(ptr->type, &RNA_EditBone))
330                 *lb = CTX_data_collection_get(C, "selected_editable_bones");
331         else if(RNA_struct_is_a(ptr->type, &RNA_PoseBone))
332                 *lb = CTX_data_collection_get(C, "selected_pose_bones");
333         else if(RNA_struct_is_a(ptr->type, &RNA_Sequence))
334                 *lb = CTX_data_collection_get(C, "selected_editable_sequences");
335         else
336                 return 0;
337         
338         return 1;
339 }
340
341 static int copy_to_selected_button_poll(bContext *C)
342 {
343         PointerRNA ptr;
344         PropertyRNA *prop;
345         int index, success= 0;
346
347         uiContextActiveProperty(C, &ptr, &prop, &index);
348
349         if (ptr.data && prop) {
350                 CollectionPointerLink *link;
351                 ListBase lb;
352
353                 if(copy_to_selected_list(C, &ptr, &lb)) {
354                         for(link= lb.first; link; link=link->next)
355                                 if(link->ptr.data != ptr.data && RNA_property_editable(&link->ptr, prop))
356                                         success= 1;
357
358                         BLI_freelistN(&lb);
359                 }
360         }
361
362         return success;
363 }
364
365 static int copy_to_selected_button_exec(bContext *C, wmOperator *op)
366 {
367         PointerRNA ptr;
368         PropertyRNA *prop;
369         int success= 0;
370         int index, all = RNA_boolean_get(op->ptr, "all");
371
372         /* try to reset the nominated setting to its default value */
373         uiContextActiveProperty(C, &ptr, &prop, &index);
374         
375         /* if there is a valid property that is editable... */
376         if (ptr.data && prop) {
377                 CollectionPointerLink *link;
378                 ListBase lb;
379
380                 if(copy_to_selected_list(C, &ptr, &lb)) {
381                         for(link= lb.first; link; link=link->next) {
382                                 if(link->ptr.data != ptr.data && RNA_property_editable(&link->ptr, prop)) {
383                                         if(RNA_property_copy(&link->ptr, &ptr, prop, (all)? -1: index)) {
384                                                 RNA_property_update(C, &link->ptr, prop);
385                                                 success= 1;
386                                         }
387                                 }
388                         }
389
390                         BLI_freelistN(&lb);
391                 }
392         }
393         
394         return (success)? OPERATOR_FINISHED: OPERATOR_CANCELLED;
395 }
396
397 static void UI_OT_copy_to_selected_button(wmOperatorType *ot)
398 {
399         /* identifiers */
400         ot->name= "Copy To Selected";
401         ot->idname= "UI_OT_copy_to_selected_button";
402         ot->description= "Copy property from this object to selected objects or bones";
403
404         /* callbacks */
405         ot->poll= copy_to_selected_button_poll;
406         ot->exec= copy_to_selected_button_exec;
407
408         /* flags */
409         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
410
411         /* properties */
412         RNA_def_boolean(ot->srna, "all", 1, "All", "Reset to default values all elements of the array");
413 }
414
415 /* Reports to Textblock Operator ------------------------ */
416
417 /* FIXME: this is just a temporary operator so that we can see all the reports somewhere 
418  * when there are too many to display...
419  */
420
421 static int reports_to_text_poll(bContext *C)
422 {
423         return CTX_wm_reports(C) != NULL;
424 }
425
426 static int reports_to_text_exec(bContext *C, wmOperator *UNUSED(op))
427 {
428         ReportList *reports = CTX_wm_reports(C);
429         Text *txt;
430         char *str;
431         
432         /* create new text-block to write to */
433         txt = add_empty_text("Recent Reports");
434         
435         /* convert entire list to a display string, and add this to the text-block
436          *      - if commandline debug option enabled, show debug reports too
437          *      - otherwise, up to info (which is what users normally see)
438          */
439         str = BKE_reports_string(reports, (G.f & G_DEBUG)? RPT_DEBUG : RPT_INFO);
440         
441         write_text(txt, str);
442         MEM_freeN(str);
443         
444         return OPERATOR_FINISHED;
445 }
446
447 static void UI_OT_reports_to_textblock(wmOperatorType *ot)
448 {
449         /* identifiers */
450         ot->name= "Reports to Text Block";
451         ot->idname= "UI_OT_reports_to_textblock";
452         ot->description= "Write the reports ";
453         
454         /* callbacks */
455         ot->poll= reports_to_text_poll;
456         ot->exec= reports_to_text_exec;
457 }
458
459 /* ********************************************************* */
460 /* Registration */
461
462 void UI_buttons_operatortypes(void)
463 {
464         WM_operatortype_append(UI_OT_eyedropper);
465         WM_operatortype_append(UI_OT_reset_default_theme);
466         WM_operatortype_append(UI_OT_copy_data_path_button);
467         WM_operatortype_append(UI_OT_reset_default_button);
468         WM_operatortype_append(UI_OT_copy_to_selected_button);
469         WM_operatortype_append(UI_OT_reports_to_textblock); // XXX: temp?
470 }
471