While it might be handy to have type-less functionality which is similar to how C++ math is implemented it can not be easily achieved with just preprocessor in a way which does not have side-effects on wrong usage. There macros where often used on a non-trivial expression, and there was at least one usage where it was causing an actual side effect/bug on Windows (see change around square_f(sh[index++]) in studiolight.c). For such cases it is handy to have a function which is guaranteed to have zero side-effects. The motivation behind actually removing the macros is that there is already a way to do similar calculation. Also, not having such macros is a way to guarantee that its usage is not changed in a way which have side-effects and that it's not used as an inspiration for cases where it should not be used. Differential Revision: https://developer.blender.org/D7051
		
			
				
	
	
		
			786 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			786 lines
		
	
	
		
			22 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * This program is free software; you can redistribute it and/or
 | |
|  * modify it under the terms of the GNU General Public License
 | |
|  * as published by the Free Software Foundation; either version 2
 | |
|  * of the License, or (at your option) any later version.
 | |
|  *
 | |
|  * This program is distributed in the hope that it will be useful,
 | |
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|  * GNU General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License
 | |
|  * along with this program; if not, write to the Free Software Foundation,
 | |
|  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 | |
|  *
 | |
|  * The Original Code is Copyright (C) 2009 Blender Foundation.
 | |
|  * All rights reserved.
 | |
|  */
 | |
| 
 | |
| /** \file
 | |
|  * \ingroup edinterface
 | |
|  */
 | |
| 
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <assert.h>
 | |
| 
 | |
| #include "DNA_object_types.h"
 | |
| #include "DNA_screen_types.h"
 | |
| 
 | |
| #include "BLI_utildefines.h"
 | |
| #include "BLI_math.h"
 | |
| #include "BLI_string.h"
 | |
| #include "BLI_listbase.h"
 | |
| 
 | |
| #include "BLT_translation.h"
 | |
| 
 | |
| #include "BKE_report.h"
 | |
| 
 | |
| #include "MEM_guardedalloc.h"
 | |
| 
 | |
| #include "RNA_access.h"
 | |
| 
 | |
| #include "UI_interface.h"
 | |
| #include "UI_resources.h"
 | |
| 
 | |
| #include "WM_api.h"
 | |
| #include "WM_types.h"
 | |
| 
 | |
| #include "interface_intern.h"
 | |
| 
 | |
| /*************************** RNA Utilities ******************************/
 | |
| 
 | |
| uiBut *uiDefAutoButR(uiBlock *block,
 | |
|                      PointerRNA *ptr,
 | |
|                      PropertyRNA *prop,
 | |
|                      int index,
 | |
|                      const char *name,
 | |
|                      int icon,
 | |
|                      int x1,
 | |
|                      int y1,
 | |
|                      int x2,
 | |
|                      int y2)
 | |
