Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / interface / interface_eyedropper_datablock.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  * ***** END GPL LICENSE BLOCK *****
22  */
23
24 /** \file blender/editors/interface/interface_eyedropper_datablock.c
25  *  \ingroup edinterface
26  *
27  * Eyedropper (ID data-blocks)
28  *
29  * Defines:
30  * - #UI_OT_eyedropper_id
31  */
32
33 #include "MEM_guardedalloc.h"
34
35 #include "DNA_space_types.h"
36 #include "DNA_screen_types.h"
37 #include "DNA_object_types.h"
38
39 #include "BLI_string.h"
40 #include "BLI_math_vector.h"
41
42 #include "BLT_translation.h"
43
44 #include "BKE_context.h"
45 #include "BKE_screen.h"
46 #include "BKE_report.h"
47 #include "BKE_idcode.h"
48
49 #include "RNA_access.h"
50
51 #include "UI_interface.h"
52
53 #include "WM_api.h"
54 #include "WM_types.h"
55
56 #include "ED_space_api.h"
57 #include "ED_screen.h"
58 #include "ED_view3d.h"
59
60 #include "interface_intern.h"
61 #include "interface_eyedropper_intern.h"
62
63 /**
64  * \note #DataDropper is only internal name to avoid confusion with other kinds of eye-droppers.
65  */
66 typedef struct DataDropper {
67         PointerRNA ptr;
68         PropertyRNA *prop;
69         short idcode;
70         const char *idcode_name;
71
72         ID *init_id; /* for resetting on cancel */
73
74         ARegionType *art;
75         void *draw_handle_pixel;
76         char name[200];
77 } DataDropper;
78
79
80 static void datadropper_draw_cb(const struct bContext *C, ARegion *ar, void *arg)
81 {
82         DataDropper *ddr = arg;
83         eyedropper_draw_cursor_text(C, ar, ddr->name);
84 }
85
86
87 static int datadropper_init(bContext *C, wmOperator *op)
88 {
89         DataDropper *ddr;
90         int index_dummy;
91         StructRNA *type;
92
93         SpaceType *st;
94         ARegionType *art;
95
96         st = BKE_spacetype_from_id(SPACE_VIEW3D);
97         art = BKE_regiontype_from_id(st, RGN_TYPE_WINDOW);
98
99         op->customdata = ddr = MEM_callocN(sizeof(DataDropper), "DataDropper");
100
101         UI_context_active_but_prop_get(C, &ddr->ptr, &ddr->prop, &index_dummy);
102
103         if ((ddr->ptr.data == NULL) ||
104             (ddr->prop == NULL) ||
105             (RNA_property_editable(&ddr->ptr, ddr->prop) == false) ||
106             (RNA_property_type(ddr->prop) != PROP_POINTER))
107         {
108                 return false;
109         }
110
111         ddr->art = art;
112         ddr->draw_handle_pixel = ED_region_draw_cb_activate(art, datadropper_draw_cb, ddr, REGION_DRAW_POST_PIXEL);
113
114         type = RNA_property_pointer_type(&ddr->ptr, ddr->prop);
115         ddr->idcode = RNA_type_to_ID_code(type);
116         BLI_assert(ddr->idcode != 0);
117         /* Note we can translate here (instead of on draw time), because this struct has very short lifetime. */
118         ddr->idcode_name = TIP_(BKE_idcode_to_name(ddr->idcode));
119
120         PointerRNA ptr = RNA_property_pointer_get(&ddr->ptr, ddr->prop);
121         ddr->init_id = ptr.id.data;
122
123         return true;
124 }
125
126 static void datadropper_exit(bContext *C, wmOperator *op)
127 {
128         WM_cursor_modal_restore(CTX_wm_window(C));
129
130         if (op->customdata) {
131                 DataDropper *ddr = (DataDropper *)op->customdata;
132
133                 if (ddr->art) {
134                         ED_region_draw_cb_exit(ddr->art, ddr->draw_handle_pixel);
135                 }
136
137                 MEM_freeN(op->customdata);
138
139                 op->customdata = NULL;
140         }
141
142         WM_event_add_mousemove(C);
143 }
144
145 /* *** datadropper id helper functions *** */
146 /**
147  * \brief get the ID from the screen.
148  *
149  */
150 static void datadropper_id_sample_pt(bContext *C, DataDropper *ddr, int mx, int my, ID **r_id)
151 {
152         /* we could use some clever */
153         bScreen *screen = CTX_wm_screen(C);
154         ScrArea *sa = BKE_screen_find_area_xy(screen, -1, mx, my);
155
156         ScrArea *area_prev = CTX_wm_area(C);
157         ARegion *ar_prev = CTX_wm_region(C);
158
159         ddr->name[0] = '\0';
160
161         if (sa) {
162                 if (sa->spacetype == SPACE_VIEW3D) {
163                         ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my);
164                         if (ar) {
165                                 const int mval[2] = {
166                                     mx - ar->winrct.xmin,
167                                     my - ar->winrct.ymin};
168                                 Base *base;
169
170                                 CTX_wm_area_set(C, sa);
171                                 CTX_wm_region_set(C, ar);
172
173                                 /* grr, always draw else we leave stale text */
174                                 ED_region_tag_redraw(ar);
175
176                                 base = ED_view3d_give_base_under_cursor(C, mval);
177                                 if (base) {
178                                         Object *ob = base->object;
179                                         ID *id = NULL;
180                                         if (ddr->idcode == ID_OB) {
181                                                 id = (ID *)ob;
182                                         }
183                                         else if (ob->data) {
184                                                 if (GS(((ID *)ob->data)->name) == ddr->idcode) {
185                                                         id = (ID *)ob->data;
186                                                 }
187                                                 else {
188                                                         BLI_snprintf(ddr->name, sizeof(ddr->name), "Incompatible, expected a %s",
189                                                                      ddr->idcode_name);
190                                                 }
191                                         }
192
193                                         PointerRNA idptr;
194                                         RNA_id_pointer_create(id, &idptr);
195
196                                         if (id && RNA_property_pointer_poll(&ddr->ptr, ddr->prop, &idptr)) {
197                                                 BLI_snprintf(ddr->name, sizeof(ddr->name), "%s: %s",
198                                                              ddr->idcode_name, id->name + 2);
199                                                 *r_id = id;
200                                         }
201                                 }
202                         }
203                 }
204         }
205
206         CTX_wm_area_set(C, area_prev);
207         CTX_wm_region_set(C, ar_prev);
208 }
209
210 /* sets the ID, returns success */
211 static bool datadropper_id_set(bContext *C, DataDropper *ddr, ID *id)
212 {
213         PointerRNA ptr_value;
214
215         RNA_id_pointer_create(id, &ptr_value);
216
217         RNA_property_pointer_set(&ddr->ptr, ddr->prop, ptr_value);
218
219         RNA_property_update(C, &ddr->ptr, ddr->prop);
220
221         ptr_value = RNA_property_pointer_get(&ddr->ptr, ddr->prop);
222
223         return (ptr_value.id.data == id);
224 }
225
226 /* single point sample & set */
227 static bool datadropper_id_sample(bContext *C, DataDropper *ddr, int mx, int my)
228 {
229         ID *id = NULL;
230
231         datadropper_id_sample_pt(C, ddr, mx, my, &id);
232         return datadropper_id_set(C, ddr, id);
233 }
234
235 static void datadropper_cancel(bContext *C, wmOperator *op)
236 {
237         DataDropper *ddr = op->customdata;
238         datadropper_id_set(C, ddr, ddr->init_id);
239         datadropper_exit(C, op);
240 }
241
242 /* main modal status check */
243 static int datadropper_modal(bContext *C, wmOperator *op, const wmEvent *event)
244 {
245         DataDropper *ddr = (DataDropper *)op->customdata;
246
247         /* handle modal keymap */
248         if (event->type == EVT_MODAL_MAP) {
249                 switch (event->val) {
250                         case EYE_MODAL_CANCEL:
251                                 datadropper_cancel(C, op);
252                                 return OPERATOR_CANCELLED;
253                         case EYE_MODAL_SAMPLE_CONFIRM:
254                         {
255                                 bool success;
256
257                                 success = datadropper_id_sample(C, ddr, event->x, event->y);
258                                 datadropper_exit(C, op);
259
260                                 if (success) {
261                                         return OPERATOR_FINISHED;
262                                 }
263                                 else {
264                                         BKE_report(op->reports, RPT_WARNING, "Failed to set value");
265                                         return OPERATOR_CANCELLED;
266                                 }
267                         }
268                 }
269         }
270         else if (event->type == MOUSEMOVE) {
271                 ID *id = NULL;
272                 datadropper_id_sample_pt(C, ddr, event->x, event->y, &id);
273         }
274
275         return OPERATOR_RUNNING_MODAL;
276 }
277
278 /* Modal Operator init */
279 static int datadropper_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
280 {
281         /* init */
282         if (datadropper_init(C, op)) {
283                 WM_cursor_modal_set(CTX_wm_window(C), BC_EYEDROPPER_CURSOR);
284
285                 /* add temp handler */
286                 WM_event_add_modal_handler(C, op);
287
288                 return OPERATOR_RUNNING_MODAL;
289         }
290         else {
291                 datadropper_exit(C, op);
292                 return OPERATOR_CANCELLED;
293         }
294 }
295
296 /* Repeat operator */
297 static int datadropper_exec(bContext *C, wmOperator *op)
298 {
299         /* init */
300         if (datadropper_init(C, op)) {
301                 /* cleanup */
302                 datadropper_exit(C, op);
303
304                 return OPERATOR_FINISHED;
305         }
306         else {
307                 return OPERATOR_CANCELLED;
308         }
309 }
310
311 static bool datadropper_poll(bContext *C)
312 {
313         PointerRNA ptr;
314         PropertyRNA *prop;
315         int index_dummy;
316         uiBut *but;
317
318         /* data dropper only supports object data */
319         if ((CTX_wm_window(C) != NULL) &&
320             (but = UI_context_active_but_prop_get(C, &ptr, &prop, &index_dummy)) &&
321             (but->type == UI_BTYPE_SEARCH_MENU) &&
322             (but->flag & UI_BUT_VALUE_CLEAR))
323         {
324                 if (prop && RNA_property_type(prop) == PROP_POINTER) {
325                         StructRNA *type = RNA_property_pointer_type(&ptr, prop);
326                         const short idcode = RNA_type_to_ID_code(type);
327                         if ((idcode == ID_OB) || OB_DATA_SUPPORT_ID(idcode)) {
328                                 return 1;
329                         }
330                 }
331         }
332
333         return 0;
334 }
335
336 void UI_OT_eyedropper_id(wmOperatorType *ot)
337 {
338         /* identifiers */
339         ot->name = "Eyedropper Data-Block";
340         ot->idname = "UI_OT_eyedropper_id";
341         ot->description = "Sample a data-block from the 3D View to store in a property";
342
343         /* api callbacks */
344         ot->invoke = datadropper_invoke;
345         ot->modal = datadropper_modal;
346         ot->cancel = datadropper_cancel;
347         ot->exec = datadropper_exec;
348         ot->poll = datadropper_poll;
349
350         /* flags */
351         ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL;
352
353         /* properties */
354 }