D2607: Switch eye dropper to use linear color space internally
[blender.git] / source / blender / editors / interface / interface_eyedropper.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2009 Blender Foundation.
19  * All rights reserved.
20  *
21  * Contributor(s): Blender Foundation, Joshua Leung
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/editors/interface/interface_eyedropper.c
27  *  \ingroup edinterface
28  */
29
30 #include "MEM_guardedalloc.h"
31
32 #include "DNA_anim_types.h"
33 #include "DNA_space_types.h"
34 #include "DNA_screen_types.h"
35 #include "DNA_object_types.h"
36
37 #include "BLI_blenlib.h"
38 #include "BLI_math_vector.h"
39
40 #include "BLT_translation.h"
41
42 #include "BKE_context.h"
43 #include "BKE_screen.h"
44 #include "BKE_report.h"
45 #include "BKE_animsys.h"
46 #include "BKE_depsgraph.h"
47 #include "BKE_idcode.h"
48 #include "BKE_unit.h"
49
50 #include "RNA_access.h"
51 #include "RNA_define.h"
52
53 #include "BIF_gl.h"
54
55 #include "UI_interface.h"
56
57 #include "IMB_colormanagement.h"
58
59 #include "WM_api.h"
60 #include "WM_types.h"
61
62 #include "interface_intern.h"
63
64 /* for HDR color sampling */
65 #include "ED_image.h"
66 #include "ED_node.h"
67 #include "ED_clip.h"
68
69 /* for ID data eyedropper */
70 #include "ED_space_api.h"
71 #include "ED_screen.h"
72 #include "ED_view3d.h"
73
74 /* for Driver eyedropper */
75 #include "ED_keyframing.h"
76
77
78 /* -------------------------------------------------------------------- */
79 /* Keymap
80  */
81 /** \name Modal Keymap
82  * \{ */
83
84 enum {
85         EYE_MODAL_CANCEL = 1,
86         EYE_MODAL_SAMPLE_CONFIRM,
87         EYE_MODAL_SAMPLE_BEGIN,
88         EYE_MODAL_SAMPLE_RESET,
89 };
90
91 wmKeyMap *eyedropper_modal_keymap(wmKeyConfig *keyconf)
92 {
93         static EnumPropertyItem modal_items[] = {
94                 {EYE_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
95                 {EYE_MODAL_SAMPLE_CONFIRM, "SAMPLE_CONFIRM", 0, "Confirm Sampling", ""},
96                 {EYE_MODAL_SAMPLE_BEGIN, "SAMPLE_BEGIN", 0, "Start Sampling", ""},
97                 {EYE_MODAL_SAMPLE_RESET, "SAMPLE_RESET", 0, "Reset Sampling", ""},
98                 {0, NULL, 0, NULL, NULL}
99         };
100
101         wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "Eyedropper Modal Map");
102
103         /* this function is called for each spacetype, only needs to add map once */
104         if (keymap && keymap->modal_items)
105                 return NULL;
106
107         keymap = WM_modalkeymap_add(keyconf, "Eyedropper Modal Map", modal_items);
108
109         /* items for modal map */
110         WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, EYE_MODAL_CANCEL);
111         WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_PRESS, KM_ANY, 0, EYE_MODAL_CANCEL);
112         WM_modalkeymap_add_item(keymap, RETKEY, KM_RELEASE, KM_ANY, 0, EYE_MODAL_SAMPLE_CONFIRM);
113         WM_modalkeymap_add_item(keymap, PADENTER, KM_RELEASE, KM_ANY, 0, EYE_MODAL_SAMPLE_CONFIRM);
114         WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, EYE_MODAL_SAMPLE_CONFIRM);
115         WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, EYE_MODAL_SAMPLE_BEGIN);
116         WM_modalkeymap_add_item(keymap, SPACEKEY, KM_RELEASE, KM_ANY, 0, EYE_MODAL_SAMPLE_RESET);
117
118         /* assign to operators */
119         WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_color");
120         WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_id");
121         WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_depth");
122         WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_driver");
123
124         return keymap;
125 }
126
127 /** \} */
128
129
130 /* -------------------------------------------------------------------- */
131 /* Utility Functions
132  */
133 /** \name Generic Shared Functions
134  * \{ */
135
136 static void eyedropper_draw_cursor_text(const struct bContext *C, ARegion *ar, const char *name)
137 {
138         const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
139         wmWindow *win = CTX_wm_window(C);
140         int x = win->eventstate->x;
141         int y = win->eventstate->y;
142         const unsigned char fg[4] = {255, 255, 255, 255};
143         const unsigned char bg[4] = {0, 0, 0, 50};
144
145
146         if ((name[0] == '\0') ||
147             (BLI_rcti_isect_pt(&ar->winrct, x, y) == false))
148         {
149                 return;
150         }
151
152         x = x - ar->winrct.xmin;
153         y = y - ar->winrct.ymin;
154
155         y += U.widget_unit;
156
157         UI_fontstyle_draw_simple_backdrop(fstyle, x, y, name, fg, bg);
158 }
159
160
161 /**
162  * Utility to retrieve a button representing a RNA property that is currently under the cursor.
163  *
164  * This is to be used by any eyedroppers which fetch properties (e.g. UI_OT_eyedropper_driver).
165  * Especially during modal operations (e.g. as with the eyedroppers), context cannot be relied
166  * upon to provide this information, as it is not updated until the operator finishes.
167  *
168  * \return A button under the mouse which relates to some RNA Property, or NULL
169  */
170 static uiBut *eyedropper_get_property_button_under_mouse(bContext *C, const wmEvent *event)
171 {
172         wmWindow *win = CTX_wm_window(C);
173         ScrArea *sa = BKE_screen_find_area_xy(win->screen, SPACE_TYPE_ANY, event->x, event->y);
174         ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_ANY, event->x, event->y);
175         
176         uiBut *but = ui_but_find_mouse_over(ar, event);
177         
178         if (ELEM(NULL, but, but->rnapoin.data, but->rnaprop)) {
179                 return NULL;
180         }
181         else {
182                 return but;
183         }
184 }
185
186 /** \} */
187
188
189 /* -------------------------------------------------------------------- */
190 /* Eyedropper
191  */
192
193 /** \name Eyedropper (RGB Color)
194  * \{ */
195
196 typedef struct Eyedropper {
197         struct ColorManagedDisplay *display;
198
199         PointerRNA ptr;
200         PropertyRNA *prop;
201         int index;
202
203         float init_col[3]; /* for resetting on cancel */
204
205         bool  accum_start; /* has mouse been pressed */
206         float accum_col[3];
207         int   accum_tot;
208 } Eyedropper;
209
210 static bool eyedropper_init(bContext *C, wmOperator *op)
211 {
212         Scene *scene = CTX_data_scene(C);
213         Eyedropper *eye;
214
215         op->customdata = eye = MEM_callocN(sizeof(Eyedropper), "Eyedropper");
216
217         UI_context_active_but_prop_get(C, &eye->ptr, &eye->prop, &eye->index);
218
219         if ((eye->ptr.data == NULL) ||
220             (eye->prop == NULL) ||
221             (RNA_property_editable(&eye->ptr, eye->prop) == false) ||
222             (RNA_property_array_length(&eye->ptr, eye->prop) < 3) ||
223             (RNA_property_type(eye->prop) != PROP_FLOAT))
224         {
225                 return false;
226         }
227
228         if (RNA_property_subtype(eye->prop) != PROP_COLOR) {
229                 const char *display_device;
230                 float col[4];
231
232                 display_device = scene->display_settings.display_device;
233                 eye->display = IMB_colormanagement_display_get_named(display_device);
234
235                 /* store inital color */
236                 RNA_property_float_get_array(&eye->ptr, eye->prop, col);
237                 if (eye->display) {
238                         IMB_colormanagement_display_to_scene_linear_v3(col, eye->display);
239                 }
240                 copy_v3_v3(eye->init_col, col);
241         }
242
243         return true;
244 }
245
246 static void eyedropper_exit(bContext *C, wmOperator *op)
247 {
248         WM_cursor_modal_restore(CTX_wm_window(C));
249
250         if (op->customdata) {
251                 MEM_freeN(op->customdata);
252                 op->customdata = NULL;
253         }
254 }
255
256 /* *** eyedropper_color_ helper functions *** */
257
258 /**
259  * \brief get the color from the screen.
260  *
261  * Special check for image or nodes where we MAY have HDR pixels which don't display.
262  */
263 static void eyedropper_color_sample_fl(bContext *C, Eyedropper *UNUSED(eye), int mx, int my, float r_col[3])
264 {
265
266         /* we could use some clever */
267         wmWindow *win = CTX_wm_window(C);
268         ScrArea *sa = BKE_screen_find_area_xy(win->screen, SPACE_TYPE_ANY, mx, my);
269         const char *display_device = CTX_data_scene(C)->display_settings.display_device;
270         struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device);
271
272         if (sa) {
273                 if (sa->spacetype == SPACE_IMAGE) {
274                         ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my);
275                         if (ar) {
276                                 SpaceImage *sima = sa->spacedata.first;
277                                 int mval[2] = {mx - ar->winrct.xmin,
278                                                my - ar->winrct.ymin};
279
280                                 if (ED_space_image_color_sample(sima, ar, mval, r_col)) {
281                                         return;
282                                 }
283                         }
284                 }
285                 else if (sa->spacetype == SPACE_NODE) {
286                         ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my);
287                         if (ar) {
288                                 SpaceNode *snode = sa->spacedata.first;
289                                 int mval[2] = {mx - ar->winrct.xmin,
290                                                my - ar->winrct.ymin};
291
292                                 if (ED_space_node_color_sample(snode, ar, mval, r_col)) {
293                                         return;
294                                 }
295                         }
296                 }
297                 else if (sa->spacetype == SPACE_CLIP) {
298                         ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my);
299                         if (ar) {
300                                 SpaceClip *sc = sa->spacedata.first;
301                                 int mval[2] = {mx - ar->winrct.xmin,
302                                                my - ar->winrct.ymin};
303
304                                 if (ED_space_clip_color_sample(sc, ar, mval, r_col)) {
305                                         return;
306                                 }
307                         }
308                 }
309         }
310
311         /* fallback to simple opengl picker */
312         glReadBuffer(GL_FRONT);
313         glReadPixels(mx, my, 1, 1, GL_RGB, GL_FLOAT, r_col);
314         glReadBuffer(GL_BACK);
315         
316         IMB_colormanagement_display_to_scene_linear_v3(r_col, display);
317 }
318
319 /* sets the sample color RGB, maintaining A */
320 static void eyedropper_color_set(bContext *C, Eyedropper *eye, const float col[3])
321 {
322         float col_conv[4];
323
324         /* to maintain alpha */
325         RNA_property_float_get_array(&eye->ptr, eye->prop, col_conv);
326
327         /* convert from linear rgb space to display space */
328         if (eye->display) {
329                 copy_v3_v3(col_conv, col);
330                 IMB_colormanagement_scene_linear_to_display_v3(col_conv, eye->display);
331         }
332         else {
333                 copy_v3_v3(col_conv, col);
334         }
335
336         RNA_property_float_set_array(&eye->ptr, eye->prop, col_conv);
337
338         RNA_property_update(C, &eye->ptr, eye->prop);
339 }
340
341 /* set sample from accumulated values */
342 static void eyedropper_color_set_accum(bContext *C, Eyedropper *eye)
343 {
344         float col[3];
345         mul_v3_v3fl(col, eye->accum_col, 1.0f / (float)eye->accum_tot);
346         eyedropper_color_set(C, eye, col);
347 }
348
349 /* single point sample & set */
350 static void eyedropper_color_sample(bContext *C, Eyedropper *eye, int mx, int my)
351 {
352         float col[3];
353         eyedropper_color_sample_fl(C, eye, mx, my, col);
354         eyedropper_color_set(C, eye, col);
355 }
356
357 static void eyedropper_color_sample_accum(bContext *C, Eyedropper *eye, int mx, int my)
358 {
359         float col[3];
360         eyedropper_color_sample_fl(C, eye, mx, my, col);
361         /* delay linear conversion */
362         add_v3_v3(eye->accum_col, col);
363         eye->accum_tot++;
364 }
365
366 static void eyedropper_cancel(bContext *C, wmOperator *op)
367 {
368         Eyedropper *eye = op->customdata;
369         eyedropper_color_set(C, eye, eye->init_col);
370         eyedropper_exit(C, op);
371 }
372
373 /* main modal status check */
374 static int eyedropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
375 {
376         Eyedropper *eye = (Eyedropper *)op->customdata;
377
378         /* handle modal keymap */
379         if (event->type == EVT_MODAL_MAP) {
380                 switch (event->val) {
381                         case EYE_MODAL_CANCEL:
382                                 eyedropper_cancel(C, op);
383                                 return OPERATOR_CANCELLED;
384                         case EYE_MODAL_SAMPLE_CONFIRM:
385                                 if (eye->accum_tot == 0) {
386                                         eyedropper_color_sample(C, eye, event->x, event->y);
387                                 }
388                                 else {
389                                         eyedropper_color_set_accum(C, eye);
390                                 }
391                                 eyedropper_exit(C, op);
392                                 return OPERATOR_FINISHED;
393                         case EYE_MODAL_SAMPLE_BEGIN:
394                                 /* enable accum and make first sample */
395                                 eye->accum_start = true;
396                                 eyedropper_color_sample_accum(C, eye, event->x, event->y);
397                                 break;
398                         case EYE_MODAL_SAMPLE_RESET:
399                                 eye->accum_tot = 0;
400                                 zero_v3(eye->accum_col);
401                                 eyedropper_color_sample_accum(C, eye, event->x, event->y);
402                                 eyedropper_color_set_accum(C, eye);
403                                 break;
404                 }
405         }
406         else if (event->type == MOUSEMOVE) {
407                 if (eye->accum_start) {
408                         /* button is pressed so keep sampling */
409                         eyedropper_color_sample_accum(C, eye, event->x, event->y);
410                         eyedropper_color_set_accum(C, eye);
411                 }
412         }
413
414         return OPERATOR_RUNNING_MODAL;
415 }
416
417 /* Modal Operator init */
418 static int eyedropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
419 {
420         /* init */
421         if (eyedropper_init(C, op)) {
422                 WM_cursor_modal_set(CTX_wm_window(C), BC_EYEDROPPER_CURSOR);
423
424                 /* add temp handler */
425                 WM_event_add_modal_handler(C, op);
426
427                 return OPERATOR_RUNNING_MODAL;
428         }
429         else {
430                 eyedropper_exit(C, op);
431                 return OPERATOR_CANCELLED;
432         }
433 }
434
435 /* Repeat operator */
436 static int eyedropper_exec(bContext *C, wmOperator *op)
437 {
438         /* init */
439         if (eyedropper_init(C, op)) {
440
441                 /* do something */
442
443                 /* cleanup */
444                 eyedropper_exit(C, op);
445
446                 return OPERATOR_FINISHED;
447         }
448         else {
449                 return OPERATOR_CANCELLED;
450         }
451 }
452
453 static int eyedropper_poll(bContext *C)
454 {
455         PointerRNA ptr;
456         PropertyRNA *prop;
457         int index_dummy;
458         uiBut *but;
459
460         /* Only color buttons */
461         if ((CTX_wm_window(C) != NULL) &&
462             (but = UI_context_active_but_prop_get(C, &ptr, &prop, &index_dummy)) &&
463             (but->type == UI_BTYPE_COLOR))
464         {
465                 return 1;
466         }
467
468         return 0;
469 }
470
471 void UI_OT_eyedropper_color(wmOperatorType *ot)
472 {
473         /* identifiers */
474         ot->name = "Eyedropper";
475         ot->idname = "UI_OT_eyedropper_color";
476         ot->description = "Sample a color from the Blender Window to store in a property";
477
478         /* api callbacks */
479         ot->invoke = eyedropper_invoke;
480         ot->modal = eyedropper_modal;
481         ot->cancel = eyedropper_cancel;
482         ot->exec = eyedropper_exec;
483         ot->poll = eyedropper_poll;
484
485         /* flags */
486         ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
487
488         /* properties */
489 }
490
491 /** \} */
492
493
494 /* -------------------------------------------------------------------- */
495 /* Data Dropper */
496
497 /** \name Eyedropper (ID data-blocks)
498  *
499  * \note: datadropper is only internal name to avoid confusion in this file.
500  * \{ */
501
502 typedef struct DataDropper {
503         PointerRNA ptr;
504         PropertyRNA *prop;
505         short idcode;
506         const char *idcode_name;
507
508         ID *init_id; /* for resetting on cancel */
509
510         ARegionType *art;
511         void *draw_handle_pixel;
512         char name[200];
513 } DataDropper;
514
515
516 static void datadropper_draw_cb(const struct bContext *C, ARegion *ar, void *arg)
517 {
518         DataDropper *ddr = arg;
519         eyedropper_draw_cursor_text(C, ar, ddr->name);
520 }
521
522
523 static int datadropper_init(bContext *C, wmOperator *op)
524 {
525         DataDropper *ddr;
526         int index_dummy;
527         StructRNA *type;
528
529         SpaceType *st;
530         ARegionType *art;
531
532         st = BKE_spacetype_from_id(SPACE_VIEW3D);
533         art = BKE_regiontype_from_id(st, RGN_TYPE_WINDOW);
534
535         op->customdata = ddr = MEM_callocN(sizeof(DataDropper), "DataDropper");
536
537         UI_context_active_but_prop_get(C, &ddr->ptr, &ddr->prop, &index_dummy);
538
539         if ((ddr->ptr.data == NULL) ||
540             (ddr->prop == NULL) ||
541             (RNA_property_editable(&ddr->ptr, ddr->prop) == false) ||
542             (RNA_property_type(ddr->prop) != PROP_POINTER))
543         {
544                 return false;
545         }
546
547         ddr->art = art;
548         ddr->draw_handle_pixel = ED_region_draw_cb_activate(art, datadropper_draw_cb, ddr, REGION_DRAW_POST_PIXEL);
549
550         type = RNA_property_pointer_type(&ddr->ptr, ddr->prop);
551         ddr->idcode = RNA_type_to_ID_code(type);
552         BLI_assert(ddr->idcode != 0);
553         /* Note we can translate here (instead of on draw time), because this struct has very short lifetime. */
554         ddr->idcode_name = TIP_(BKE_idcode_to_name(ddr->idcode));
555
556         PointerRNA ptr = RNA_property_pointer_get(&ddr->ptr, ddr->prop);
557         ddr->init_id = ptr.id.data;
558
559         return true;
560 }
561
562 static void datadropper_exit(bContext *C, wmOperator *op)
563 {
564         WM_cursor_modal_restore(CTX_wm_window(C));
565
566         if (op->customdata) {
567                 DataDropper *ddr = (DataDropper *)op->customdata;
568
569                 if (ddr->art) {
570                         ED_region_draw_cb_exit(ddr->art, ddr->draw_handle_pixel);
571                 }
572
573                 MEM_freeN(op->customdata);
574
575                 op->customdata = NULL;
576         }
577
578         WM_event_add_mousemove(C);
579 }
580
581 /* *** datadropper id helper functions *** */
582 /**
583  * \brief get the ID from the screen.
584  *
585  */
586 static void datadropper_id_sample_pt(bContext *C, DataDropper *ddr, int mx, int my, ID **r_id)
587 {
588         /* we could use some clever */
589         wmWindow *win = CTX_wm_window(C);
590         ScrArea *sa = BKE_screen_find_area_xy(win->screen, -1, mx, my);
591
592         ScrArea *area_prev = CTX_wm_area(C);
593         ARegion *ar_prev = CTX_wm_region(C);
594
595         ddr->name[0] = '\0';
596
597         if (sa) {
598                 if (sa->spacetype == SPACE_VIEW3D) {
599                         ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my);
600                         if (ar) {
601                                 const int mval[2] = {
602                                     mx - ar->winrct.xmin,
603                                     my - ar->winrct.ymin};
604                                 Base *base;
605
606                                 CTX_wm_area_set(C, sa);
607                                 CTX_wm_region_set(C, ar);
608
609                                 /* grr, always draw else we leave stale text */
610                                 ED_region_tag_redraw(ar);
611
612                                 base = ED_view3d_give_base_under_cursor(C, mval);
613                                 if (base) {
614                                         Object *ob = base->object;
615                                         ID *id = NULL;
616                                         if (ddr->idcode == ID_OB) {
617                                                 id = (ID *)ob;
618                                         }
619                                         else if (ob->data) {
620                                                 if (GS(((ID *)ob->data)->name) == ddr->idcode) {
621                                                         id = (ID *)ob->data;
622                                                 }
623                                                 else {
624                                                         BLI_snprintf(ddr->name, sizeof(ddr->name), "Incompatible, expected a %s",
625                                                                      ddr->idcode_name);
626                                                 }
627                                         }
628
629                                         if (id) {
630                                                 BLI_snprintf(ddr->name, sizeof(ddr->name), "%s: %s",
631                                                              ddr->idcode_name, id->name + 2);
632                                                 *r_id = id;
633                                         }
634                                 }
635                         }
636                 }
637         }
638
639         CTX_wm_area_set(C, area_prev);
640         CTX_wm_region_set(C, ar_prev);
641 }
642
643 /* sets the ID, returns success */
644 static bool datadropper_id_set(bContext *C, DataDropper *ddr, ID *id)
645 {
646         PointerRNA ptr_value;
647
648         RNA_id_pointer_create(id, &ptr_value);
649
650         RNA_property_pointer_set(&ddr->ptr, ddr->prop, ptr_value);
651
652         RNA_property_update(C, &ddr->ptr, ddr->prop);
653
654         ptr_value = RNA_property_pointer_get(&ddr->ptr, ddr->prop);
655
656         return (ptr_value.id.data == id);
657 }
658
659 /* single point sample & set */
660 static bool datadropper_id_sample(bContext *C, DataDropper *ddr, int mx, int my)
661 {
662         ID *id = NULL;
663
664         datadropper_id_sample_pt(C, ddr, mx, my, &id);
665         return datadropper_id_set(C, ddr, id);
666 }
667
668 static void datadropper_cancel(bContext *C, wmOperator *op)
669 {
670         DataDropper *ddr = op->customdata;
671         datadropper_id_set(C, ddr, ddr->init_id);
672         datadropper_exit(C, op);
673 }
674
675 /* main modal status check */
676 static int datadropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
677 {
678         DataDropper *ddr = (DataDropper *)op->customdata;
679
680         /* handle modal keymap */
681         if (event->type == EVT_MODAL_MAP) {
682                 switch (event->val) {
683                         case EYE_MODAL_CANCEL:
684                                 datadropper_cancel(C, op);
685                                 return OPERATOR_CANCELLED;
686                         case EYE_MODAL_SAMPLE_CONFIRM:
687                         {
688                                 bool success;
689
690                                 success = datadropper_id_sample(C, ddr, event->x, event->y);
691                                 datadropper_exit(C, op);
692
693                                 if (success) {
694                                         return OPERATOR_FINISHED;
695                                 }
696                                 else {
697                                         BKE_report(op->reports, RPT_WARNING, "Failed to set value");
698                                         return OPERATOR_CANCELLED;
699                                 }
700                         }
701                 }
702         }
703         else if (event->type == MOUSEMOVE) {
704                 ID *id = NULL;
705                 datadropper_id_sample_pt(C, ddr, event->x, event->y, &id);
706         }
707
708         return OPERATOR_RUNNING_MODAL;
709 }
710
711 /* Modal Operator init */
712 static int datadropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
713 {
714         /* init */
715         if (datadropper_init(C, op)) {
716                 WM_cursor_modal_set(CTX_wm_window(C), BC_EYEDROPPER_CURSOR);
717
718                 /* add temp handler */
719                 WM_event_add_modal_handler(C, op);
720
721                 return OPERATOR_RUNNING_MODAL;
722         }
723         else {
724                 datadropper_exit(C, op);
725                 return OPERATOR_CANCELLED;
726         }
727 }
728
729 /* Repeat operator */
730 static int datadropper_exec(bContext *C, wmOperator *op)
731 {
732         /* init */
733         if (datadropper_init(C, op)) {
734                 /* cleanup */
735                 datadropper_exit(C, op);
736
737                 return OPERATOR_FINISHED;
738         }
739         else {
740                 return OPERATOR_CANCELLED;
741         }
742 }
743
744 static int datadropper_poll(bContext *C)
745 {
746         PointerRNA ptr;
747         PropertyRNA *prop;
748         int index_dummy;
749         uiBut *but;
750
751         /* data dropper only supports object data */
752         if ((CTX_wm_window(C) != NULL) &&
753             (but = UI_context_active_but_prop_get(C, &ptr, &prop, &index_dummy)) &&
754             (but->type == UI_BTYPE_SEARCH_MENU) &&
755             (but->flag & UI_BUT_VALUE_CLEAR))
756         {
757                 if (prop && RNA_property_type(prop) == PROP_POINTER) {
758                         StructRNA *type = RNA_property_pointer_type(&ptr, prop);
759                         const short idcode = RNA_type_to_ID_code(type);
760                         if ((idcode == ID_OB) || OB_DATA_SUPPORT_ID(idcode)) {
761                                 return 1;
762                         }
763                 }
764         }
765
766         return 0;
767 }
768
769 void UI_OT_eyedropper_id(wmOperatorType *ot)
770 {
771         /* identifiers */
772         ot->name = "Eyedropper Data-Block";
773         ot->idname = "UI_OT_eyedropper_id";
774         ot->description = "Sample a data-block from the 3D View to store in a property";
775
776         /* api callbacks */
777         ot->invoke = datadropper_invoke;
778         ot->modal = datadropper_modal;
779         ot->cancel = datadropper_cancel;
780         ot->exec = datadropper_exec;
781         ot->poll = datadropper_poll;
782
783         /* flags */
784         ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
785
786         /* properties */
787 }
788
789 /** \} */
790
791
792 /* -------------------------------------------------------------------- */
793 /* Depth Dropper */
794
795 /** \name Eyedropper (Depth)
796  *
797  * \note: depthdropper is only internal name to avoid confusion in this file.
798  * \{ */
799
800 typedef struct DepthDropper {
801         PointerRNA ptr;
802         PropertyRNA *prop;
803
804         float init_depth; /* for resetting on cancel */
805
806         bool  accum_start; /* has mouse been presed */
807         float accum_depth;
808         int   accum_tot;
809
810         ARegionType *art;
811         void *draw_handle_pixel;
812         char name[200];
813 } DepthDropper;
814
815
816 static void depthdropper_draw_cb(const struct bContext *C, ARegion *ar, void *arg)
817 {
818         DepthDropper *ddr = arg;
819         eyedropper_draw_cursor_text(C, ar, ddr->name);
820 }
821
822
823 static int depthdropper_init(bContext *C, wmOperator *op)
824 {
825         DepthDropper *ddr;
826         int index_dummy;
827
828         SpaceType *st;
829         ARegionType *art;
830
831         st = BKE_spacetype_from_id(SPACE_VIEW3D);
832         art = BKE_regiontype_from_id(st, RGN_TYPE_WINDOW);
833
834         op->customdata = ddr = MEM_callocN(sizeof(DepthDropper), "DepthDropper");
835
836         UI_context_active_but_prop_get(C, &ddr->ptr, &ddr->prop, &index_dummy);
837
838         /* fallback to the active camera's dof */
839         if (ddr->prop == NULL) {
840                 RegionView3D *rv3d = CTX_wm_region_view3d(C);
841                 if (rv3d && rv3d->persp == RV3D_CAMOB) {
842                         View3D *v3d = CTX_wm_view3d(C);
843                         if (v3d->camera && v3d->camera->data && !ID_IS_LINKED_DATABLOCK(v3d->camera->data)) {
844                                 RNA_id_pointer_create(v3d->camera->data, &ddr->ptr);
845                                 ddr->prop = RNA_struct_find_property(&ddr->ptr, "dof_distance");
846                         }
847                 }
848         }
849
850         if ((ddr->ptr.data == NULL) ||
851             (ddr->prop == NULL) ||
852             (RNA_property_editable(&ddr->ptr, ddr->prop) == false) ||
853             (RNA_property_type(ddr->prop) != PROP_FLOAT))
854         {
855                 return false;
856         }
857
858         ddr->art = art;
859         ddr->draw_handle_pixel = ED_region_draw_cb_activate(art, depthdropper_draw_cb, ddr, REGION_DRAW_POST_PIXEL);
860         ddr->init_depth = RNA_property_float_get(&ddr->ptr, ddr->prop);
861
862         return true;
863 }
864
865 static void depthdropper_exit(bContext *C, wmOperator *op)
866 {
867         WM_cursor_modal_restore(CTX_wm_window(C));
868
869         if (op->customdata) {
870                 DepthDropper *ddr = (DepthDropper *)op->customdata;
871
872                 if (ddr->art) {
873                         ED_region_draw_cb_exit(ddr->art, ddr->draw_handle_pixel);
874                 }
875
876                 MEM_freeN(op->customdata);
877
878                 op->customdata = NULL;
879         }
880 }
881
882 /* *** depthdropper id helper functions *** */
883 /**
884  * \brief get the ID from the screen.
885  *
886  */
887 static void depthdropper_depth_sample_pt(bContext *C, DepthDropper *ddr, int mx, int my, float *r_depth)
888 {
889         /* we could use some clever */
890         wmWindow *win = CTX_wm_window(C);
891         ScrArea *sa = BKE_screen_find_area_xy(win->screen, SPACE_TYPE_ANY, mx, my);
892         Scene *scene = win->screen->scene;
893         UnitSettings *unit = &scene->unit;
894         const bool do_split = (unit->flag & USER_UNIT_OPT_SPLIT) != 0;
895
896         ScrArea *area_prev = CTX_wm_area(C);
897         ARegion *ar_prev = CTX_wm_region(C);
898
899         ddr->name[0] = '\0';
900
901         if (sa) {
902                 if (sa->spacetype == SPACE_VIEW3D) {
903                         ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my);
904                         if (ar) {
905                                 View3D *v3d = sa->spacedata.first;
906                                 RegionView3D *rv3d = ar->regiondata;
907                                 /* weak, we could pass in some reference point */
908                                 const float *view_co = v3d->camera ? v3d->camera->obmat[3] : rv3d->viewinv[3];
909                                 const int mval[2] = {
910                                     mx - ar->winrct.xmin,
911                                     my - ar->winrct.ymin};
912                                 float co[3];
913
914                                 CTX_wm_area_set(C, sa);
915                                 CTX_wm_region_set(C, ar);
916
917                                 /* grr, always draw else we leave stale text */
918                                 ED_region_tag_redraw(ar);
919
920                                 view3d_operator_needs_opengl(C);
921
922                                 if (ED_view3d_autodist(scene, ar, v3d, mval, co, true, NULL)) {
923                                         const float mval_center_fl[2] = {
924                                             (float)ar->winx / 2,
925                                             (float)ar->winy / 2};
926                                         float co_align[3];
927
928                                         /* quick way to get view-center aligned point */
929                                         ED_view3d_win_to_3d(v3d, ar, co, mval_center_fl, co_align);
930
931                                         *r_depth = len_v3v3(view_co, co_align);
932
933                                         bUnit_AsString(ddr->name, sizeof(ddr->name),
934                                                        (double)*r_depth,
935                                                        4, unit->system, B_UNIT_LENGTH, do_split, false);
936                                 }
937                                 else {
938                                         BLI_strncpy(ddr->name, "Nothing under cursor", sizeof(ddr->name));
939                                 }
940                         }
941                 }
942         }
943
944         CTX_wm_area_set(C, area_prev);
945         CTX_wm_region_set(C, ar_prev);
946 }
947
948 /* sets the sample depth RGB, maintaining A */
949 static void depthdropper_depth_set(bContext *C, DepthDropper *ddr, const float depth)
950 {
951         RNA_property_float_set(&ddr->ptr, ddr->prop, depth);
952         RNA_property_update(C, &ddr->ptr, ddr->prop);
953 }
954
955 /* set sample from accumulated values */
956 static void depthdropper_depth_set_accum(bContext *C, DepthDropper *ddr)
957 {
958         float depth = ddr->accum_depth;
959         if (ddr->accum_tot) {
960                 depth /= (float)ddr->accum_tot;
961         }
962         depthdropper_depth_set(C, ddr, depth);
963 }
964
965 /* single point sample & set */
966 static void depthdropper_depth_sample(bContext *C, DepthDropper *ddr, int mx, int my)
967 {
968         float depth = -1.0f;
969         if (depth != -1.0f) {
970                 depthdropper_depth_sample_pt(C, ddr, mx, my, &depth);
971                 depthdropper_depth_set(C, ddr, depth);
972         }
973 }
974
975 static void depthdropper_depth_sample_accum(bContext *C, DepthDropper *ddr, int mx, int my)
976 {
977         float depth = -1.0f;
978         depthdropper_depth_sample_pt(C, ddr, mx, my, &depth);
979         if (depth != -1.0f) {
980                 ddr->accum_depth += depth;
981                 ddr->accum_tot++;
982         }
983 }
984
985 static void depthdropper_cancel(bContext *C, wmOperator *op)
986 {
987         DepthDropper *ddr = op->customdata;
988         depthdropper_depth_set(C, ddr, ddr->init_depth);
989         depthdropper_exit(C, op);
990 }
991
992 /* main modal status check */
993 static int depthdropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
994 {
995         DepthDropper *ddr = (DepthDropper *)op->customdata;
996
997         /* handle modal keymap */
998         if (event->type == EVT_MODAL_MAP) {
999                 switch (event->val) {
1000                         case EYE_MODAL_CANCEL:
1001                                 depthdropper_cancel(C, op);
1002                                 return OPERATOR_CANCELLED;
1003                         case EYE_MODAL_SAMPLE_CONFIRM:
1004                                 if (ddr->accum_tot == 0) {
1005                                         depthdropper_depth_sample(C, ddr, event->x, event->y);
1006                                 }
1007                                 else {
1008                                         depthdropper_depth_set_accum(C, ddr);
1009                                 }
1010                                 depthdropper_exit(C, op);
1011                                 return OPERATOR_FINISHED;
1012                         case EYE_MODAL_SAMPLE_BEGIN:
1013                                 /* enable accum and make first sample */
1014                                 ddr->accum_start = true;
1015                                 depthdropper_depth_sample_accum(C, ddr, event->x, event->y);
1016                                 break;
1017                         case EYE_MODAL_SAMPLE_RESET:
1018                                 ddr->accum_tot = 0;
1019                                 ddr->accum_depth = 0.0f;
1020                                 depthdropper_depth_sample_accum(C, ddr, event->x, event->y);
1021                                 depthdropper_depth_set_accum(C, ddr);
1022                                 break;
1023                 }
1024         }
1025         else if (event->type == MOUSEMOVE) {
1026                 if (ddr->accum_start) {
1027                         /* button is pressed so keep sampling */
1028                         depthdropper_depth_sample_accum(C, ddr, event->x, event->y);
1029                         depthdropper_depth_set_accum(C, ddr);
1030                 }
1031         }
1032
1033         return OPERATOR_RUNNING_MODAL;
1034 }
1035
1036 /* Modal Operator init */
1037 static int depthdropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1038 {
1039         /* init */
1040         if (depthdropper_init(C, op)) {
1041                 WM_cursor_modal_set(CTX_wm_window(C), BC_EYEDROPPER_CURSOR);
1042
1043                 /* add temp handler */
1044                 WM_event_add_modal_handler(C, op);
1045
1046                 return OPERATOR_RUNNING_MODAL;
1047         }
1048         else {
1049                 depthdropper_exit(C, op);
1050                 return OPERATOR_CANCELLED;
1051         }
1052 }
1053
1054 /* Repeat operator */
1055 static int depthdropper_exec(bContext *C, wmOperator *op)
1056 {
1057         /* init */
1058         if (depthdropper_init(C, op)) {
1059                 /* cleanup */
1060                 depthdropper_exit(C, op);
1061
1062                 return OPERATOR_FINISHED;
1063         }
1064         else {
1065                 return OPERATOR_CANCELLED;
1066         }
1067 }
1068
1069 static int depthdropper_poll(bContext *C)
1070 {
1071         PointerRNA ptr;
1072         PropertyRNA *prop;
1073         int index_dummy;
1074         uiBut *but;
1075
1076         /* check if there's an active button taking depth value */
1077         if ((CTX_wm_window(C) != NULL) &&
1078             (but = UI_context_active_but_prop_get(C, &ptr, &prop, &index_dummy)) &&
1079             (but->type == UI_BTYPE_NUM) &&
1080             (prop != NULL))
1081         {
1082                 if ((RNA_property_type(prop) == PROP_FLOAT) &&
1083                     (RNA_property_subtype(prop) & PROP_UNIT_LENGTH) &&
1084                     (RNA_property_array_check(prop) == false))
1085                 {
1086                         return 1;
1087                 }
1088         }
1089         else {
1090                 RegionView3D *rv3d = CTX_wm_region_view3d(C);
1091                 if (rv3d && rv3d->persp == RV3D_CAMOB) {
1092                         View3D *v3d = CTX_wm_view3d(C);
1093                         if (v3d->camera && v3d->camera->data && !ID_IS_LINKED_DATABLOCK(v3d->camera->data)) {
1094                                 return 1;
1095                         }
1096                 }
1097         }
1098
1099         return 0;
1100 }
1101
1102 void UI_OT_eyedropper_depth(wmOperatorType *ot)
1103 {
1104         /* identifiers */
1105         ot->name = "Eyedropper Depth";
1106         ot->idname = "UI_OT_eyedropper_depth";
1107         ot->description = "Sample depth from the 3D view";
1108
1109         /* api callbacks */
1110         ot->invoke = depthdropper_invoke;
1111         ot->modal = depthdropper_modal;
1112         ot->cancel = depthdropper_cancel;
1113         ot->exec = depthdropper_exec;
1114         ot->poll = depthdropper_poll;
1115
1116         /* flags */
1117         ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
1118
1119         /* properties */
1120 }
1121
1122 /** \} */
1123
1124 /* -------------------------------------------------------------------- */
1125 /* Eyedropper
1126  */
1127  
1128 /* NOTE: This is here (instead of in drivers.c) because we need access the button internals,
1129  * which we cannot access outside of the interface module
1130  */
1131
1132 /** \name Eyedropper (Driver Target)
1133  * \{ */
1134
1135 typedef struct DriverDropper {
1136         /* Destination property (i.e. where we'll add a driver) */
1137         PointerRNA ptr;
1138         PropertyRNA *prop;
1139         int index;
1140         
1141         // TODO: new target?
1142 } DriverDropper;
1143
1144 static bool driverdropper_init(bContext *C, wmOperator *op)
1145 {
1146         DriverDropper *ddr;
1147         uiBut *but;
1148
1149         op->customdata = ddr = MEM_callocN(sizeof(DriverDropper), "DriverDropper");
1150
1151         but = UI_context_active_but_prop_get(C, &ddr->ptr, &ddr->prop, &ddr->index);
1152
1153         if ((ddr->ptr.data == NULL) ||
1154             (ddr->prop == NULL) ||
1155             (RNA_property_editable(&ddr->ptr, ddr->prop) == false) ||
1156             (RNA_property_animateable(&ddr->ptr, ddr->prop) == false) ||
1157             (but->flag & UI_BUT_DRIVEN))
1158         {
1159                 return false;
1160         }
1161         
1162         return true;
1163 }
1164
1165 static void driverdropper_exit(bContext *C, wmOperator *op)
1166 {
1167         WM_cursor_modal_restore(CTX_wm_window(C));
1168
1169         if (op->customdata) {
1170                 MEM_freeN(op->customdata);
1171                 op->customdata = NULL;
1172         }
1173 }
1174
1175 static void driverdropper_sample(bContext *C, wmOperator *op, const wmEvent *event)
1176 {
1177         DriverDropper *ddr = (DriverDropper *)op->customdata;
1178         uiBut *but = eyedropper_get_property_button_under_mouse(C, event);
1179         
1180         short mapping_type = RNA_enum_get(op->ptr, "mapping_type");
1181         short flag = 0;
1182         
1183         /* we can only add a driver if we know what RNA property it corresponds to */
1184         if (but == NULL) {
1185                 return;
1186         }
1187         else {
1188                 /* Get paths for src... */
1189                 PointerRNA *target_ptr = &but->rnapoin;
1190                 PropertyRNA *target_prop = but->rnaprop;
1191                 int target_index = but->rnaindex;
1192                 
1193                 char *target_path = RNA_path_from_ID_to_property(target_ptr, target_prop);
1194                 
1195                 /* ... and destination */
1196                 char *dst_path    = BKE_animdata_driver_path_hack(C, &ddr->ptr, ddr->prop, NULL);
1197                 
1198                 /* Now create driver(s) */
1199                 if (target_path && dst_path) {
1200                         int success = ANIM_add_driver_with_target(op->reports,
1201                                                                   ddr->ptr.id.data, dst_path, ddr->index,
1202                                                                   target_ptr->id.data, target_path, target_index,
1203                                                                   flag, DRIVER_TYPE_PYTHON, mapping_type);
1204                         
1205                         if (success) {
1206                                 /* send updates */
1207                                 UI_context_update_anim_flag(C);
1208                                 DAG_relations_tag_update(CTX_data_main(C));
1209                                 DAG_id_tag_update(ddr->ptr.id.data, OB_RECALC_OB | OB_RECALC_DATA);
1210                                 WM_event_add_notifier(C, NC_ANIMATION | ND_FCURVES_ORDER, NULL);  // XXX
1211                         }
1212                 }
1213                 
1214                 /* cleanup */
1215                 if (target_path)
1216                         MEM_freeN(target_path);
1217                 if (dst_path)
1218                         MEM_freeN(dst_path);
1219         }
1220 }
1221
1222 static void driverdropper_cancel(bContext *C, wmOperator *op)
1223 {
1224         driverdropper_exit(C, op);
1225 }
1226
1227 /* main modal status check */
1228 static int driverdropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
1229 {
1230         /* handle modal keymap */
1231         if (event->type == EVT_MODAL_MAP) {
1232                 switch (event->val) {
1233                         case EYE_MODAL_CANCEL:
1234                                 driverdropper_cancel(C, op);
1235                                 return OPERATOR_CANCELLED;
1236                                 
1237                         case EYE_MODAL_SAMPLE_CONFIRM:
1238                                 driverdropper_sample(C, op, event);
1239                                 driverdropper_exit(C, op);
1240                                 
1241                                 return OPERATOR_FINISHED;
1242                 }
1243         }
1244         
1245         return OPERATOR_RUNNING_MODAL;
1246 }
1247
1248 /* Modal Operator init */
1249 static int driverdropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1250 {
1251         /* init */
1252         if (driverdropper_init(C, op)) {
1253                 WM_cursor_modal_set(CTX_wm_window(C), BC_EYEDROPPER_CURSOR);
1254                 
1255                 /* add temp handler */
1256                 WM_event_add_modal_handler(C, op);
1257                 
1258                 return OPERATOR_RUNNING_MODAL;
1259         }
1260         else {
1261                 driverdropper_exit(C, op);
1262                 return OPERATOR_CANCELLED;
1263         }
1264 }
1265
1266 /* Repeat operator */
1267 static int driverdropper_exec(bContext *C, wmOperator *op)
1268 {
1269         /* init */
1270         if (driverdropper_init(C, op)) {
1271                 /* cleanup */
1272                 driverdropper_exit(C, op);
1273                 
1274                 return OPERATOR_FINISHED;
1275         }
1276         else {
1277                 return OPERATOR_CANCELLED;
1278         }
1279 }
1280
1281 static int driverdropper_poll(bContext *C)
1282 {
1283         if (!CTX_wm_window(C)) return 0;
1284         else return 1;
1285 }
1286
1287 void UI_OT_eyedropper_driver(wmOperatorType *ot)
1288 {
1289         /* identifiers */
1290         ot->name = "Eyedropper Driver";
1291         ot->idname = "UI_OT_eyedropper_driver";
1292         ot->description = "Pick a property to use as a driver target";
1293         
1294         /* api callbacks */
1295         ot->invoke = driverdropper_invoke;
1296         ot->modal = driverdropper_modal;
1297         ot->cancel = driverdropper_cancel;
1298         ot->exec = driverdropper_exec;
1299         ot->poll = driverdropper_poll;
1300         
1301         /* flags */
1302         ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL | OPTYPE_UNDO;
1303         
1304         /* properties */
1305         RNA_def_enum(ot->srna, "mapping_type", prop_driver_create_mapping_types, 0,
1306                      "Mapping Type", "Method used to match target and driven properties");
1307 }
1308
1309 /** \} */