Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / interface / interface_utils.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
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/editors/interface/interface_utils.c
27  *  \ingroup edinterface
28  */
29
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <assert.h>
35
36 #include "DNA_object_types.h"
37 #include "DNA_screen_types.h"
38
39 #include "BLI_utildefines.h"
40 #include "BLI_math.h"
41 #include "BLI_string.h"
42 #include "BLI_listbase.h"
43
44 #include "BLT_translation.h"
45
46 #include "BKE_report.h"
47
48 #include "MEM_guardedalloc.h"
49
50 #include "RNA_access.h"
51
52 #include "UI_interface.h"
53 #include "UI_resources.h"
54
55 #include "WM_api.h"
56 #include "WM_types.h"
57
58 #include "interface_intern.h"
59
60
61 /*************************** RNA Utilities ******************************/
62
63 uiBut *uiDefAutoButR(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, int index, const char *name, int icon, int x1, int y1, int x2, int y2)
64 {
65         uiBut *but = NULL;
66
67         switch (RNA_property_type(prop)) {
68                 case PROP_BOOLEAN:
69                 {
70                         int arraylen = RNA_property_array_length(ptr, prop);
71
72                         if (arraylen && index == -1)
73                                 return NULL;
74                         
75                         if (icon && name && name[0] == '\0')
76                                 but = uiDefIconButR_prop(block, UI_BTYPE_ICON_TOGGLE, 0, icon, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL);
77                         else if (icon)
78                                 but = uiDefIconTextButR_prop(block, UI_BTYPE_ICON_TOGGLE, 0, icon, name, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL);
79                         else
80                                 but = uiDefButR_prop(block, UI_BTYPE_CHECKBOX, 0, name, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL);
81                         break;
82                 }
83                 case PROP_INT:
84                 case PROP_FLOAT:
85                 {
86                         int arraylen = RNA_property_array_length(ptr, prop);
87
88                         if (arraylen && index == -1) {
89                                 if (ELEM(RNA_property_subtype(prop), PROP_COLOR, PROP_COLOR_GAMMA)) {
90                                         but = uiDefButR_prop(block, UI_BTYPE_COLOR, 0, name, x1, y1, x2, y2, ptr, prop, -1, 0, 0, -1, -1, NULL);
91                                 }
92                                 else {
93                                         return NULL;
94                                 }
95                         }
96                         else if (RNA_property_subtype(prop) == PROP_PERCENTAGE || RNA_property_subtype(prop) == PROP_FACTOR)
97                                 but = uiDefButR_prop(block, UI_BTYPE_NUM_SLIDER, 0, name, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL);
98                         else
99                                 but = uiDefButR_prop(block, UI_BTYPE_NUM, 0, name, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL);
100
101                         if (RNA_property_flag(prop) & PROP_TEXTEDIT_UPDATE) {
102                                 UI_but_flag_enable(but, UI_BUT_TEXTEDIT_UPDATE);
103                         }
104                         break;
105                 }
106                 case PROP_ENUM:
107                         if (icon && name && name[0] == '\0')
108                                 but = uiDefIconButR_prop(block, UI_BTYPE_MENU, 0, icon, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL);
109                         else if (icon)
110                                 but = uiDefIconTextButR_prop(block, UI_BTYPE_MENU, 0, icon, NULL, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL);
111                         else
112                                 but = uiDefButR_prop(block, UI_BTYPE_MENU, 0, name, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL);
113                         break;
114                 case PROP_STRING:
115                         if (icon && name && name[0] == '\0')
116                                 but = uiDefIconButR_prop(block, UI_BTYPE_TEXT, 0, icon, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL);
117                         else if (icon)
118                                 but = uiDefIconTextButR_prop(block, UI_BTYPE_TEXT, 0, icon, name, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL);
119                         else
120                                 but = uiDefButR_prop(block, UI_BTYPE_TEXT, 0, name, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL);
121
122                         if (RNA_property_flag(prop) & PROP_TEXTEDIT_UPDATE) {
123                                 /* TEXTEDIT_UPDATE is usally used for search buttons. For these we also want
124                                  * the 'x' icon to clear search string, so setting VALUE_CLEAR flag, too. */
125                                 UI_but_flag_enable(but, UI_BUT_TEXTEDIT_UPDATE | UI_BUT_VALUE_CLEAR);
126                         }
127                         break;
128                 case PROP_POINTER:
129                 {
130                         if (icon == 0) {
131                                 PointerRNA pptr = RNA_property_pointer_get(ptr, prop);
132                                 icon = RNA_struct_ui_icon(pptr.type ? pptr.type : RNA_property_pointer_type(ptr, prop));
133                         }
134                         if (icon == ICON_DOT)
135                                 icon = 0;
136
137                         but = uiDefIconTextButR_prop(block, UI_BTYPE_SEARCH_MENU, 0, icon, name, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL);
138                         break;
139                 }
140                 case PROP_COLLECTION:
141                 {
142                         char text[256];
143                         BLI_snprintf(text, sizeof(text), IFACE_("%d items"), RNA_property_collection_length(ptr, prop));
144                         but = uiDefBut(block, UI_BTYPE_LABEL, 0, text, x1, y1, x2, y2, NULL, 0, 0, 0, 0, NULL);
145                         UI_but_flag_enable(but, UI_BUT_DISABLED);
146                         break;
147                 }
148                 default:
149                         but = NULL;
150                         break;
151         }
152
153         return but;
154 }
155
156 /**
157  * \a check_prop callback filters functions to avoid drawing certain properties,
158  * in cases where PROP_HIDDEN flag can't be used for a property.
159  */
160 int uiDefAutoButsRNA(
161         uiLayout *layout, PointerRNA *ptr,
162         bool (*check_prop)(PointerRNA *, PropertyRNA *),
163         const char label_align)
164 {
165         uiLayout *split, *col;
166         int flag;
167         const char *name;
168         int tot = 0;
169
170         assert(ELEM(label_align, '\0', 'H', 'V'));
171
172         RNA_STRUCT_BEGIN (ptr, prop)
173         {
174                 flag = RNA_property_flag(prop);
175                 if (flag & PROP_HIDDEN || (check_prop && check_prop(ptr, prop) == 0))
176                         continue;
177
178                 if (label_align != '\0') {
179                         PropertyType type = RNA_property_type(prop);
180                         const bool is_boolean = (type == PROP_BOOLEAN && !RNA_property_array_check(prop));
181
182                         name = RNA_property_ui_name(prop);
183
184                         if (label_align == 'V') {
185                                 col = uiLayoutColumn(layout, true);
186
187                                 if (!is_boolean)
188                                         uiItemL(col, name, ICON_NONE);
189                         }
190                         else {  /* (label_align == 'H') */
191                                 BLI_assert(label_align == 'H');
192                                 split = uiLayoutSplit(layout, 0.5f, false);
193
194                                 col = uiLayoutColumn(split, false);
195                                 uiItemL(col, (is_boolean) ? "" : name, ICON_NONE);
196                                 col = uiLayoutColumn(split, false);
197                         }
198
199                         /* may meed to add more cases here.
200                          * don't override enum flag names */
201
202                         /* name is shown above, empty name for button below */
203                         name = (flag & PROP_ENUM_FLAG || is_boolean) ? NULL : "";
204                 }
205                 else {
206                         col = layout;
207                         name = NULL; /* no smart label alignment, show default name with button */
208                 }
209
210                 uiItemFullR(col, ptr, prop, -1, 0, 0, name, ICON_NONE);
211                 tot++;
212         }
213         RNA_STRUCT_END;
214
215         return tot;
216 }
217
218 /* *** RNA collection search menu *** */
219
220 typedef struct CollItemSearch {
221         struct CollItemSearch *next, *prev;
222         void *data;
223         char *name;
224         int index;
225         int iconid;
226 } CollItemSearch;
227
228 static int sort_search_items_list(const void *a, const void *b)
229 {
230         const CollItemSearch *cis1 = a;
231         const CollItemSearch *cis2 = b;
232
233         if (BLI_strcasecmp(cis1->name, cis2->name) > 0)
234                 return 1;
235         else
236                 return 0;
237 }
238
239 void ui_rna_collection_search_cb(const struct bContext *C, void *arg, const char *str, uiSearchItems *items)
240 {
241         uiRNACollectionSearch *data = arg;
242         char *name;
243         int i = 0, iconid = 0, flag = RNA_property_flag(data->target_prop);
244         ListBase *items_list = MEM_callocN(sizeof(ListBase), "items_list");
245         CollItemSearch *cis;
246         const bool skip_filter = !(data->but_changed && *data->but_changed);
247
248         /* build a temporary list of relevant items first */
249         RNA_PROP_BEGIN (&data->search_ptr, itemptr, data->search_prop)
250         {
251
252                 if (flag & PROP_ID_SELF_CHECK)
253                         if (itemptr.data == data->target_ptr.id.data)
254                                 continue;
255
256                 /* use filter */
257                 if (RNA_property_type(data->target_prop) == PROP_POINTER) {
258                         if (RNA_property_pointer_poll(&data->target_ptr, data->target_prop, &itemptr) == 0)
259                                 continue;
260                 }
261
262                 name = RNA_struct_name_get_alloc(&itemptr, NULL, 0, NULL); /* could use the string length here */
263                 iconid = 0;
264                 if (itemptr.type && RNA_struct_is_ID(itemptr.type)) {
265                         iconid = ui_id_icon_get(C, itemptr.data, false);
266                 }
267
268                 if (name) {
269                         if (skip_filter || BLI_strcasestr(name, str)) {
270                                 cis = MEM_callocN(sizeof(CollItemSearch), "CollectionItemSearch");
271                                 cis->data = itemptr.data;
272                                 cis->name = MEM_dupallocN(name);
273                                 cis->index = i;
274                                 cis->iconid = iconid;
275                                 BLI_addtail(items_list, cis);
276                         }
277                         MEM_freeN(name);
278                 }
279
280                 i++;
281         }
282         RNA_PROP_END;
283
284         BLI_listbase_sort(items_list, sort_search_items_list);
285
286         /* add search items from temporary list */
287         for (cis = items_list->first; cis; cis = cis->next) {
288                 if (UI_search_item_add(items, cis->name, cis->data, cis->iconid) == false) {
289                         break;
290                 }
291         }
292
293         for (cis = items_list->first; cis; cis = cis->next) {
294                 MEM_freeN(cis->name);
295         }
296         BLI_freelistN(items_list);
297         MEM_freeN(items_list);
298 }
299
300
301 /***************************** ID Utilities *******************************/ 
302 int UI_icon_from_id(ID *id)
303 {
304         Object *ob;
305         PointerRNA ptr;
306         short idcode;
307
308         if (id == NULL)
309                 return ICON_NONE;
310         
311         idcode = GS(id->name);
312
313         /* exception for objects */
314         if (idcode == ID_OB) {
315                 ob = (Object *)id;
316
317                 if (ob->type == OB_EMPTY)
318                         return ICON_EMPTY_DATA;
319                 else
320                         return UI_icon_from_id(ob->data);
321         }
322
323         /* otherwise get it through RNA, creating the pointer
324          * will set the right type, also with subclassing */
325         RNA_id_pointer_create(id, &ptr);
326
327         return (ptr.type) ? RNA_struct_ui_icon(ptr.type) : ICON_NONE;
328 }
329
330 /* see: report_type_str */
331 int UI_icon_from_report_type(int type)
332 {
333         if (type & RPT_ERROR_ALL)
334                 return ICON_ERROR;
335         else if (type & RPT_WARNING_ALL)
336                 return ICON_ERROR;
337         else if (type & RPT_INFO_ALL)
338                 return ICON_INFO;
339         else
340                 return ICON_NONE;
341 }
342
343 /********************************** Misc **************************************/
344
345 /**
346  * Returns the best "UI" precision for given floating value, so that e.g. 10.000001 rather gets drawn as '10'...
347  */
348 int UI_calc_float_precision(int prec, double value)
349 {
350         static const double pow10_neg[UI_PRECISION_FLOAT_MAX + 1] = {1e0, 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7};
351         static const double max_pow = 10000000.0;  /* pow(10, UI_PRECISION_FLOAT_MAX) */
352
353         BLI_assert(prec <= UI_PRECISION_FLOAT_MAX);
354         BLI_assert(fabs(pow10_neg[prec] - pow(10, -prec)) < 1e-16);
355
356         /* check on the number of decimal places need to display the number, this is so 0.00001 is not displayed as 0.00,
357          * _but_, this is only for small values si 10.0001 will not get the same treatment.
358          */
359         value = ABS(value);
360         if ((value < pow10_neg[prec]) && (value > (1.0 / max_pow))) {
361                 int value_i = (int)((value * max_pow) + 0.5);
362                 if (value_i != 0) {
363                         const int prec_span = 3; /* show: 0.01001, 5 would allow 0.0100001 for eg. */
364                         int test_prec;
365                         int prec_min = -1;
366                         int dec_flag = 0;
367                         int i = UI_PRECISION_FLOAT_MAX;
368                         while (i && value_i) {
369                                 if (value_i % 10) {
370                                         dec_flag |= 1 << i;
371                                         prec_min = i;
372                                 }
373                                 value_i /= 10;
374                                 i--;
375                         }
376
377                         /* even though its a small value, if the second last digit is not 0, use it */
378                         test_prec = prec_min;
379
380                         dec_flag = (dec_flag >> (prec_min + 1)) & ((1 << prec_span) - 1);
381
382                         while (dec_flag) {
383                                 test_prec++;
384                                 dec_flag = dec_flag >> 1;
385                         }
386
387                         if (test_prec > prec) {
388                                 prec = test_prec;
389                         }
390                 }
391         }
392
393         CLAMP(prec, 0, UI_PRECISION_FLOAT_MAX);
394
395         return prec;
396 }
397
398 bool UI_but_online_manual_id(const uiBut *but, char *r_str, size_t maxlength)
399 {
400         if (but->rnapoin.id.data && but->rnapoin.data && but->rnaprop) {
401                 BLI_snprintf(r_str, maxlength, "%s.%s", RNA_struct_identifier(but->rnapoin.type),
402                              RNA_property_identifier(but->rnaprop));
403                 return true;
404         }
405         else if (but->optype) {
406                 WM_operator_py_idname(r_str, but->optype->idname);
407                 return true;
408         }
409
410         *r_str = '\0';
411         return false;
412 }
413
414 bool UI_but_online_manual_id_from_active(const struct bContext *C, char *r_str, size_t maxlength)
415 {
416         uiBut *but = UI_context_active_but_get(C);
417
418         if (but) {
419                 return UI_but_online_manual_id(but, r_str, maxlength);
420         }
421
422         *r_str = '\0';
423         return false;
424 }
425
426
427 /* -------------------------------------------------------------------- */
428 /* Modal Button Store API */
429
430 /** \name Button Store
431  *
432  * Store for modal operators & handlers to register button pointers
433  * which are maintained while drawing or NULL when removed.
434  *
435  * This is needed since button pointers are continuously freed and re-allocated.
436  *
437  * \{ */
438
439 struct uiButStore {
440         struct uiButStore *next, *prev;
441         uiBlock *block;
442         ListBase items;
443 };
444
445 struct uiButStoreElem {
446         struct uiButStoreElem *next, *prev;
447         uiBut **but_p;
448 };
449
450 /**
451  * Create a new button store, the caller must manage and run #UI_butstore_free
452  */
453 uiButStore *UI_butstore_create(uiBlock *block)
454 {
455         uiButStore *bs_handle = MEM_callocN(sizeof(uiButStore), __func__);
456
457         bs_handle->block = block;
458         BLI_addtail(&block->butstore, bs_handle);
459
460         return bs_handle;
461 }
462
463 void UI_butstore_free(uiBlock *block, uiButStore *bs_handle)
464 {
465         BLI_freelistN(&bs_handle->items);
466         BLI_remlink(&block->butstore, bs_handle);
467
468         MEM_freeN(bs_handle);
469 }
470
471 bool UI_butstore_is_valid(uiButStore *bs)
472 {
473         return (bs->block != NULL);
474 }
475
476 bool UI_butstore_is_registered(uiBlock *block, uiBut *but)
477 {
478         uiButStore *bs_handle;
479
480         for (bs_handle = block->butstore.first; bs_handle; bs_handle = bs_handle->next) {
481                 uiButStoreElem *bs_elem;
482
483                 for (bs_elem = bs_handle->items.first; bs_elem; bs_elem = bs_elem->next) {
484                         if (*bs_elem->but_p == but) {
485                                 return true;
486                         }
487                 }
488         }
489
490         return false;
491 }
492
493 void UI_butstore_register(uiButStore *bs_handle, uiBut **but_p)
494 {
495         uiButStoreElem *bs_elem = MEM_callocN(sizeof(uiButStoreElem), __func__);
496         BLI_assert(*but_p);
497         bs_elem->but_p = but_p;
498
499         BLI_addtail(&bs_handle->items, bs_elem);
500
501 }
502
503 void UI_butstore_unregister(uiButStore *bs_handle, uiBut **but_p)
504 {
505         uiButStoreElem *bs_elem, *bs_elem_next;
506
507         for (bs_elem = bs_handle->items.first; bs_elem; bs_elem = bs_elem_next) {
508                 bs_elem_next = bs_elem->next;
509                 if (bs_elem->but_p == but_p) {
510                         BLI_remlink(&bs_handle->items, bs_elem);
511                         MEM_freeN(bs_elem);
512                 }
513         }
514
515         BLI_assert(0);
516 }
517
518 /**
519  * Update the pointer for a registered button.
520  */
521 bool UI_butstore_register_update(uiBlock *block, uiBut *but_dst, const uiBut *but_src)
522 {
523         uiButStore *bs_handle;
524         bool found = false;
525
526         for (bs_handle = block->butstore.first; bs_handle; bs_handle = bs_handle->next) {
527                 uiButStoreElem *bs_elem;
528                 for (bs_elem = bs_handle->items.first; bs_elem; bs_elem = bs_elem->next) {
529                         if (*bs_elem->but_p == but_src) {
530                                 *bs_elem->but_p = but_dst;
531                                 found = true;
532                         }
533                 }
534         }
535
536         return found;
537 }
538
539 /**
540  * NULL all pointers, don't free since the owner needs to be able to inspect.
541  */
542 void UI_butstore_clear(uiBlock *block)
543 {
544         uiButStore *bs_handle;
545
546         for (bs_handle = block->butstore.first; bs_handle; bs_handle = bs_handle->next) {
547                 uiButStoreElem *bs_elem;
548
549                 bs_handle->block = NULL;
550
551                 for (bs_elem = bs_handle->items.first; bs_elem; bs_elem = bs_elem->next) {
552                         *bs_elem->but_p = NULL;
553                 }
554         }
555 }
556
557 /**
558  * Map freed buttons from the old block and update pointers.
559  */
560 void UI_butstore_update(uiBlock *block)
561 {
562         uiButStore *bs_handle;
563
564         /* move this list to the new block */
565         if (block->oldblock) {
566                 if (block->oldblock->butstore.first) {
567                         block->butstore = block->oldblock->butstore;
568                         BLI_listbase_clear(&block->oldblock->butstore);
569                 }
570         }
571
572         if (LIKELY(block->butstore.first == NULL))
573                 return;
574
575         /* warning, loop-in-loop, in practice we only store <10 buttons at a time,
576          * so this isn't going to be a problem, if that changes old-new mapping can be cached first */
577         for (bs_handle = block->butstore.first; bs_handle; bs_handle = bs_handle->next) {
578
579                 BLI_assert((bs_handle->block == NULL) ||
580                            (bs_handle->block == block) ||
581                            (block->oldblock && block->oldblock == bs_handle->block));
582
583                 if (bs_handle->block == block->oldblock) {
584                         uiButStoreElem *bs_elem;
585
586                         bs_handle->block = block;
587
588                         for (bs_elem = bs_handle->items.first; bs_elem; bs_elem = bs_elem->next) {
589                                 if (*bs_elem->but_p) {
590                                         uiBut *but_new = ui_but_find_new(block, *bs_elem->but_p);
591
592                                         /* can be NULL if the buttons removed,
593                                          * note: we could allow passing in a callback when buttons are removed
594                                          * so the caller can cleanup */
595                                         *bs_elem->but_p = but_new;
596                                 }
597                         }
598                 }
599         }
600 }
601
602 /** \} */