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