a01c7eecda1faaca54c5d02545de433b765fdb3d
[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 "BKE_context.h"
40 #include "BKE_screen.h"
41 #include "BKE_report.h"
42 #include "BKE_idcode.h"
43
44 #include "RNA_access.h"
45
46 #include "BIF_gl.h"
47
48 #include "UI_interface.h"
49
50 #include "IMB_colormanagement.h"
51
52 #include "WM_api.h"
53 #include "WM_types.h"
54
55 #include "interface_intern.h"
56
57 /* for HDR color sampling */
58 #include "ED_image.h"
59 #include "ED_node.h"
60 #include "ED_clip.h"
61
62 /* for ID data eyedropper */
63 #include "ED_space_api.h"
64 #include "ED_screen.h"
65 #include "ED_view3d.h"
66
67
68 /* -------------------------------------------------------------------- */
69 /* Eyedropper
70  */
71
72 /** \name Eyedropper (RGB Color)
73  * \{ */
74
75 typedef struct Eyedropper {
76         struct ColorManagedDisplay *display;
77
78         PointerRNA ptr;
79         PropertyRNA *prop;
80         int index;
81
82         int   accum_start; /* has mouse been presed */
83         float accum_col[3];
84         int   accum_tot;
85 } Eyedropper;
86
87 static int eyedropper_init(bContext *C, wmOperator *op)
88 {
89         Scene *scene = CTX_data_scene(C);
90         Eyedropper *eye;
91
92         op->customdata = eye = MEM_callocN(sizeof(Eyedropper), "Eyedropper");
93
94         uiContextActiveProperty(C, &eye->ptr, &eye->prop, &eye->index);
95
96         if ((eye->ptr.data == NULL) ||
97             (eye->prop == NULL) ||
98             (RNA_property_editable(&eye->ptr, eye->prop) == FALSE) ||
99             (RNA_property_array_length(&eye->ptr, eye->prop) < 3) ||
100             (RNA_property_type(eye->prop) != PROP_FLOAT))
101         {
102                 return FALSE;
103         }
104
105         if (RNA_property_subtype(eye->prop) == PROP_COLOR) {
106                 const char *display_device;
107
108                 display_device = scene->display_settings.display_device;
109                 eye->display = IMB_colormanagement_display_get_named(display_device);
110         }
111
112         return TRUE;
113 }
114
115 static void eyedropper_exit(bContext *C, wmOperator *op)
116 {
117         WM_cursor_modal_restore(CTX_wm_window(C));
118
119         if (op->customdata) {
120                 MEM_freeN(op->customdata);
121                 op->customdata = NULL;
122         }
123 }
124
125 static void eyedropper_cancel(bContext *C, wmOperator *op)
126 {
127         eyedropper_exit(C, op);
128 }
129
130 /* *** eyedropper_color_ helper functions *** */
131
132 /**
133  * \brief get the color from the screen.
134  *
135  * Special check for image or nodes where we MAY have HDR pixels which don't display.
136  */
137 static void eyedropper_color_sample_fl(bContext *C, Eyedropper *UNUSED(eye), int mx, int my, float r_col[3])
138 {
139
140         /* we could use some clever */
141         wmWindow *win = CTX_wm_window(C);
142         ScrArea *sa;
143         for (sa = win->screen->areabase.first; sa; sa = sa->next) {
144                 if (BLI_rcti_isect_pt(&sa->totrct, mx, my)) {
145                         if (sa->spacetype == SPACE_IMAGE) {
146                                 ARegion *ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW);
147                                 if (ar && BLI_rcti_isect_pt(&ar->winrct, mx, my)) {
148                                         SpaceImage *sima = sa->spacedata.first;
149                                         int mval[2] = {mx - ar->winrct.xmin,
150                                                        my - ar->winrct.ymin};
151
152                                         if (ED_space_image_color_sample(sima, ar, mval, r_col)) {
153                                                 return;
154                                         }
155                                 }
156                         }
157                         else if (sa->spacetype == SPACE_NODE) {
158                                 ARegion *ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW);
159                                 if (ar && BLI_rcti_isect_pt(&ar->winrct, mx, my)) {
160                                         SpaceNode *snode = sa->spacedata.first;
161                                         int mval[2] = {mx - ar->winrct.xmin,
162                                                        my - ar->winrct.ymin};
163
164                                         if (ED_space_node_color_sample(snode, ar, mval, r_col)) {
165                                                 return;
166                                         }
167                                 }
168                         }
169                         else if (sa->spacetype == SPACE_CLIP) {
170                                 ARegion *ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW);
171                                 if (ar && BLI_rcti_isect_pt(&ar->winrct, mx, my)) {
172                                         SpaceClip *sc = sa->spacedata.first;
173                                         int mval[2] = {mx - ar->winrct.xmin,
174                                                        my - ar->winrct.ymin};
175
176                                         if (ED_space_clip_color_sample(sc, ar, mval, r_col)) {
177                                                 return;
178                                         }
179                                 }
180                         }
181                 }
182         }
183
184         /* fallback to simple opengl picker */
185         glReadBuffer(GL_FRONT);
186         glReadPixels(mx, my, 1, 1, GL_RGB, GL_FLOAT, r_col);
187         glReadBuffer(GL_BACK);
188 }
189
190 /* sets the sample color RGB, maintaining A */
191 static void eyedropper_color_set(bContext *C, Eyedropper *eye, const float col[3])
192 {
193         float col_conv[4];
194
195         /* to maintain alpha */
196         RNA_property_float_get_array(&eye->ptr, eye->prop, col_conv);
197
198         /* convert from display space to linear rgb space */
199         if (eye->display) {
200                 copy_v3_v3(col_conv, col);
201                 IMB_colormanagement_display_to_scene_linear_v3(col_conv, eye->display);
202         }
203         else {
204                 copy_v3_v3(col_conv, col);
205         }
206
207         RNA_property_float_set_array(&eye->ptr, eye->prop, col_conv);
208
209         RNA_property_update(C, &eye->ptr, eye->prop);
210 }
211
212 /* set sample from accumulated values */
213 static void eyedropper_color_set_accum(bContext *C, Eyedropper *eye)
214 {
215         float col[3];
216         mul_v3_v3fl(col, eye->accum_col, 1.0f / (float)eye->accum_tot);
217         eyedropper_color_set(C, eye, col);
218 }
219
220 /* single point sample & set */
221 static void eyedropper_color_sample(bContext *C, Eyedropper *eye, int mx, int my)
222 {
223         float col[3];
224         eyedropper_color_sample_fl(C, eye, mx, my, col);
225         eyedropper_color_set(C, eye, col);
226 }
227
228 static void eyedropper_color_sample_accum(bContext *C, Eyedropper *eye, int mx, int my)
229 {
230         float col[3];
231         eyedropper_color_sample_fl(C, eye, mx, my, col);
232         /* delay linear conversion */
233         add_v3_v3(eye->accum_col, col);
234         eye->accum_tot++;
235 }
236
237 /* main modal status check */
238 static int eyedropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
239 {
240         Eyedropper *eye = (Eyedropper *)op->customdata;
241
242         switch (event->type) {
243                 case ESCKEY:
244                 case RIGHTMOUSE:
245                         eyedropper_cancel(C, op);
246                         return OPERATOR_CANCELLED;
247                 case LEFTMOUSE:
248                         if (event->val == KM_RELEASE) {
249                                 if (eye->accum_tot == 0) {
250                                         eyedropper_color_sample(C, eye, event->x, event->y);
251                                 }
252                                 else {
253                                         eyedropper_color_set_accum(C, eye);
254                                 }
255                                 eyedropper_exit(C, op);
256                                 return OPERATOR_FINISHED;
257                         }
258                         else if (event->val == KM_PRESS) {
259                                 /* enable accum and make first sample */
260                                 eye->accum_start = TRUE;
261                                 eyedropper_color_sample_accum(C, eye, event->x, event->y);
262                         }
263                         break;
264                 case MOUSEMOVE:
265                         if (eye->accum_start) {
266                                 /* button is pressed so keep sampling */
267                                 eyedropper_color_sample_accum(C, eye, event->x, event->y);
268                                 eyedropper_color_set_accum(C, eye);
269                         }
270                         break;
271                 case SPACEKEY:
272                         if (event->val == KM_RELEASE) {
273                                 eye->accum_tot = 0;
274                                 zero_v3(eye->accum_col);
275                                 eyedropper_color_sample_accum(C, eye, event->x, event->y);
276                                 eyedropper_color_set_accum(C, eye);
277                         }
278                         break;
279         }
280
281         return OPERATOR_RUNNING_MODAL;
282 }
283
284 /* Modal Operator init */
285 static int eyedropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
286 {
287         /* init */
288         if (eyedropper_init(C, op)) {
289                 WM_cursor_modal_set(CTX_wm_window(C), BC_EYEDROPPER_CURSOR);
290
291                 /* add temp handler */
292                 WM_event_add_modal_handler(C, op);
293
294                 return OPERATOR_RUNNING_MODAL;
295         }
296         else {
297                 eyedropper_exit(C, op);
298                 return OPERATOR_CANCELLED;
299         }
300 }
301
302 /* Repeat operator */
303 static int eyedropper_exec(bContext *C, wmOperator *op)
304 {
305         /* init */
306         if (eyedropper_init(C, op)) {
307
308                 /* do something */
309
310                 /* cleanup */
311                 eyedropper_exit(C, op);
312
313                 return OPERATOR_FINISHED;
314         }
315         else {
316                 return OPERATOR_CANCELLED;
317         }
318 }
319
320 static int eyedropper_poll(bContext *C)
321 {
322         if (!CTX_wm_window(C)) return 0;
323         else return 1;
324 }
325
326 void UI_OT_eyedropper_color(wmOperatorType *ot)
327 {
328         /* identifiers */
329         ot->name = "Eyedropper";
330         ot->idname = "UI_OT_eyedropper_color";
331         ot->description = "Sample a color from the Blender Window to store in a property";
332
333         /* api callbacks */
334         ot->invoke = eyedropper_invoke;
335         ot->modal = eyedropper_modal;
336         ot->cancel = eyedropper_cancel;
337         ot->exec = eyedropper_exec;
338         ot->poll = eyedropper_poll;
339
340         /* flags */
341         ot->flag = OPTYPE_BLOCKING;
342
343         /* properties */
344 }
345 /** \} */
346
347
348 /* -------------------------------------------------------------------- */
349 /* Data Dropper
350  *
351  * note: datadropper is only internal name to avoid confusion in this file
352  */
353
354 /** \name Eyedropper (ID data-blocks)
355  * \{ */
356
357 typedef struct DataDropper {
358         PointerRNA ptr;
359         PropertyRNA *prop;
360         short idcode;
361         const char *idcode_name;
362
363         ARegionType *art;
364         void *draw_handle_pixel;
365         char name[200];
366 } DataDropper;
367
368
369 static void datadropper_draw_cb(const struct bContext *C, ARegion *ar, void *arg)
370 {
371         DataDropper *ddr = arg;
372         int width;
373         const char *name = ddr->name;
374         wmWindow *win = CTX_wm_window(C);
375         int x = win->eventstate->x;
376         int y = win->eventstate->y;
377
378         if ((name[0] == '\0') ||
379             (BLI_rcti_isect_pt(&ar->winrct, x, y) == false))
380         {
381                 return;
382         }
383
384         width = UI_GetStringWidth(name);
385         x = x - ar->winrct.xmin;
386         y = y - ar->winrct.ymin;
387
388         y += 20;
389
390         glColor4ub(0, 0, 0, 50);
391
392         uiSetRoundBox(UI_CNR_ALL | UI_RB_ALPHA);
393         uiRoundBox(x, y, x + width + 8, y + 15, 4);
394
395         glColor4ub(255, 255, 255, 255);
396         UI_DrawString(x + 4, y + 4, name);
397 }
398
399
400 static int datadropper_init(bContext *C, wmOperator *op)
401 {
402         DataDropper *ddr;
403         int index_dummy;
404         StructRNA *type;
405
406         SpaceType *st;
407         ARegionType *art;
408
409         st = BKE_spacetype_from_id(SPACE_VIEW3D);
410         art = BKE_regiontype_from_id(st, RGN_TYPE_WINDOW);
411
412         op->customdata = ddr = MEM_callocN(sizeof(DataDropper), "DataDropper");
413
414         uiContextActiveProperty(C, &ddr->ptr, &ddr->prop, &index_dummy);
415
416         if ((ddr->ptr.data == NULL) ||
417             (ddr->prop == NULL) ||
418             (RNA_property_editable(&ddr->ptr, ddr->prop) == false) ||
419             (RNA_property_type(ddr->prop) != PROP_POINTER))
420         {
421                 return false;
422         }
423
424         ddr->art = art;
425         ddr->draw_handle_pixel = ED_region_draw_cb_activate(art, datadropper_draw_cb, ddr, REGION_DRAW_POST_PIXEL);
426
427         type = RNA_property_pointer_type(&ddr->ptr, ddr->prop);
428         ddr->idcode = RNA_type_to_ID_code(type);
429         BLI_assert(ddr->idcode != 0);
430         ddr->idcode_name = BKE_idcode_to_name(ddr->idcode);
431
432         return true;
433 }
434
435 static void datadropper_exit(bContext *C, wmOperator *op)
436 {
437         WM_cursor_modal_restore(CTX_wm_window(C));
438
439         if (op->customdata) {
440                 DataDropper *ddr = (DataDropper *)op->customdata;
441
442                 if (ddr->art) {
443                         ED_region_draw_cb_exit(ddr->art, ddr->draw_handle_pixel);
444                 }
445
446                 MEM_freeN(op->customdata);
447
448                 op->customdata = NULL;
449         }
450 }
451
452 static void datadropper_cancel(bContext *C, wmOperator *op)
453 {
454         datadropper_exit(C, op);
455 }
456
457 /* *** datadropper id helper functions *** */
458 /**
459  * \brief get the ID from the screen.
460  *
461  */
462 static void datadropper_id_sample_pt(bContext *C, DataDropper *ddr, int mx, int my, ID **r_id)
463 {
464
465         /* we could use some clever */
466         wmWindow *win = CTX_wm_window(C);
467         ScrArea *sa;
468
469         ScrArea *area_prev = CTX_wm_area(C);
470         ARegion *ar_prev = CTX_wm_region(C);
471
472         ddr->name[0] = '\0';
473
474         for (sa = win->screen->areabase.first; sa; sa = sa->next) {
475                 if (BLI_rcti_isect_pt(&sa->totrct, mx, my)) {
476                         if (sa->spacetype == SPACE_VIEW3D) {
477                                 ARegion *ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW);
478                                 if (ar && BLI_rcti_isect_pt(&ar->winrct, mx, my)) {
479                                         int mval[2] = {mx - ar->winrct.xmin,
480                                                        my - ar->winrct.ymin};
481                                         Base *base;
482
483                                         CTX_wm_area_set(C, sa);
484                                         CTX_wm_region_set(C, ar);
485
486                                         /* grr, always draw else we leave stale text */
487                                         ED_region_tag_redraw(ar);
488
489                                         base = ED_view3d_give_base_under_cursor(C, mval);
490                                         if (base) {
491                                                 Object *ob = base->object;
492                                                 ID *id = NULL;
493                                                 if (ddr->idcode == ID_OB) {
494                                                         id = (ID *)ob;
495                                                 }
496                                                 else if (ob->data) {
497                                                         if (GS(((ID *)ob->data)->name) == ddr->idcode) {
498                                                                 id = (ID *)ob->data;
499                                                         }
500                                                         else {
501                                                                 BLI_snprintf(ddr->name, sizeof(ddr->name), "Incompatible, expected a %s",
502                                                                              ddr->idcode_name);
503                                                         }
504                                                 }
505
506                                                 if (id) {
507                                                         BLI_snprintf(ddr->name, sizeof(ddr->name), "%s: %s",
508                                                                      ddr->idcode_name, id->name + 2);
509                                                         *r_id = id;
510                                                 }
511
512                                                 break;
513                                         }
514                                 }
515                         }
516                 }
517         }
518
519         CTX_wm_area_set(C, area_prev);
520         CTX_wm_region_set(C, ar_prev);
521 }
522
523 /* sets the ID, returns success */
524 static bool datadropper_id_set(bContext *C, DataDropper *ddr, ID *id)
525 {
526         PointerRNA ptr_value;
527
528         RNA_id_pointer_create(id, &ptr_value);
529
530         RNA_property_pointer_set(&ddr->ptr, ddr->prop, ptr_value);
531
532         RNA_property_update(C, &ddr->ptr, ddr->prop);
533
534         ptr_value = RNA_property_pointer_get(&ddr->ptr, ddr->prop);
535
536         return (ptr_value.id.data == id);
537 }
538
539 /* single point sample & set */
540 static bool datadropper_id_sample(bContext *C, DataDropper *ddr, int mx, int my)
541 {
542         ID *id = NULL;
543
544         datadropper_id_sample_pt(C, ddr, mx, my, &id);
545         return datadropper_id_set(C, ddr, id);
546 }
547
548 /* main modal status check */
549 static int datadropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
550 {
551         DataDropper *ddr = (DataDropper *)op->customdata;
552
553         switch (event->type) {
554                 case ESCKEY:
555                 case RIGHTMOUSE:
556                         datadropper_cancel(C, op);
557                         return OPERATOR_CANCELLED;
558                 case LEFTMOUSE:
559                         if (event->val == KM_RELEASE) {
560                                 bool success;
561
562                                 success = datadropper_id_sample(C, ddr, event->x, event->y);
563                                 datadropper_exit(C, op);
564
565                                 if (success) {
566                                         return OPERATOR_FINISHED;
567                                 }
568                                 else {
569                                         BKE_report(op->reports, RPT_WARNING, "Failed to set value");
570                                         return OPERATOR_CANCELLED;
571                                 }
572                         }
573                         break;
574                 case MOUSEMOVE:
575                 {
576                         ID *id = NULL;
577                         datadropper_id_sample_pt(C, ddr, event->x, event->y, &id);
578                         break;
579                 }
580         }
581
582         return OPERATOR_RUNNING_MODAL;
583 }
584
585 /* Modal Operator init */
586 static int datadropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
587 {
588         /* init */
589         if (datadropper_init(C, op)) {
590                 WM_cursor_modal_set(CTX_wm_window(C), BC_EYEDROPPER_CURSOR);
591
592                 /* add temp handler */
593                 WM_event_add_modal_handler(C, op);
594
595                 return OPERATOR_RUNNING_MODAL;
596         }
597         else {
598                 datadropper_exit(C, op);
599                 return OPERATOR_CANCELLED;
600         }
601 }
602
603 /* Repeat operator */
604 static int datadropper_exec(bContext *C, wmOperator *op)
605 {
606         /* init */
607         if (datadropper_init(C, op)) {
608                 /* cleanup */
609                 datadropper_exit(C, op);
610
611                 return OPERATOR_FINISHED;
612         }
613         else {
614                 return OPERATOR_CANCELLED;
615         }
616 }
617
618 static int datadropper_poll(bContext *C)
619 {
620         if (!CTX_wm_window(C)) return 0;
621         else return 1;
622 }
623
624 void UI_OT_eyedropper_id(wmOperatorType *ot)
625 {
626         /* identifiers */
627         ot->name = "Eyedropper Datablock";
628         ot->idname = "UI_OT_eyedropper_id";
629         ot->description = "Sample a color from the Blender Window to store in a property";
630
631         /* api callbacks */
632         ot->invoke = datadropper_invoke;
633         ot->modal = datadropper_modal;
634         ot->cancel = datadropper_cancel;
635         ot->exec = datadropper_exec;
636         ot->poll = datadropper_poll;
637
638         /* flags */
639         ot->flag = OPTYPE_BLOCKING;
640
641         /* properties */
642 }
643
644 /** \} */