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