User customizable keymap for eyedropper (modal operator)
[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_space_types.h"
33 #include "DNA_screen_types.h"
34 #include "DNA_object_types.h"
35
36 #include "BLI_blenlib.h"
37 #include "BLI_math_vector.h"
38
39 #include "BLT_translation.h"
40
41 #include "BKE_context.h"
42 #include "BKE_screen.h"
43 #include "BKE_report.h"
44 #include "BKE_idcode.h"
45 #include "BKE_unit.h"
46
47 #include "RNA_access.h"
48
49 #include "BIF_gl.h"
50
51 #include "UI_interface.h"
52
53 #include "IMB_colormanagement.h"
54
55 #include "WM_api.h"
56 #include "WM_types.h"
57
58 #include "interface_intern.h"
59
60 /* for HDR color sampling */
61 #include "ED_image.h"
62 #include "ED_node.h"
63 #include "ED_clip.h"
64
65 /* for ID data eyedropper */
66 #include "ED_space_api.h"
67 #include "ED_screen.h"
68 #include "ED_view3d.h"
69
70
71 /* -------------------------------------------------------------------- */
72 /* Keymap
73  */
74 /** \name Modal Keymap
75  * \{ */
76
77 enum {
78         EYE_MODAL_CANCEL = 1, /* XXX actually does same as confirming */
79         EYE_MODAL_SAMPLE_CONFIRM,
80         EYE_MODAL_SAMPLE_BEGIN,
81         EYE_MODAL_SAMPLE_RESET,
82 };
83
84 wmKeyMap *eyedropper_modal_keymap(wmKeyConfig *keyconf)
85 {
86         static EnumPropertyItem modal_items[] = {
87                 {EYE_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
88                 {EYE_MODAL_SAMPLE_CONFIRM, "SAMPLE_CONFIRM", 0, "Confirm Sampling", ""},
89                 {EYE_MODAL_SAMPLE_BEGIN, "SAMPLE_BEGIN", 0, "Start Sampling", ""},
90                 {EYE_MODAL_SAMPLE_RESET, "SAMPLE_RESET", 0, "Reset Sampling", ""},
91                 {0, NULL, 0, NULL, NULL}
92         };
93
94         wmKeyMap *keymap = WM_modalkeymap_get(keyconf, "Eyedropper Modal Map");
95
96         /* this function is called for each spacetype, only needs to add map once */
97         if (keymap && keymap->modal_items)
98                 return NULL;
99
100         keymap = WM_modalkeymap_add(keyconf, "Eyedropper Modal Map", modal_items);
101
102         /* items for modal map */
103         WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, EYE_MODAL_CANCEL);
104         WM_modalkeymap_add_item(keymap, RIGHTMOUSE, KM_PRESS, KM_ANY, 0, EYE_MODAL_CANCEL);
105         WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, EYE_MODAL_SAMPLE_CONFIRM);
106         WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, EYE_MODAL_SAMPLE_BEGIN);
107         WM_modalkeymap_add_item(keymap, SPACEKEY, KM_RELEASE, KM_ANY, 0, EYE_MODAL_SAMPLE_RESET);
108
109         /* assign to operators */
110         WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_color");
111         WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_id");
112         WM_modalkeymap_assign(keymap, "UI_OT_eyedropper_depth");
113
114         return keymap;
115 }
116
117 /** \} */
118
119
120 /* -------------------------------------------------------------------- */
121 /* Utility Functions
122  */
123 /** \name Generic Shared Functions
124  * \{ */
125
126 static void eyedropper_draw_cursor_text(const struct bContext *C, ARegion *ar, const char *name)
127 {
128         const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
129         wmWindow *win = CTX_wm_window(C);
130         int x = win->eventstate->x;
131         int y = win->eventstate->y;
132         const unsigned char fg[4] = {255, 255, 255, 255};
133         const unsigned char bg[4] = {0, 0, 0, 50};
134
135
136         if ((name[0] == '\0') ||
137             (BLI_rcti_isect_pt(&ar->winrct, x, y) == false))
138         {
139                 return;
140         }
141
142         x = x - ar->winrct.xmin;
143         y = y - ar->winrct.ymin;
144
145         y += U.widget_unit;
146
147         UI_fontstyle_draw_simple_backdrop(fstyle, x, y, name, fg, bg);
148 }
149
150 /** \} */
151
152
153 /* -------------------------------------------------------------------- */
154 /* Eyedropper
155  */
156
157 /** \name Eyedropper (RGB Color)
158  * \{ */
159
160 typedef struct Eyedropper {
161         struct ColorManagedDisplay *display;
162
163         PointerRNA ptr;
164         PropertyRNA *prop;
165         int index;
166
167         bool  accum_start; /* has mouse been presed */
168         float accum_col[3];
169         int   accum_tot;
170 } Eyedropper;
171
172 static bool eyedropper_init(bContext *C, wmOperator *op)
173 {
174         Scene *scene = CTX_data_scene(C);
175         Eyedropper *eye;
176
177         op->customdata = eye = MEM_callocN(sizeof(Eyedropper), "Eyedropper");
178
179         UI_context_active_but_prop_get(C, &eye->ptr, &eye->prop, &eye->index);
180
181         if ((eye->ptr.data == NULL) ||
182             (eye->prop == NULL) ||
183             (RNA_property_editable(&eye->ptr, eye->prop) == false) ||
184             (RNA_property_array_length(&eye->ptr, eye->prop) < 3) ||
185             (RNA_property_type(eye->prop) != PROP_FLOAT))
186         {
187                 return false;
188         }
189
190         if (RNA_property_subtype(eye->prop) == PROP_COLOR) {
191                 const char *display_device;
192
193                 display_device = scene->display_settings.display_device;
194                 eye->display = IMB_colormanagement_display_get_named(display_device);
195         }
196
197         return true;
198 }
199
200 static void eyedropper_exit(bContext *C, wmOperator *op)
201 {
202         WM_cursor_modal_restore(CTX_wm_window(C));
203
204         if (op->customdata) {
205                 MEM_freeN(op->customdata);
206                 op->customdata = NULL;
207         }
208 }
209
210 static void eyedropper_cancel(bContext *C, wmOperator *op)
211 {
212         eyedropper_exit(C, op);
213 }
214
215 /* *** eyedropper_color_ helper functions *** */
216
217 /**
218  * \brief get the color from the screen.
219  *
220  * Special check for image or nodes where we MAY have HDR pixels which don't display.
221  */
222 static void eyedropper_color_sample_fl(bContext *C, Eyedropper *UNUSED(eye), int mx, int my, float r_col[3])
223 {
224
225         /* we could use some clever */
226         wmWindow *win = CTX_wm_window(C);
227         ScrArea *sa = BKE_screen_find_area_xy(win->screen, SPACE_TYPE_ANY, mx, my);
228
229         if (sa) {
230                 if (sa->spacetype == SPACE_IMAGE) {
231                         ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my);
232                         if (ar) {
233                                 SpaceImage *sima = sa->spacedata.first;
234                                 int mval[2] = {mx - ar->winrct.xmin,
235                                                my - ar->winrct.ymin};
236
237                                 if (ED_space_image_color_sample(CTX_data_scene(C), sima, ar, mval, r_col)) {
238                                         return;
239                                 }
240                         }
241                 }
242                 else if (sa->spacetype == SPACE_NODE) {
243                         ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my);
244                         if (ar) {
245                                 SpaceNode *snode = sa->spacedata.first;
246                                 int mval[2] = {mx - ar->winrct.xmin,
247                                                my - ar->winrct.ymin};
248
249                                 if (ED_space_node_color_sample(CTX_data_scene(C), snode, ar, mval, r_col)) {
250                                         return;
251                                 }
252                         }
253                 }
254                 else if (sa->spacetype == SPACE_CLIP) {
255                         ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my);
256                         if (ar) {
257                                 SpaceClip *sc = sa->spacedata.first;
258                                 int mval[2] = {mx - ar->winrct.xmin,
259                                                my - ar->winrct.ymin};
260
261                                 if (ED_space_clip_color_sample(CTX_data_scene(C), sc, ar, mval, r_col)) {
262                                         return;
263                                 }
264                         }
265                 }
266         }
267
268         /* fallback to simple opengl picker */
269         glReadBuffer(GL_FRONT);
270         glReadPixels(mx, my, 1, 1, GL_RGB, GL_FLOAT, r_col);
271         glReadBuffer(GL_BACK);
272 }
273
274 /* sets the sample color RGB, maintaining A */
275 static void eyedropper_color_set(bContext *C, Eyedropper *eye, const float col[3])
276 {
277         float col_conv[4];
278
279         /* to maintain alpha */
280         RNA_property_float_get_array(&eye->ptr, eye->prop, col_conv);
281
282         /* convert from display space to linear rgb space */
283         if (eye->display) {
284                 copy_v3_v3(col_conv, col);
285                 IMB_colormanagement_display_to_scene_linear_v3(col_conv, eye->display);
286         }
287         else {
288                 copy_v3_v3(col_conv, col);
289         }
290
291         RNA_property_float_set_array(&eye->ptr, eye->prop, col_conv);
292
293         RNA_property_update(C, &eye->ptr, eye->prop);
294 }
295
296 /* set sample from accumulated values */
297 static void eyedropper_color_set_accum(bContext *C, Eyedropper *eye)
298 {
299         float col[3];
300         mul_v3_v3fl(col, eye->accum_col, 1.0f / (float)eye->accum_tot);
301         eyedropper_color_set(C, eye, col);
302 }
303
304 /* single point sample & set */
305 static void eyedropper_color_sample(bContext *C, Eyedropper *eye, int mx, int my)
306 {
307         float col[3];
308         eyedropper_color_sample_fl(C, eye, mx, my, col);
309         eyedropper_color_set(C, eye, col);
310 }
311
312 static void eyedropper_color_sample_accum(bContext *C, Eyedropper *eye, int mx, int my)
313 {
314         float col[3];
315         eyedropper_color_sample_fl(C, eye, mx, my, col);
316         /* delay linear conversion */
317         add_v3_v3(eye->accum_col, col);
318         eye->accum_tot++;
319 }
320
321 /* main modal status check */
322 static int eyedropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
323 {
324         Eyedropper *eye = (Eyedropper *)op->customdata;
325
326         /* handle modal keymap */
327         if (event->type == EVT_MODAL_MAP) {
328                 switch (event->val) {
329                         case EYE_MODAL_CANCEL:
330                                 eyedropper_cancel(C, op);
331                                 return OPERATOR_CANCELLED;
332                         case EYE_MODAL_SAMPLE_CONFIRM:
333                                 if (eye->accum_tot == 0) {
334                                         eyedropper_color_sample(C, eye, event->x, event->y);
335                                 }
336                                 else {
337                                         eyedropper_color_set_accum(C, eye);
338                                 }
339                                 eyedropper_exit(C, op);
340                                 return OPERATOR_FINISHED;
341                         case EYE_MODAL_SAMPLE_BEGIN:
342                                 /* enable accum and make first sample */
343                                 eye->accum_start = true;
344                                 eyedropper_color_sample_accum(C, eye, event->x, event->y);
345                                 break;
346                         case EYE_MODAL_SAMPLE_RESET:
347                                 eye->accum_tot = 0;
348                                 zero_v3(eye->accum_col);
349                                 eyedropper_color_sample_accum(C, eye, event->x, event->y);
350                                 eyedropper_color_set_accum(C, eye);
351                                 break;
352                 }
353         }
354         else if (event->type == MOUSEMOVE) {
355                 if (eye->accum_start) {
356                         /* button is pressed so keep sampling */
357                         eyedropper_color_sample_accum(C, eye, event->x, event->y);
358                         eyedropper_color_set_accum(C, eye);
359                 }
360         }
361
362         return OPERATOR_RUNNING_MODAL;
363 }
364
365 /* Modal Operator init */
366 static int eyedropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
367 {
368         /* init */
369         if (eyedropper_init(C, op)) {
370                 WM_cursor_modal_set(CTX_wm_window(C), BC_EYEDROPPER_CURSOR);
371
372                 /* add temp handler */
373                 WM_event_add_modal_handler(C, op);
374
375                 return OPERATOR_RUNNING_MODAL;
376         }
377         else {
378                 eyedropper_exit(C, op);
379                 return OPERATOR_CANCELLED;
380         }
381 }
382
383 /* Repeat operator */
384 static int eyedropper_exec(bContext *C, wmOperator *op)
385 {
386         /* init */
387         if (eyedropper_init(C, op)) {
388
389                 /* do something */
390
391                 /* cleanup */
392                 eyedropper_exit(C, op);
393
394                 return OPERATOR_FINISHED;
395         }
396         else {
397                 return OPERATOR_CANCELLED;
398         }
399 }
400
401 static int eyedropper_poll(bContext *C)
402 {
403         if (!CTX_wm_window(C)) return 0;
404         else return 1;
405 }
406
407 void UI_OT_eyedropper_color(wmOperatorType *ot)
408 {
409         /* identifiers */
410         ot->name = "Eyedropper";
411         ot->idname = "UI_OT_eyedropper_color";
412         ot->description = "Sample a data-block from the 3D view";
413
414         /* api callbacks */
415         ot->invoke = eyedropper_invoke;
416         ot->modal = eyedropper_modal;
417         ot->cancel = eyedropper_cancel;
418         ot->exec = eyedropper_exec;
419         ot->poll = eyedropper_poll;
420
421         /* flags */
422         ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
423
424         /* properties */
425 }
426
427 /** \} */
428
429
430 /* -------------------------------------------------------------------- */
431 /* Data Dropper */
432
433 /** \name Eyedropper (ID data-blocks)
434  *
435  * \note: datadropper is only internal name to avoid confusion in this file.
436  * \{ */
437
438 typedef struct DataDropper {
439         PointerRNA ptr;
440         PropertyRNA *prop;
441         short idcode;
442         const char *idcode_name;
443
444         ARegionType *art;
445         void *draw_handle_pixel;
446         char name[200];
447 } DataDropper;
448
449
450 static void datadropper_draw_cb(const struct bContext *C, ARegion *ar, void *arg)
451 {
452         DataDropper *ddr = arg;
453         eyedropper_draw_cursor_text(C, ar, ddr->name);
454 }
455
456
457 static int datadropper_init(bContext *C, wmOperator *op)
458 {
459         DataDropper *ddr;
460         int index_dummy;
461         StructRNA *type;
462
463         SpaceType *st;
464         ARegionType *art;
465
466         st = BKE_spacetype_from_id(SPACE_VIEW3D);
467         art = BKE_regiontype_from_id(st, RGN_TYPE_WINDOW);
468
469         op->customdata = ddr = MEM_callocN(sizeof(DataDropper), "DataDropper");
470
471         UI_context_active_but_prop_get(C, &ddr->ptr, &ddr->prop, &index_dummy);
472
473         if ((ddr->ptr.data == NULL) ||
474             (ddr->prop == NULL) ||
475             (RNA_property_editable(&ddr->ptr, ddr->prop) == false) ||
476             (RNA_property_type(ddr->prop) != PROP_POINTER))
477         {
478                 return false;
479         }
480
481         ddr->art = art;
482         ddr->draw_handle_pixel = ED_region_draw_cb_activate(art, datadropper_draw_cb, ddr, REGION_DRAW_POST_PIXEL);
483
484         type = RNA_property_pointer_type(&ddr->ptr, ddr->prop);
485         ddr->idcode = RNA_type_to_ID_code(type);
486         BLI_assert(ddr->idcode != 0);
487         /* Note we can translate here (instead of on draw time), because this struct has very short lifetime. */
488         ddr->idcode_name = TIP_(BKE_idcode_to_name(ddr->idcode));
489
490         return true;
491 }
492
493 static void datadropper_exit(bContext *C, wmOperator *op)
494 {
495         WM_cursor_modal_restore(CTX_wm_window(C));
496
497         if (op->customdata) {
498                 DataDropper *ddr = (DataDropper *)op->customdata;
499
500                 if (ddr->art) {
501                         ED_region_draw_cb_exit(ddr->art, ddr->draw_handle_pixel);
502                 }
503
504                 MEM_freeN(op->customdata);
505
506                 op->customdata = NULL;
507         }
508
509         WM_event_add_mousemove(C);
510 }
511
512 static void datadropper_cancel(bContext *C, wmOperator *op)
513 {
514         datadropper_exit(C, op);
515 }
516
517 /* *** datadropper id helper functions *** */
518 /**
519  * \brief get the ID from the screen.
520  *
521  */
522 static void datadropper_id_sample_pt(bContext *C, DataDropper *ddr, int mx, int my, ID **r_id)
523 {
524
525         /* we could use some clever */
526         wmWindow *win = CTX_wm_window(C);
527         ScrArea *sa = BKE_screen_find_area_xy(win->screen, -1, mx, my);
528
529         ScrArea *area_prev = CTX_wm_area(C);
530         ARegion *ar_prev = CTX_wm_region(C);
531
532         ddr->name[0] = '\0';
533
534         if (sa) {
535                 if (sa->spacetype == SPACE_VIEW3D) {
536                         ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my);
537                         if (ar) {
538                                 const int mval[2] = {
539                                     mx - ar->winrct.xmin,
540                                     my - ar->winrct.ymin};
541                                 Base *base;
542
543                                 CTX_wm_area_set(C, sa);
544                                 CTX_wm_region_set(C, ar);
545
546                                 /* grr, always draw else we leave stale text */
547                                 ED_region_tag_redraw(ar);
548
549                                 base = ED_view3d_give_base_under_cursor(C, mval);
550                                 if (base) {
551                                         Object *ob = base->object;
552                                         ID *id = NULL;
553                                         if (ddr->idcode == ID_OB) {
554                                                 id = (ID *)ob;
555                                         }
556                                         else if (ob->data) {
557                                                 if (GS(((ID *)ob->data)->name) == ddr->idcode) {
558                                                         id = (ID *)ob->data;
559                                                 }
560                                                 else {
561                                                         BLI_snprintf(ddr->name, sizeof(ddr->name), "Incompatible, expected a %s",
562                                                                      ddr->idcode_name);
563                                                 }
564                                         }
565
566                                         if (id) {
567                                                 BLI_snprintf(ddr->name, sizeof(ddr->name), "%s: %s",
568                                                              ddr->idcode_name, id->name + 2);
569                                                 *r_id = id;
570                                         }
571                                 }
572                         }
573                 }
574         }
575
576         CTX_wm_area_set(C, area_prev);
577         CTX_wm_region_set(C, ar_prev);
578 }
579
580 /* sets the ID, returns success */
581 static bool datadropper_id_set(bContext *C, DataDropper *ddr, ID *id)
582 {
583         PointerRNA ptr_value;
584
585         RNA_id_pointer_create(id, &ptr_value);
586
587         RNA_property_pointer_set(&ddr->ptr, ddr->prop, ptr_value);
588
589         RNA_property_update(C, &ddr->ptr, ddr->prop);
590
591         ptr_value = RNA_property_pointer_get(&ddr->ptr, ddr->prop);
592
593         return (ptr_value.id.data == id);
594 }
595
596 /* single point sample & set */
597 static bool datadropper_id_sample(bContext *C, DataDropper *ddr, int mx, int my)
598 {
599         ID *id = NULL;
600
601         datadropper_id_sample_pt(C, ddr, mx, my, &id);
602         return datadropper_id_set(C, ddr, id);
603 }
604
605 /* main modal status check */
606 static int datadropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
607 {
608         DataDropper *ddr = (DataDropper *)op->customdata;
609
610         /* handle modal keymap */
611         if (event->type == EVT_MODAL_MAP) {
612                 switch (event->val) {
613                         case EYE_MODAL_CANCEL:
614                                 datadropper_cancel(C, op);
615                                 return OPERATOR_CANCELLED;
616                         case EYE_MODAL_SAMPLE_CONFIRM:
617                         {
618                                 bool success;
619
620                                 success = datadropper_id_sample(C, ddr, event->x, event->y);
621                                 datadropper_exit(C, op);
622
623                                 if (success) {
624                                         return OPERATOR_FINISHED;
625                                 }
626                                 else {
627                                         BKE_report(op->reports, RPT_WARNING, "Failed to set value");
628                                         return OPERATOR_CANCELLED;
629                                 }
630                         }
631                 }
632         }
633         else if (event->type == MOUSEMOVE) {
634                 ID *id = NULL;
635                 datadropper_id_sample_pt(C, ddr, event->x, event->y, &id);
636         }
637
638         return OPERATOR_RUNNING_MODAL;
639 }
640
641 /* Modal Operator init */
642 static int datadropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
643 {
644         /* init */
645         if (datadropper_init(C, op)) {
646                 WM_cursor_modal_set(CTX_wm_window(C), BC_EYEDROPPER_CURSOR);
647
648                 /* add temp handler */
649                 WM_event_add_modal_handler(C, op);
650
651                 return OPERATOR_RUNNING_MODAL;
652         }
653         else {
654                 datadropper_exit(C, op);
655                 return OPERATOR_CANCELLED;
656         }
657 }
658
659 /* Repeat operator */
660 static int datadropper_exec(bContext *C, wmOperator *op)
661 {
662         /* init */
663         if (datadropper_init(C, op)) {
664                 /* cleanup */
665                 datadropper_exit(C, op);
666
667                 return OPERATOR_FINISHED;
668         }
669         else {
670                 return OPERATOR_CANCELLED;
671         }
672 }
673
674 static int datadropper_poll(bContext *C)
675 {
676         if (!CTX_wm_window(C)) return 0;
677         else return 1;
678 }
679
680 void UI_OT_eyedropper_id(wmOperatorType *ot)
681 {
682         /* identifiers */
683         ot->name = "Eyedropper Datablock";
684         ot->idname = "UI_OT_eyedropper_id";
685         ot->description = "Sample a datablock from the Blender Window to store in a property";
686
687         /* api callbacks */
688         ot->invoke = datadropper_invoke;
689         ot->modal = datadropper_modal;
690         ot->cancel = datadropper_cancel;
691         ot->exec = datadropper_exec;
692         ot->poll = datadropper_poll;
693
694         /* flags */
695         ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
696
697         /* properties */
698 }
699
700 /** \} */
701
702
703 /* -------------------------------------------------------------------- */
704 /* Depth Dropper */
705
706 /** \name Eyedropper (Depth)
707  *
708  * \note: depthdropper is only internal name to avoid confusion in this file.
709  * \{ */
710
711 typedef struct DepthDropper {
712         PointerRNA ptr;
713         PropertyRNA *prop;
714
715         bool  accum_start; /* has mouse been presed */
716         float accum_depth;
717         int   accum_tot;
718
719         ARegionType *art;
720         void *draw_handle_pixel;
721         char name[200];
722 } DepthDropper;
723
724
725 static void depthdropper_draw_cb(const struct bContext *C, ARegion *ar, void *arg)
726 {
727         DepthDropper *ddr = arg;
728         eyedropper_draw_cursor_text(C, ar, ddr->name);
729 }
730
731
732 static int depthdropper_init(bContext *C, wmOperator *op)
733 {
734         DepthDropper *ddr;
735         int index_dummy;
736
737         SpaceType *st;
738         ARegionType *art;
739
740         st = BKE_spacetype_from_id(SPACE_VIEW3D);
741         art = BKE_regiontype_from_id(st, RGN_TYPE_WINDOW);
742
743         op->customdata = ddr = MEM_callocN(sizeof(DepthDropper), "DepthDropper");
744
745         UI_context_active_but_prop_get(C, &ddr->ptr, &ddr->prop, &index_dummy);
746
747         /* fallback to the active camera's dof */
748         if (ddr->prop == NULL) {
749                 RegionView3D *rv3d = CTX_wm_region_view3d(C);
750                 if (rv3d && rv3d->persp == RV3D_CAMOB) {
751                         View3D *v3d = CTX_wm_view3d(C);
752                         if (v3d->camera && v3d->camera->data && (((ID *)v3d->camera->data)->lib == NULL)) {
753                                 RNA_id_pointer_create(v3d->camera->data, &ddr->ptr);
754                                 ddr->prop = RNA_struct_find_property(&ddr->ptr, "dof_distance");
755                         }
756                 }
757         }
758
759         if ((ddr->ptr.data == NULL) ||
760             (ddr->prop == NULL) ||
761             (RNA_property_editable(&ddr->ptr, ddr->prop) == false) ||
762             (RNA_property_type(ddr->prop) != PROP_FLOAT))
763         {
764                 return false;
765         }
766
767         ddr->art = art;
768         ddr->draw_handle_pixel = ED_region_draw_cb_activate(art, depthdropper_draw_cb, ddr, REGION_DRAW_POST_PIXEL);
769
770         return true;
771 }
772
773 static void depthdropper_exit(bContext *C, wmOperator *op)
774 {
775         WM_cursor_modal_restore(CTX_wm_window(C));
776
777         if (op->customdata) {
778                 DepthDropper *ddr = (DepthDropper *)op->customdata;
779
780                 if (ddr->art) {
781                         ED_region_draw_cb_exit(ddr->art, ddr->draw_handle_pixel);
782                 }
783
784                 MEM_freeN(op->customdata);
785
786                 op->customdata = NULL;
787         }
788 }
789
790 static void depthdropper_cancel(bContext *C, wmOperator *op)
791 {
792         depthdropper_exit(C, op);
793 }
794
795 /* *** depthdropper id helper functions *** */
796 /**
797  * \brief get the ID from the screen.
798  *
799  */
800 static void depthdropper_depth_sample_pt(bContext *C, DepthDropper *ddr, int mx, int my, float *r_depth)
801 {
802
803         /* we could use some clever */
804         wmWindow *win = CTX_wm_window(C);
805         ScrArea *sa = BKE_screen_find_area_xy(win->screen, SPACE_TYPE_ANY, mx, my);
806         Scene *scene = win->screen->scene;
807         UnitSettings *unit = &scene->unit;
808         const bool do_split = (unit->flag & USER_UNIT_OPT_SPLIT) != 0;
809
810         ScrArea *area_prev = CTX_wm_area(C);
811         ARegion *ar_prev = CTX_wm_region(C);
812
813         ddr->name[0] = '\0';
814
815         if (sa) {
816                 if (sa->spacetype == SPACE_VIEW3D) {
817                         ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my);
818                         if (ar) {
819                                 View3D *v3d = sa->spacedata.first;
820                                 RegionView3D *rv3d = ar->regiondata;
821                                 /* weak, we could pass in some reference point */
822                                 const float *view_co = v3d->camera ? v3d->camera->obmat[3] : rv3d->viewinv[3];
823                                 const int mval[2] = {
824                                     mx - ar->winrct.xmin,
825                                     my - ar->winrct.ymin};
826                                 float co[3];
827
828                                 CTX_wm_area_set(C, sa);
829                                 CTX_wm_region_set(C, ar);
830
831                                 /* grr, always draw else we leave stale text */
832                                 ED_region_tag_redraw(ar);
833
834                                 view3d_operator_needs_opengl(C);
835
836                                 if (ED_view3d_autodist(scene, ar, v3d, mval, co, true, NULL)) {
837                                         const float mval_center_fl[2] = {
838                                             (float)ar->winx / 2,
839                                             (float)ar->winy / 2};
840                                         float co_align[3];
841
842                                         /* quick way to get view-center aligned point */
843                                         ED_view3d_win_to_3d(ar, co, mval_center_fl, co_align);
844
845                                         *r_depth = len_v3v3(view_co, co_align);
846
847                                         bUnit_AsString(ddr->name, sizeof(ddr->name),
848                                                        (double)*r_depth,
849                                                        4, unit->system, B_UNIT_LENGTH, do_split, false);
850                                 }
851                                 else {
852                                         BLI_strncpy(ddr->name, "Nothing under cursor", sizeof(ddr->name));
853                                 }
854                         }
855                 }
856         }
857
858         CTX_wm_area_set(C, area_prev);
859         CTX_wm_region_set(C, ar_prev);
860 }
861
862 /* sets the sample depth RGB, maintaining A */
863 static void depthdropper_depth_set(bContext *C, DepthDropper *ddr, const float depth)
864 {
865         RNA_property_float_set(&ddr->ptr, ddr->prop, depth);
866         RNA_property_update(C, &ddr->ptr, ddr->prop);
867 }
868
869 /* set sample from accumulated values */
870 static void depthdropper_depth_set_accum(bContext *C, DepthDropper *ddr)
871 {
872         float depth = ddr->accum_depth;
873         if (ddr->accum_tot) {
874                 depth /= (float)ddr->accum_tot;
875         }
876         depthdropper_depth_set(C, ddr, depth);
877 }
878
879 /* single point sample & set */
880 static void depthdropper_depth_sample(bContext *C, DepthDropper *ddr, int mx, int my)
881 {
882         float depth = -1.0f;
883         if (depth != -1.0f) {
884                 depthdropper_depth_sample_pt(C, ddr, mx, my, &depth);
885                 depthdropper_depth_set(C, ddr, depth);
886         }
887 }
888
889 static void depthdropper_depth_sample_accum(bContext *C, DepthDropper *ddr, int mx, int my)
890 {
891         float depth = -1.0f;
892         depthdropper_depth_sample_pt(C, ddr, mx, my, &depth);
893         if (depth != -1.0f) {
894                 ddr->accum_depth += depth;
895                 ddr->accum_tot++;
896         }
897 }
898
899 /* main modal status check */
900 static int depthdropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
901 {
902         DepthDropper *ddr = (DepthDropper *)op->customdata;
903
904         /* handle modal keymap */
905         if (event->type == EVT_MODAL_MAP) {
906                 switch (event->val) {
907                         case EYE_MODAL_CANCEL:
908                                 depthdropper_cancel(C, op);
909                                 return OPERATOR_CANCELLED;
910                         case EYE_MODAL_SAMPLE_CONFIRM:
911                                 if (ddr->accum_tot == 0) {
912                                         depthdropper_depth_sample(C, ddr, event->x, event->y);
913                                 }
914                                 else {
915                                         depthdropper_depth_set_accum(C, ddr);
916                                 }
917                                 depthdropper_exit(C, op);
918                                 return OPERATOR_FINISHED;
919                         case EYE_MODAL_SAMPLE_BEGIN:
920                                 /* enable accum and make first sample */
921                                 ddr->accum_start = true;
922                                 depthdropper_depth_sample_accum(C, ddr, event->x, event->y);
923                                 break;
924                         case EYE_MODAL_SAMPLE_RESET:
925                                 ddr->accum_tot = 0;
926                                 ddr->accum_depth = 0.0f;
927                                 depthdropper_depth_sample_accum(C, ddr, event->x, event->y);
928                                 depthdropper_depth_set_accum(C, ddr);
929                                 break;
930                 }
931         }
932         else if (event->type == MOUSEMOVE) {
933                 if (ddr->accum_start) {
934                         /* button is pressed so keep sampling */
935                         depthdropper_depth_sample_accum(C, ddr, event->x, event->y);
936                         depthdropper_depth_set_accum(C, ddr);
937                 }
938         }
939
940         return OPERATOR_RUNNING_MODAL;
941 }
942
943 /* Modal Operator init */
944 static int depthdropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
945 {
946         /* init */
947         if (depthdropper_init(C, op)) {
948                 WM_cursor_modal_set(CTX_wm_window(C), BC_EYEDROPPER_CURSOR);
949
950                 /* add temp handler */
951                 WM_event_add_modal_handler(C, op);
952
953                 return OPERATOR_RUNNING_MODAL;
954         }
955         else {
956                 depthdropper_exit(C, op);
957                 return OPERATOR_CANCELLED;
958         }
959 }
960
961 /* Repeat operator */
962 static int depthdropper_exec(bContext *C, wmOperator *op)
963 {
964         /* init */
965         if (depthdropper_init(C, op)) {
966                 /* cleanup */
967                 depthdropper_exit(C, op);
968
969                 return OPERATOR_FINISHED;
970         }
971         else {
972                 return OPERATOR_CANCELLED;
973         }
974 }
975
976 static int depthdropper_poll(bContext *C)
977 {
978         if (!CTX_wm_window(C)) return 0;
979         else return 1;
980 }
981
982 void UI_OT_eyedropper_depth(wmOperatorType *ot)
983 {
984         /* identifiers */
985         ot->name = "Eyedropper Depth";
986         ot->idname = "UI_OT_eyedropper_depth";
987         ot->description = "Sample depth from the 3D view";
988
989         /* api callbacks */
990         ot->invoke = depthdropper_invoke;
991         ot->modal = depthdropper_modal;
992         ot->cancel = depthdropper_cancel;
993         ot->exec = depthdropper_exec;
994         ot->poll = depthdropper_poll;
995
996         /* flags */
997         ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
998
999         /* properties */
1000 }
1001
1002 /** \} */