| {
 | |
|   uiBut *but = NULL;
 | |
| 
 | |
|   switch (RNA_property_type(prop)) {
 | |
|     case PROP_BOOLEAN: {
 | |
|       if (RNA_property_array_check(prop) && index == -1) {
 | |
|         return NULL;
 | |
|       }
 | |
| 
 | |
|       if (icon && name && name[0] == '\0') {
 | |
|         but = uiDefIconButR_prop(block,
 | |
|                                  UI_BTYPE_ICON_TOGGLE,
 | |
|                                  0,
 | |
|                                  icon,
 | |
|                                  x1,
 | |
|                                  y1,
 | |
|                                  x2,
 | |
|                                  y2,
 | |
|                                  ptr,
 | |
|                                  prop,
 | |
|                                  index,
 | |
|                                  0,
 | |
|                                  0,
 | |
|                                  -1,
 | |
|                                  -1,
 | |
|                                  NULL);
 | |
|       }
 | |
|       else if (icon) {
 | |
|         but = uiDefIconTextButR_prop(block,
 | |
|                                      UI_BTYPE_ICON_TOGGLE,
 | |
|                                      0,
 | |
|                                      icon,
 | |
|                                      name,
 | |
|                                      x1,
 | |
|                                      y1,
 | |
|                                      x2,
 | |
|                                      y2,
 | |
|                                      ptr,
 | |
|                                      prop,
 | |
|                                      index,
 | |
|                                      0,
 | |
|                                      0,
 | |
|                                      -1,
 | |
|                                      -1,
 | |
|                                      NULL);
 | |
|       }
 | |
|       else {
 | |
|         but = uiDefButR_prop(block,
 | |
|                              UI_BTYPE_CHECKBOX,
 | |
|                              0,
 | |
|                              name,
 | |
|                              x1,
 | |
|                              y1,
 | |
|                              x2,
 | |
|                              y2,
 | |
|                              ptr,
 | |
|                              prop,
 | |
|                              index,
 | |
|                              0,
 | |
|                              0,
 | |
|                              -1,
 | |
|                              -1,
 | |
|                              NULL);
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
|     case PROP_INT:
 | |
|     case PROP_FLOAT: {
 | |
|       if (RNA_property_array_check(prop) && index == -1) {
 | |
|         if (ELEM(RNA_property_subtype(prop), PROP_COLOR, PROP_COLOR_GAMMA)) {
 | |
|           but = uiDefButR_prop(
 | |
|               block, UI_BTYPE_COLOR, 0, name, x1, y1, x2, y2, ptr, prop, -1, 0, 0, -1, -1, NULL);
 | |
|         }
 | |
|         else {
 | |
|           return NULL;
 | |
|         }
 | |
|       }
 | |
|       else if (RNA_property_subtype(prop) == PROP_PERCENTAGE ||
 | |
|                RNA_property_subtype(prop) == PROP_FACTOR) {
 | |
|         but = uiDefButR_prop(block,
 | |
|                              UI_BTYPE_NUM_SLIDER,
 | |
|                              0,
 | |
|                              name,
 | |
|                              x1,
 | |
|                              y1,
 | |
|                              x2,
 | |
|                              y2,
 | |
|                              ptr,
 | |
|                              prop,
 | |
|                              index,
 | |
|                              0,
 | |
|                              0,
 | |
|                              -1,
 | |
|                              -1,
 | |
|                              NULL);
 | |
|       }
 | |
|       else {
 | |
|         but = uiDefButR_prop(
 | |
|             block, UI_BTYPE_NUM, 0, name, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL);
 | |
|       }
 | |
| 
 | |
|       if (RNA_property_flag(prop) & PROP_TEXTEDIT_UPDATE) {
 | |
|         UI_but_flag_enable(but, UI_BUT_TEXTEDIT_UPDATE);
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
|     case PROP_ENUM:
 | |
|       if (icon && name && name[0] == '\0') {
 | |
|         but = uiDefIconButR_prop(
 | |
|             block, UI_BTYPE_MENU, 0, icon, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL);
 | |
|       }
 | |
|       else if (icon) {
 | |
|         but = uiDefIconTextButR_prop(block,
 | |
|                                      UI_BTYPE_MENU,
 | |
|                                      0,
 | |
|                                      icon,
 | |
|                                      NULL,
 | |
|                                      x1,
 | |
|                                      y1,
 | |
|                                      x2,
 | |
|                                      y2,
 | |
|                                      ptr,
 | |
|                                      prop,
 | |
|                                      index,
 | |
|                                      0,
 | |
|                                      0,
 | |
|                                      -1,
 | |
|                                      -1,
 | |
|                                      NULL);
 | |
|       }
 | |
|       else {
 | |
|         but = uiDefButR_prop(
 | |
|             block, UI_BTYPE_MENU, 0, name, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL);
 | |
|       }
 | |
|       break;
 | |
|     case PROP_STRING:
 | |
|       if (icon && name && name[0] == '\0') {
 | |
|         but = uiDefIconButR_prop(
 | |
|             block, UI_BTYPE_TEXT, 0, icon, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL);
 | |
|       }
 | |
|       else if (icon) {
 | |
|         but = uiDefIconTextButR_prop(block,
 | |
|                                      UI_BTYPE_TEXT,
 | |
|                                      0,
 | |
|                                      icon,
 | |
|                                      name,
 | |
|                                      x1,
 | |
|                                      y1,
 | |
|                                      x2,
 | |
|                                      y2,
 | |
|                                      ptr,
 | |
|                                      prop,
 | |
|                                      index,
 | |
|                                      0,
 | |
|                                      0,
 | |
|                                      -1,
 | |
|                                      -1,
 | |
|                                      NULL);
 | |
|       }
 | |
|       else {
 | |
|         but = uiDefButR_prop(
 | |
|             block, UI_BTYPE_TEXT, 0, name, x1, y1, x2, y2, ptr, prop, index, 0, 0, -1, -1, NULL);
 | |
|       }
 | |
| 
 | |
|       if (RNA_property_flag(prop) & PROP_TEXTEDIT_UPDATE) {
 | |
|         /* TEXTEDIT_UPDATE is usually used for search buttons. For these we also want
 | |
|          * the 'x' icon to clear search string, so setting VALUE_CLEAR flag, too. */
 | |
|         UI_but_flag_enable(but, UI_BUT_TEXTEDIT_UPDATE | UI_BUT_VALUE_CLEAR);
 | |
|       }
 | |
|       break;
 | |
|     case PROP_POINTER: {
 | |
|       if (icon == 0) {
 | |
|         PointerRNA pptr = RNA_property_pointer_get(ptr, prop);
 | |
|         icon = RNA_struct_ui_icon(pptr.type ? pptr.type : RNA_property_pointer_type(ptr, prop));
 | |
|       }
 | |
|       if (icon == ICON_DOT) {
 | |
|         icon = 0;
 | |
|       }
 | |
| 
 | |
|       but = uiDefIconTextButR_prop(block,
 | |
|                                    UI_BTYPE_SEARCH_MENU,
 | |
|                                    0,
 | |
|                                    icon,
 | |
|                                    name,
 | |
|                                    x1,
 | |
|                                    y1,
 | |
|                                    x2,
 | |
|                                    y2,
 | |
|                                    ptr,
 | |
|                                    prop,
 | |
|                                    index,
 | |
|                                    0,
 | |
|                                    0,
 | |
|                                    -1,
 | |
|                                    -1,
 | |
|                                    NULL);
 | |
|       break;
 | |
|     }
 | |
|     case PROP_COLLECTION: {
 | |
|       char text[256];
 | |
|       BLI_snprintf(
 | |
|           text, sizeof(text), IFACE_("%d items"), RNA_property_collection_length(ptr, prop));
 | |
|       but = uiDefBut(block, UI_BTYPE_LABEL, 0, text, x1, y1, x2, y2, NULL, 0, 0, 0, 0, NULL);
 | |
|       UI_but_flag_enable(but, UI_BUT_DISABLED);
 | |
|       break;
 | |
|     }
 | |
|     default:
 | |
|       but = NULL;
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   return but;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * \a check_prop callback filters functions to avoid drawing certain properties,
 | |
|  * in cases where PROP_HIDDEN flag can't be used for a property.
 | |
|  *
 | |
|  * \param prop_activate_init: Property to activate on initial popup (#UI_BUT_ACTIVATE_ON_INIT).
 | |
|  */
 | |
| eAutoPropButsReturn uiDefAutoButsRNA(uiLayout *layout,
 | |
|                                      PointerRNA *ptr,
 | |
|                                      bool (*check_prop)(PointerRNA *ptr,
 | |
|                                                         PropertyRNA *prop,
 | |
|                                                         void *user_data),
 | |
|                                      void *user_data,
 | |
|                                      PropertyRNA *prop_activate_init,
 | |
|                                      const eButLabelAlign label_align,
 | |
|                                      const bool compact)
 | |
| {
 | |
|   eAutoPropButsReturn return_info = UI_PROP_BUTS_NONE_ADDED;
 | |
|   uiLayout *split, *col;
 | |
|   const char *name;
 | |
| 
 | |
|   RNA_STRUCT_BEGIN (ptr, prop) {
 | |
|     const int flag = RNA_property_flag(prop);
 | |
| 
 | |
|     if (flag & PROP_HIDDEN) {
 | |
|       continue;
 | |
|     }
 | |
|     if (check_prop && check_prop(ptr, prop, user_data) == 0) {
 | |
|       return_info |= UI_PROP_BUTS_ANY_FAILED_CHECK;
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     const PropertyType type = RNA_property_type(prop);
 | |
|     switch (label_align) {
 | |
|       case UI_BUT_LABEL_ALIGN_COLUMN:
 | |
|       case UI_BUT_LABEL_ALIGN_SPLIT_COLUMN: {
 | |
|         const bool is_boolean = (type == PROP_BOOLEAN && !RNA_property_array_check(prop));
 | |
| 
 | |
|         name = RNA_property_ui_name(prop);
 | |
| 
 | |
|         if (label_align == UI_BUT_LABEL_ALIGN_COLUMN) {
 | |
|           col = uiLayoutColumn(layout, true);
 | |
| 
 | |
|           if (!is_boolean) {
 | |
|             uiItemL(col, name, ICON_NONE);
 | |
|           }
 | |
|         }
 | |
|         else {
 | |
|           BLI_assert(label_align == UI_BUT_LABEL_ALIGN_SPLIT_COLUMN);
 | |
|           split = uiLayoutSplit(layout, 0.5f, false);
 | |
| 
 | |
|           col = uiLayoutColumn(split, false);
 | |
|           uiItemL(col, (is_boolean) ? "" : name, ICON_NONE);
 | |
|           col = uiLayoutColumn(split, false);
 | |
|         }
 | |
| 
 | |
|         /* May need to add more cases here.
 | |
|          * don't override enum flag names */
 | |
| 
 | |
|         /* name is shown above, empty name for button below */
 | |
|         name = (flag & PROP_ENUM_FLAG || is_boolean) ? NULL : "";
 | |
| 
 | |
|         break;
 | |
|       }
 | |
|       case UI_BUT_LABEL_ALIGN_NONE:
 | |
|       default:
 | |
|         col = layout;
 | |
|         name = NULL; /* no smart label alignment, show default name with button */
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     /* Only buttons that can be edited as text. */
 | |
|     const bool use_activate_init = ((prop == prop_activate_init) &&
 | |
|                                     (ELEM(type, PROP_STRING, PROP_INT, PROP_FLOAT)));
 | |
| 
 | |
|     if (use_activate_init) {
 | |
|       uiLayoutSetActivateInit(col, true);
 | |
|     }
 | |
| 
 | |
|     uiItemFullR(col, ptr, prop, -1, 0, compact ? UI_ITEM_R_COMPACT : 0, name, ICON_NONE);
 | |
|     return_info &= ~UI_PROP_BUTS_NONE_ADDED;
 | |
| 
 | |
|     if (use_activate_init) {
 | |
|       uiLayoutSetActivateInit(col, false);
 | |
|     }
 | |
|   }
 | |
|   RNA_STRUCT_END;
 | |
| 
 | |
|   return return_info;
 | |
| }
 | |
| 
 | |
| /* *** RNA collection search menu *** */
 | |
| 
 | |
| typedef struct CollItemSearch {
 | |
|   struct CollItemSearch *next, *prev;
 | |
|   void *data;
 | |
|   char *name;
 | |
|   int index;
 | |
|   int iconid;
 | |
| } CollItemSearch;
 | |
| 
 | |
| static int sort_search_items_list(const void *a, const void *b)
 | |
| {
 | |
|   const CollItemSearch *cis1 = a;
 | |
|   const CollItemSearch *cis2 = b;
 | |
| 
 | |
|   if (BLI_strcasecmp(cis1->name, cis2->name) > 0) {
 | |
|     return 1;
 | |
|   }
 | |
|   else {
 | |
|     return 0;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ui_rna_collection_search_cb(const struct bContext *C,
 | |
|                                  void *arg,
 | |
|                                  const char *str,
 | |
|                                  uiSearchItems *items)
 | |
| {
 | |
|   uiRNACollectionSearch *data = arg;
 | |
|   char *name;
 | |
|   int i = 0, iconid = 0, flag = RNA_property_flag(data->target_prop);
 | |
|   ListBase *items_list = MEM_callocN(sizeof(ListBase), "items_list");
 | |
|   CollItemSearch *cis;
 | |
|   const bool skip_filter = (data->but_changed && !(*data->but_changed));
 | |
| 
 | |
|   /* build a temporary list of relevant items first */
 | |
|   RNA_PROP_BEGIN (&data->search_ptr, itemptr, data->search_prop) {
 | |
| 
 | |
|     if (flag & PROP_ID_SELF_CHECK) {
 | |
|       if (itemptr.data == data->target_ptr.owner_id) {
 | |
|         continue;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     /* use filter */
 | |
|     if (RNA_property_type(data->target_prop) == PROP_POINTER) {
 | |
|       if (RNA_property_pointer_poll(&data->target_ptr, data->target_prop, &itemptr) == 0) {
 | |
|         continue;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     /* Could use the string length here. */
 | |
|     name = RNA_struct_name_get_alloc(&itemptr, NULL, 0, NULL);
 | |
| 
 | |
|     iconid = 0;
 | |
|     if (itemptr.type && RNA_struct_is_ID(itemptr.type)) {
 | |
|       iconid = ui_id_icon_get(C, itemptr.data, false);
 | |
|     }
 | |
| 
 | |
|     if (name) {
 | |
|       if (skip_filter || BLI_strcasestr(name, str)) {
 | |
|         cis = MEM_callocN(sizeof(CollItemSearch), "CollectionItemSearch");
 | |
|         cis->data = itemptr.data;
 | |
|         cis->name = MEM_dupallocN(name);
 | |
|         cis->index = i;
 | |
|         cis->iconid = iconid;
 | |
|         BLI_addtail(items_list, cis);
 | |
|       }
 | |
|       MEM_freeN(name);
 | |
|     }
 | |
| 
 | |
|     i++;
 | |
|   }
 | |
|   RNA_PROP_END;
 | |
| 
 | |
|   BLI_listbase_sort(items_list, sort_search_items_list);
 | |
| 
 | |
|   /* add search items from temporary list */
 | |
|   for (cis = items_list->first; cis; cis = cis->next) {
 | |
|     if (UI_search_item_add(items, cis->name, cis->data, cis->iconid) == false) {
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   for (cis = items_list->first; cis; cis = cis->next) {
 | |
|     MEM_freeN(cis->name);
 | |
|   }
 | |
|   BLI_freelistN(items_list);
 | |
|   MEM_freeN(items_list);
 | |
| }
 | |
| 
 | |
| /***************************** ID Utilities *******************************/
 | |
| int UI_icon_from_id(ID *id)
 | |
| {
 | |
|   Object *ob;
 | |
|   PointerRNA ptr;
 | |
|   short idcode;
 | |
| 
 | |
|   if (id == NULL) {
 | |
|     return ICON_NONE;
 | |
|   }
 | |
| 
 | |
|   idcode = GS(id->name);
 | |
| 
 | |
|   /* exception for objects */
 | |
|   if (idcode == ID_OB) {
 | |
|     ob = (Object *)id;
 | |
| 
 | |
|     if (ob->type == OB_EMPTY) {
 | |
|       return ICON_EMPTY_DATA;
 | |
|     }
 | |
|     else {
 | |
|       return UI_icon_from_id(ob->data);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /* otherwise get it through RNA, creating the pointer
 | |
|    * will set the right type, also with subclassing */
 | |
|   RNA_id_pointer_create(id, &ptr);
 | |
| 
 | |
|   return (ptr.type) ? RNA_struct_ui_icon(ptr.type) : ICON_NONE;
 | |
| }
 | |
| 
 | |
| /* see: report_type_str */
 | |
| int UI_icon_from_report_type(int type)
 | |
| {
 | |
|   if (type & RPT_ERROR_ALL) {
 | |
|     return ICON_ERROR;
 | |
|   }
 | |
|   else if (type & RPT_WARNING_ALL) {
 | |
|     return ICON_ERROR;
 | |
|   }
 | |
|   else if (type & RPT_INFO_ALL) {
 | |
|     return ICON_INFO;
 | |
|   }
 | |
|   else {
 | |
|     return ICON_NONE;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /********************************** Misc **************************************/
 | |
| 
 | |
| /**
 | |
|  * Returns the best "UI" precision for given floating value,
 | |
|  * so that e.g. 10.000001 rather gets drawn as '10'...
 | |
|  */
 | |
| int UI_calc_float_precision(int prec, double value)
 | |
| {
 | |
|   static const double pow10_neg[UI_PRECISION_FLOAT_MAX + 1] = {
 | |
|       1e0, 1e-1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6};
 | |
|   static const double max_pow = 10000000.0; /* pow(10, UI_PRECISION_FLOAT_MAX) */
 | |
| 
 | |
|   BLI_assert(prec <= UI_PRECISION_FLOAT_MAX);
 | |
|   BLI_assert(fabs(pow10_neg[prec] - pow(10, -prec)) < 1e-16);
 | |
| 
 | |
|   /* Check on the number of decimal places need to display the number,
 | |
|    * this is so 0.00001 is not displayed as 0.00,
 | |
|    * _but_, this is only for small values si 10.0001 will not get the same treatment.
 | |
|    */
 | |
|   value = fabs(value);
 | |
|   if ((value < pow10_neg[prec]) && (value > (1.0 / max_pow))) {
 | |
|     int value_i = (int)((value * max_pow) + 0.5);
 | |
|     if (value_i != 0) {
 | |
|       const int prec_span = 3; /* show: 0.01001, 5 would allow 0.0100001 for eg. */
 | |
|       int test_prec;
 | |
|       int prec_min = -1;
 | |
|       int dec_flag = 0;
 | |
|       int i = UI_PRECISION_FLOAT_MAX;
 | |
|       while (i && value_i) {
 | |
|         if (value_i % 10) {
 | |
|           dec_flag |= 1 << i;
 | |
|           prec_min = i;
 | |
|         }
 | |
|         value_i /= 10;
 | |
|         i--;
 | |
|       }
 | |
| 
 | |
|       /* even though its a small value, if the second last digit is not 0, use it */
 | |
|       test_prec = prec_min;
 | |
| 
 | |
|       dec_flag = (dec_flag >> (prec_min + 1)) & ((1 << prec_span) - 1);
 | |
| 
 | |
|       while (dec_flag) {
 | |
|         test_prec++;
 | |
|         dec_flag = dec_flag >> 1;
 | |
|       }
 | |
| 
 | |
|       if (test_prec > prec) {
 | |
|         prec = test_prec;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   CLAMP(prec, 0, UI_PRECISION_FLOAT_MAX);
 | |
| 
 | |
|   return prec;
 | |
| }
 | |
| 
 | |
| bool UI_but_online_manual_id(const uiBut *but, char *r_str, size_t maxlength)
 | |
| {
 | |
|   if (but->rnapoin.owner_id && but->rnapoin.data && but->rnaprop) {
 | |
|     BLI_snprintf(r_str,
 | |
|                  maxlength,
 | |
|                  "%s.%s",
 | |
|                  RNA_struct_identifier(but->rnapoin.type),
 | |
|                  RNA_property_identifier(but->rnaprop));
 | |
|     return true;
 | |
|   }
 | |
|   else if (but->optype) {
 | |
|     WM_operator_py_idname(r_str, but->optype->idname);
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   *r_str = '\0';
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool UI_but_online_manual_id_from_active(const struct bContext *C, char *r_str, size_t maxlength)
 | |
| {
 | |
|   uiBut *but = UI_context_active_but_get(C);
 | |
| 
 | |
|   if (but) {
 | |
|     return UI_but_online_manual_id(but, r_str, maxlength);
 | |
|   }
 | |
| 
 | |
|   *r_str = '\0';
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| /* -------------------------------------------------------------------- */
 | |
| /* Modal Button Store API */
 | |
| 
 | |
| /** \name Button Store
 | |
|  *
 | |
|  * Store for modal operators & handlers to register button pointers
 | |
|  * which are maintained while drawing or NULL when removed.
 | |
|  *
 | |
|  * This is needed since button pointers are continuously freed and re-allocated.
 | |
|  *
 | |
|  * \{ */
 | |
| 
 | |
| struct uiButStore {
 | |
|   struct uiButStore *next, *prev;
 | |
|   uiBlock *block;
 | |
|   ListBase items;
 | |
| };
 | |
| 
 | |
| struct uiButStoreElem {
 | |
|   struct uiButStoreElem *next, *prev;
 | |
|   uiBut **but_p;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Create a new button store, the caller must manage and run #UI_butstore_free
 | |
|  */
 | |
| uiButStore *UI_butstore_create(uiBlock *block)
 | |
| {
 | |
|   uiButStore *bs_handle = MEM_callocN(sizeof(uiButStore), __func__);
 | |
| 
 | |
|   bs_handle->block = block;
 | |
|   BLI_addtail(&block->butstore, bs_handle);
 | |
| 
 | |
|   return bs_handle;
 | |
| }
 | |
| 
 | |
| void UI_butstore_free(uiBlock *block, uiButStore *bs_handle)
 | |
| {
 | |
|   /* Workaround for button store being moved into new block,
 | |
|    * which then can't use the previous buttons state
 | |
|    * ('ui_but_update_from_old_block' fails to find a match),
 | |
|    * keeping the active button in the old block holding a reference
 | |
|    * to the button-state in the new block: see T49034.
 | |
|    *
 | |
|    * Ideally we would manage moving the 'uiButStore', keeping a correct state.
 | |
|    * All things considered this is the most straightforward fix - Campbell.
 | |
|    */
 | |
|   if (block != bs_handle->block && bs_handle->block != NULL) {
 | |
|     block = bs_handle->block;
 | |
|   }
 | |
| 
 | |
|   BLI_freelistN(&bs_handle->items);
 | |
|   BLI_remlink(&block->butstore, bs_handle);
 | |
| 
 | |
|   MEM_freeN(bs_handle);
 | |
| }
 | |
| 
 | |
| bool UI_butstore_is_valid(uiButStore *bs)
 | |
| {
 | |
|   return (bs->block != NULL);
 | |
| }
 | |
| 
 | |
| bool UI_butstore_is_registered(uiBlock *block, uiBut *but)
 | |
| {
 | |
|   uiButStore *bs_handle;
 | |
| 
 | |
|   for (bs_handle = block->butstore.first; bs_handle; bs_handle = bs_handle->next) {
 | |
|     uiButStoreElem *bs_elem;
 | |
| 
 | |
|     for (bs_elem = bs_handle->items.first; bs_elem; bs_elem = bs_elem->next) {
 | |
|       if (*bs_elem->but_p == but) {
 | |
|         return true;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| void UI_butstore_register(uiButStore *bs_handle, uiBut **but_p)
 | |
| {
 | |
|   uiButStoreElem *bs_elem = MEM_callocN(sizeof(uiButStoreElem), __func__);
 | |
|   BLI_assert(*but_p);
 | |
|   bs_elem->but_p = but_p;
 | |
| 
 | |
|   BLI_addtail(&bs_handle->items, bs_elem);
 | |
| }
 | |
| 
 | |
| void UI_butstore_unregister(uiButStore *bs_handle, uiBut **but_p)
 | |
| {
 | |
|   uiButStoreElem *bs_elem, *bs_elem_next;
 | |
| 
 | |
|   for (bs_elem = bs_handle->items.first; bs_elem; bs_elem = bs_elem_next) {
 | |
|     bs_elem_next = bs_elem->next;
 | |
|     if (bs_elem->but_p == but_p) {
 | |
|       BLI_remlink(&bs_handle->items, bs_elem);
 | |
|       MEM_freeN(bs_elem);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   BLI_assert(0);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Update the pointer for a registered button.
 | |
|  */
 | |
| bool UI_butstore_register_update(uiBlock *block, uiBut *but_dst, const uiBut *but_src)
 | |
| {
 | |
|   uiButStore *bs_handle;
 | |
|   bool found = false;
 | |
| 
 | |
|   for (bs_handle = block->butstore.first; bs_handle; bs_handle = bs_handle->next) {
 | |
|     uiButStoreElem *bs_elem;
 | |
|     for (bs_elem = bs_handle->items.first; bs_elem; bs_elem = bs_elem->next) {
 | |
|       if (*bs_elem->but_p == but_src) {
 | |
|         *bs_elem->but_p = but_dst;
 | |
|         found = true;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return found;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * NULL all pointers, don't free since the owner needs to be able to inspect.
 | |
|  */
 | |
| void UI_butstore_clear(uiBlock *block)
 | |
| {
 | |
|   uiButStore *bs_handle;
 | |
| 
 | |
|   for (bs_handle = block->butstore.first; bs_handle; bs_handle = bs_handle->next) {
 | |
|     uiButStoreElem *bs_elem;
 | |
| 
 | |
|     bs_handle->block = NULL;
 | |
| 
 | |
|     for (bs_elem = bs_handle->items.first; bs_elem; bs_elem = bs_elem->next) {
 | |
|       *bs_elem->but_p = NULL;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Map freed buttons from the old block and update pointers.
 | |
|  */
 | |
| void UI_butstore_update(uiBlock *block)
 | |
| {
 | |
|   uiButStore *bs_handle;
 | |
| 
 | |
|   /* move this list to the new block */
 | |
|   if (block->oldblock) {
 | |
|     if (block->oldblock->butstore.first) {
 | |
|       block->butstore = block->oldblock->butstore;
 | |
|       BLI_listbase_clear(&block->oldblock->butstore);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (LIKELY(block->butstore.first == NULL)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   /* warning, loop-in-loop, in practice we only store <10 buttons at a time,
 | |
|    * so this isn't going to be a problem, if that changes old-new mapping can be cached first */
 | |
|   for (bs_handle = block->butstore.first; bs_handle; bs_handle = bs_handle->next) {
 | |
| 
 | |
|     BLI_assert((bs_handle->block == NULL) || (bs_handle->block == block) ||
 | |
|                (block->oldblock && block->oldblock == bs_handle->block));
 | |
| 
 | |
|     if (bs_handle->block == block->oldblock) {
 | |
|       uiButStoreElem *bs_elem;
 | |
| 
 | |
|       bs_handle->block = block;
 | |
| 
 | |
|       for (bs_elem = bs_handle->items.first; bs_elem; bs_elem = bs_elem->next) {
 | |
|         if (*bs_elem->but_p) {
 | |
|           uiBut *but_new = ui_but_find_new(block, *bs_elem->but_p);
 | |
| 
 | |
|           /* can be NULL if the buttons removed,
 | |
|            * note: we could allow passing in a callback when buttons are removed
 | |
|            * so the caller can cleanup */
 | |
|           *bs_elem->but_p = but_new;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /** \} */
 |