2011-02-23 10:52:22 +00:00
|
|
|
/*
|
2009-02-04 11:52:16 +00:00
|
|
|
* 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
|
2018-05-23 10:47:12 +02:00
|
|
|
* of the License, or (at your option) any later version.
|
2009-02-04 11:52:16 +00:00
|
|
|
*
|
|
|
|
* 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,
|
2010-02-12 13:34:04 +00:00
|
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
2009-02-04 11:52:16 +00:00
|
|
|
*
|
|
|
|
* The Original Code is Copyright (C) 2009 Blender Foundation.
|
|
|
|
* All rights reserved.
|
|
|
|
*/
|
|
|
|
|
2019-02-18 08:08:12 +11:00
|
|
|
/** \file
|
|
|
|
* \ingroup edinterface
|
2011-02-27 20:29:51 +00:00
|
|
|
*/
|
|
|
|
|
2020-03-19 09:33:03 +01:00
|
|
|
#include <assert.h>
|
2020-05-06 15:51:23 +10:00
|
|
|
#include <ctype.h>
|
2009-09-16 19:36:17 +00:00
|
|
|
#include <stdio.h>
|
2009-02-04 11:52:16 +00:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
2.5
Smaller jobs, all in one commit!
- Moved object_do_update out of view3d drawing, into
the event system (currently after notifiers).
Depsgraph calls for setting update flags will have to
keep track of each Screen's needs, so a UI showing only
a Sequencer doesn't do objects.
- Added button in "Properties region" in 3D window to set
or disable 4-split, including the 3 options it has.
(lock, box, clip)
- Restored legacy code for UI, to make things work like
bone rename, autocomplete.
- Node editor now shows Curves widgets again
- Bugfix: composite job increased Viewer user id count
- Bugfix: Node editor, not "Enable nodes" still called
a Job, which didn't do anything
- Various code cleaning, unused vars and prototypes.
2009-02-11 16:54:55 +00:00
|
|
|
#include "DNA_object_types.h"
|
2014-02-08 09:03:25 +11:00
|
|
|
#include "DNA_screen_types.h"
|
2009-06-10 11:43:21 +00:00
|
|
|
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "BLI_listbase.h"
|
2014-01-08 17:04:10 +01:00
|
|
|
#include "BLI_math.h"
|
2011-09-26 16:53:04 +00:00
|
|
|
#include "BLI_string.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "BLI_utildefines.h"
|
2011-01-07 18:36:47 +00:00
|
|
|
|
2015-08-16 17:32:01 +10:00
|
|
|
#include "BLT_translation.h"
|
2011-06-15 11:41:15 +00:00
|
|
|
|
2020-04-10 20:07:11 +02:00
|
|
|
#include "BKE_lib_id.h"
|
2014-02-09 12:00:03 +11:00
|
|
|
#include "BKE_report.h"
|
2011-01-07 19:18:31 +00:00
|
|
|
|
2014-02-08 09:03:25 +11:00
|
|
|
#include "MEM_guardedalloc.h"
|
2009-02-04 11:52:16 +00:00
|
|
|
|
|
|
|
#include "RNA_access.h"
|
|
|
|
|
|
|
|
#include "UI_interface.h"
|
2020-06-29 17:19:43 +02:00
|
|
|
#include "UI_interface_icons.h"
|
2009-02-04 11:52:16 +00:00
|
|
|
#include "UI_resources.h"
|
|
|
|
|
2015-04-27 01:17:51 +10:00
|
|
|
#include "WM_api.h"
|
|
|
|
#include "WM_types.h"
|
|
|
|
|
2014-02-08 09:03:25 +11:00
|
|
|
#include "interface_intern.h"
|
|
|
|
|
2020-05-06 15:51:23 +10:00
|
|
|
bool ui_str_has_word_prefix(const char *haystack, const char *needle, size_t needle_len)
|
|
|
|
{
|
|
|
|
const char *match = BLI_strncasestr(haystack, needle, needle_len);
|
|
|
|
if (match) {
|
|
|
|
if ((match == haystack) || (*(match - 1) == ' ') || ispunct(*(match - 1))) {
|
|
|
|
return true;
|
|
|
|
}
|
2020-07-03 14:20:10 +02:00
|
|
|
return ui_str_has_word_prefix(match + 1, needle, needle_len);
|
2020-05-06 15:51:23 +10:00
|
|
|
}
|
2020-07-03 14:20:10 +02:00
|
|
|
return false;
|
2020-05-06 15:51:23 +10:00
|
|
|
}
|
|
|
|
|
2009-02-04 11:52:16 +00:00
|
|
|
/*************************** RNA Utilities ******************************/
|
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
uiBut *uiDefAutoButR(uiBlock *block,
|
|
|
|
PointerRNA *ptr,
|
|
|
|
PropertyRNA *prop,
|
|
|
|
int index,
|
|
|
|
const char *name,
|
|
|
|
int icon,
|
|
|
|
int x1,
|
|
|
|
int y1,
|
|
|
|
int x2,
|
|
|
|
int y2)
|
2009-02-04 11:52:16 +00:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
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;
|
2009-02-04 11:52:16 +00:00
|
|
|
}
|
|
|
|
|
2012-06-14 22:48:40 +00:00
|
|
|
/**
|
|
|
|
* \a check_prop callback filters functions to avoid drawing certain properties,
|
|
|
|
* in cases where PROP_HIDDEN flag can't be used for a property.
|
2019-03-22 00:30:17 +11:00
|
|
|
*
|
|
|
|
* \param prop_activate_init: Property to activate on initial popup (#UI_BUT_ACTIVATE_ON_INIT).
|
2012-06-14 22:48:40 +00:00
|
|
|
*/
|
2019-04-17 06:17:24 +02:00
|
|
|
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)
|
2009-02-04 11:52:16 +00:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
eAutoPropButsReturn return_info = UI_PROP_BUTS_NONE_ADDED;
|
UI: Better split layout support for checkboxes
Makes the following layout changes possible:
{F8473498} {F8473499} {F8473502}
The next commit will contain many layout changes to make good use of
these new possibilities. The result should be more consistent, easier to
read and should give a more organized impression. Additionally, it
should be possible to replace many sub-panels with compacter layouts.
Main changes:
* Checkboxes now respect the property split layouts
* Add support for row and column headers (i.e.
`uiLayout.column(heading="Foo")`, `uiLayout.row(heading="Bar")`). If the
first property added to this layout doesn't insert anything into the label
split column, the heading is inserted there. Otherwise, it's inserted as own
item.
* Add support for manually inserting decorators for an existing item
(`uiLayout.prop_decorator()`). That way layout creators can manually insert
this, which was the only way I saw to support property split layouts with a
checkbox before the actual property. {F8471883}
* Autogenerated layouts for operator properties look bad if there are only
checkboxes (which only use half the region width). So before creating the
layout, we iterate over visible properties and disable split layout if all
are booleans. I think this is fine, if needed we could also add layout hints
to operators.
* `uiTemplateOperatorPropertyButs()` now handles macros itself, the caller
used to be responsible for this. Code that didn't handle these so far never
used macros I think, so this change should be invisible.
* Remove manual property split layout from autogenerated operator properties
layout.
* Padding of checkboxes is tweaked to make their label visually more connected
to the checkboxes.
* Support split layout for menus (should work for `uiLayout.menu()`,
`.operator_menu_enum()`, `.prop_menu_enum()`, maybe more)
Maniphest Task: https://developer.blender.org/T65965
Differential Revision: https://developer.blender.org/D7427
Reviewed by: Brecht Van Lommel, William Reynish, Pablo Vazques
2020-04-17 16:40:25 +02:00
|
|
|
uiLayout *col;
|
2019-04-17 06:17:24 +02:00
|
|
|
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);
|
UI: Better split layout support for checkboxes
Makes the following layout changes possible:
{F8473498} {F8473499} {F8473502}
The next commit will contain many layout changes to make good use of
these new possibilities. The result should be more consistent, easier to
read and should give a more organized impression. Additionally, it
should be possible to replace many sub-panels with compacter layouts.
Main changes:
* Checkboxes now respect the property split layouts
* Add support for row and column headers (i.e.
`uiLayout.column(heading="Foo")`, `uiLayout.row(heading="Bar")`). If the
first property added to this layout doesn't insert anything into the label
split column, the heading is inserted there. Otherwise, it's inserted as own
item.
* Add support for manually inserting decorators for an existing item
(`uiLayout.prop_decorator()`). That way layout creators can manually insert
this, which was the only way I saw to support property split layouts with a
checkbox before the actual property. {F8471883}
* Autogenerated layouts for operator properties look bad if there are only
checkboxes (which only use half the region width). So before creating the
layout, we iterate over visible properties and disable split layout if all
are booleans. I think this is fine, if needed we could also add layout hints
to operators.
* `uiTemplateOperatorPropertyButs()` now handles macros itself, the caller
used to be responsible for this. Code that didn't handle these so far never
used macros I think, so this change should be invisible.
* Remove manual property split layout from autogenerated operator properties
layout.
* Padding of checkboxes is tweaked to make their label visually more connected
to the checkboxes.
* Support split layout for menus (should work for `uiLayout.menu()`,
`.operator_menu_enum()`, `.prop_menu_enum()`, maybe more)
Maniphest Task: https://developer.blender.org/T65965
Differential Revision: https://developer.blender.org/D7427
Reviewed by: Brecht Van Lommel, William Reynish, Pablo Vazques
2020-04-17 16:40:25 +02:00
|
|
|
col = uiLayoutColumn(layout, true);
|
|
|
|
/* Let uiItemFullR() create the split layout. */
|
|
|
|
uiLayoutSetPropSep(col, true);
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2.5
Summary of ain features:
- Themes and Styles are now editable.
- CTRL+U "Save user defaults" now goes to new .B25.blend, so you
can use 2.4x and 2.5x next to each other. If B25 doesn't exist, it
reads the regular .B.blend
- Press Tkey in 3d window for (unfinished) toolbar WIP. It now only
shows the last operator, if appropriate.
Nkey properties moved to the other side.
A lot of work was done on removing old themes for good and properly
getting it work with the 2.5 region system. Here's some notes;
- Buttons now all have a complete set of colors, based on button classifications
(See outliner -> user prefs -> Interface
- Theme colors have been extended with basic colors for region types.
Currently colors are defined for Window, Header, List/Channels and
for Button/Tool views.
The screen manager handles this btw, so a TH_BACK will always pick the
right backdrop color.
- Menu backdrops are in in Button theme colors. Floating Panels will be in
the per-space type Themes.
- Styles were added in RNA too, but only for the font settings now.
Only Panel font, widget font and widget-label work now. The 'group label'
will be for templates mostly.
Style settings will be expanded with spacing defaults, label conventions,
etc.
- Label text colors are stored in per-space Theme too, to make sure they fit.
Same goes for Panel title color.
Note that 'shadow' for fonts can conflict with text colors; shadow color is
currently stored in Style... shadow code needs a bit of work still.
2009-04-27 13:44:11 +00:00
|
|
|
}
|
2017-05-13 19:48:04 +02:00
|
|
|
|
2017-05-12 01:42:42 +02:00
|
|
|
/* *** RNA collection search menu *** */
|
|
|
|
|
|
|
|
typedef struct CollItemSearch {
|
2019-04-17 06:17:24 +02:00
|
|
|
struct CollItemSearch *next, *prev;
|
|
|
|
void *data;
|
|
|
|
char *name;
|
|
|
|
int index;
|
|
|
|
int iconid;
|
2020-06-29 17:19:43 +02:00
|
|
|
bool is_id;
|
2020-06-15 18:32:51 +10:00
|
|
|
uint has_sep_char : 1;
|
2017-05-12 01:42:42 +02:00
|
|
|
} CollItemSearch;
|
|
|
|
|
|
|
|
static int sort_search_items_list(const void *a, const void *b)
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
const CollItemSearch *cis1 = a;
|
|
|
|
const CollItemSearch *cis2 = b;
|
|
|
|
|
|
|
|
if (BLI_strcasecmp(cis1->name, cis2->name) > 0) {
|
|
|
|
return 1;
|
|
|
|
}
|
2020-07-03 14:20:10 +02:00
|
|
|
return 0;
|
2017-05-12 01:42:42 +02:00
|
|
|
}
|
|
|
|
|
2020-05-07 23:16:05 +10:00
|
|
|
void ui_rna_collection_search_update_fn(const struct bContext *C,
|
|
|
|
void *arg,
|
|
|
|
const char *str,
|
|
|
|
uiSearchItems *items)
|
2017-05-12 01:42:42 +02:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
uiRNACollectionSearch *data = arg;
|
2020-06-15 18:32:51 +10:00
|
|
|
const int flag = RNA_property_flag(data->target_prop);
|
|
|
|
int i = 0;
|
2019-04-17 06:17:24 +02:00
|
|
|
ListBase *items_list = MEM_callocN(sizeof(ListBase), "items_list");
|
|
|
|
CollItemSearch *cis;
|
2020-05-11 16:55:33 +02:00
|
|
|
const bool is_ptr_target = (RNA_property_type(data->target_prop) == PROP_POINTER);
|
|
|
|
/* For non-pointer properties, UI code acts entirely based on the item's name. So the name has to
|
|
|
|
* match the RNA name exactly. So only for pointer properties, the name can be modified to add
|
|
|
|
* further UI hints. */
|
|
|
|
const bool requires_exact_data_name = !is_ptr_target;
|
2020-04-08 23:08:32 +02:00
|
|
|
const bool skip_filter = data->search_but && !data->search_but->changed;
|
2020-04-10 20:07:11 +02:00
|
|
|
char name_buf[UI_MAX_DRAW_STR];
|
|
|
|
char *name;
|
2020-06-29 17:19:43 +02:00
|
|
|
bool has_id_icon = false;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
|
|
|
/* build a temporary list of relevant items first */
|
|
|
|
RNA_PROP_BEGIN (&data->search_ptr, itemptr, data->search_prop) {
|
|
|
|
|
|
|
|
if (flag & PROP_ID_SELF_CHECK) {
|
2019-08-23 09:52:12 +02:00
|
|
|
if (itemptr.data == data->target_ptr.owner_id) {
|
2019-04-17 06:17:24 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* use filter */
|
2020-05-11 16:55:33 +02:00
|
|
|
if (is_ptr_target) {
|
2019-04-17 06:17:24 +02:00
|
|
|
if (RNA_property_pointer_poll(&data->target_ptr, data->target_prop, &itemptr) == 0) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-15 18:32:51 +10:00
|
|
|
int iconid = ICON_NONE;
|
|
|
|
bool has_sep_char = false;
|
2020-06-29 17:19:43 +02:00
|
|
|
bool is_id = itemptr.type && RNA_struct_is_ID(itemptr.type);
|
2020-06-15 18:32:51 +10:00
|
|
|
|
2020-06-29 17:19:43 +02:00
|
|
|
if (is_id) {
|
2019-04-17 06:17:24 +02:00
|
|
|
iconid = ui_id_icon_get(C, itemptr.data, false);
|
2020-06-29 17:19:43 +02:00
|
|
|
if (!ELEM(iconid, 0, ICON_BLANK1)) {
|
|
|
|
has_id_icon = true;
|
|
|
|
}
|
2020-04-10 20:07:11 +02:00
|
|
|
|
2020-05-11 16:55:33 +02:00
|
|
|
if (requires_exact_data_name) {
|
|
|
|
name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), NULL);
|
|
|
|
}
|
|
|
|
else {
|
2020-06-15 18:32:51 +10:00
|
|
|
const ID *id = itemptr.data;
|
2020-06-29 17:19:43 +02:00
|
|
|
BKE_id_full_name_ui_prefix_get(name_buf, itemptr.data, true, UI_SEP_CHAR);
|
2020-05-11 16:55:33 +02:00
|
|
|
BLI_STATIC_ASSERT(sizeof(name_buf) >= MAX_ID_FULL_NAME_UI,
|
|
|
|
"Name string buffer should be big enough to hold full UI ID name");
|
|
|
|
name = name_buf;
|
2020-06-15 18:32:51 +10:00
|
|
|
has_sep_char = (id->lib != NULL);
|
2020-05-11 16:55:33 +02:00
|
|
|
}
|
2020-04-10 20:07:11 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
name = RNA_struct_name_get_alloc(&itemptr, name_buf, sizeof(name_buf), NULL);
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (name) {
|
|
|
|
if (skip_filter || BLI_strcasestr(name, str)) {
|
|
|
|
cis = MEM_callocN(sizeof(CollItemSearch), "CollectionItemSearch");
|
|
|
|
cis->data = itemptr.data;
|
2020-04-10 20:07:11 +02:00
|
|
|
cis->name = BLI_strdup(name);
|
2019-04-17 06:17:24 +02:00
|
|
|
cis->index = i;
|
|
|
|
cis->iconid = iconid;
|
2020-06-29 17:19:43 +02:00
|
|
|
cis->is_id = is_id;
|
2020-06-15 18:32:51 +10:00
|
|
|
cis->has_sep_char = has_sep_char;
|
2019-04-17 06:17:24 +02:00
|
|
|
BLI_addtail(items_list, cis);
|
|
|
|
}
|
2020-04-10 20:07:11 +02:00
|
|
|
if (name != name_buf) {
|
|
|
|
MEM_freeN(name);
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2020-06-29 17:19:43 +02:00
|
|
|
/* If no item has an own icon to display, libraries can use the library icons rather than the
|
|
|
|
* name prefix for showing the library status. */
|
|
|
|
if (!has_id_icon && cis->is_id) {
|
|
|
|
cis->iconid = UI_library_icon_get(cis->data);
|
|
|
|
/* No need to re-allocate, string should be shorter than before (lib status prefix is
|
|
|
|
* removed). */
|
|
|
|
BKE_id_full_name_ui_prefix_get(name_buf, cis->data, false, UI_SEP_CHAR);
|
|
|
|
BLI_assert(strlen(name_buf) <= MEM_allocN_len(cis->name));
|
|
|
|
strcpy(cis->name, name_buf);
|
|
|
|
}
|
|
|
|
|
2020-06-15 18:32:51 +10:00
|
|
|
if (!UI_search_item_add(items,
|
|
|
|
cis->name,
|
|
|
|
cis->data,
|
|
|
|
cis->iconid,
|
|
|
|
cis->has_sep_char ? UI_BUT_HAS_SEP_CHAR : 0)) {
|
2019-04-17 06:17:24 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (cis = items_list->first; cis; cis = cis->next) {
|
|
|
|
MEM_freeN(cis->name);
|
|
|
|
}
|
|
|
|
BLI_freelistN(items_list);
|
|
|
|
MEM_freeN(items_list);
|
2017-05-12 01:42:42 +02:00
|
|
|
}
|
|
|
|
|
2018-05-23 10:50:35 +02:00
|
|
|
/***************************** ID Utilities *******************************/
|
2014-11-09 21:20:40 +01:00
|
|
|
int UI_icon_from_id(ID *id)
|
2009-09-16 01:15:30 +00:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
Object *ob;
|
|
|
|
PointerRNA ptr;
|
|
|
|
short idcode;
|
2009-06-03 00:14:12 +00:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
if (id == NULL) {
|
|
|
|
return ICON_NONE;
|
|
|
|
}
|
2018-05-23 10:47:12 +02:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
idcode = GS(id->name);
|
2009-07-23 20:50:24 +00:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
/* exception for objects */
|
|
|
|
if (idcode == ID_OB) {
|
|
|
|
ob = (Object *)id;
|
2009-06-03 00:14:12 +00:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
if (ob->type == OB_EMPTY) {
|
|
|
|
return ICON_EMPTY_DATA;
|
|
|
|
}
|
2020-07-03 14:20:10 +02:00
|
|
|
return UI_icon_from_id(ob->data);
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2009-06-03 00:14:12 +00:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
/* otherwise get it through RNA, creating the pointer
|
|
|
|
* will set the right type, also with subclassing */
|
|
|
|
RNA_id_pointer_create(id, &ptr);
|
2009-06-03 00:14:12 +00:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
return (ptr.type) ? RNA_struct_ui_icon(ptr.type) : ICON_NONE;
|
2009-06-03 00:14:12 +00:00
|
|
|
}
|
2014-01-08 17:04:10 +01:00
|
|
|
|
2014-02-09 12:00:03 +11:00
|
|
|
/* see: report_type_str */
|
2014-11-09 21:20:40 +01:00
|
|
|
int UI_icon_from_report_type(int type)
|
2014-02-09 12:00:03 +11:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
if (type & RPT_ERROR_ALL) {
|
|
|
|
return ICON_ERROR;
|
|
|
|
}
|
2020-07-03 14:20:10 +02:00
|
|
|
if (type & RPT_WARNING_ALL) {
|
2019-04-17 06:17:24 +02:00
|
|
|
return ICON_ERROR;
|
|
|
|
}
|
2020-07-03 14:20:10 +02:00
|
|
|
if (type & RPT_INFO_ALL) {
|
2019-04-17 06:17:24 +02:00
|
|
|
return ICON_INFO;
|
|
|
|
}
|
2020-07-03 14:20:10 +02:00
|
|
|
return ICON_NONE;
|
2014-02-09 12:00:03 +11:00
|
|
|
}
|
|
|
|
|
2014-01-08 17:04:10 +01:00
|
|
|
/********************************** Misc **************************************/
|
|
|
|
|
|
|
|
/**
|
2019-04-17 08:44:38 +02:00
|
|
|
* Returns the best "UI" precision for given floating value,
|
|
|
|
* so that e.g. 10.000001 rather gets drawn as '10'...
|
2014-01-08 17:04:10 +01:00
|
|
|
*/
|
2014-11-09 21:20:40 +01:00
|
|
|
int UI_calc_float_precision(int prec, double value)
|
2014-01-08 17:04:10 +01:00
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
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);
|
|
|
|
|
2019-04-17 08:44:38 +02:00
|
|
|
/* Check on the number of decimal places need to display the number,
|
|
|
|
* this is so 0.00001 is not displayed as 0.00,
|
2019-04-17 06:17:24 +02:00
|
|
|
* _but_, this is only for small values si 10.0001 will not get the same treatment.
|
|
|
|
*/
|
2020-03-06 17:18:10 +01:00
|
|
|
value = fabs(value);
|
2019-04-17 06:17:24 +02:00
|
|
|
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;
|
2014-01-08 17:04:10 +01:00
|
|
|
}
|
2014-02-08 09:03:25 +11:00
|
|
|
|
2015-04-27 01:17:51 +10:00
|
|
|
bool UI_but_online_manual_id(const uiBut *but, char *r_str, size_t maxlength)
|
|
|
|
{
|
2019-08-23 09:52:12 +02:00
|
|
|
if (but->rnapoin.owner_id && but->rnapoin.data && but->rnaprop) {
|
2019-04-17 06:17:24 +02:00
|
|
|
BLI_snprintf(r_str,
|
|
|
|
maxlength,
|
|
|
|
"%s.%s",
|
|
|
|
RNA_struct_identifier(but->rnapoin.type),
|
|
|
|
RNA_property_identifier(but->rnaprop));
|
|
|
|
return true;
|
|
|
|
}
|
2020-07-03 14:20:10 +02:00
|
|
|
if (but->optype) {
|
2019-04-17 06:17:24 +02:00
|
|
|
WM_operator_py_idname(r_str, but->optype->idname);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
*r_str = '\0';
|
|
|
|
return false;
|
2015-04-27 01:17:51 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
bool UI_but_online_manual_id_from_active(const struct bContext *C, char *r_str, size_t maxlength)
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
uiBut *but = UI_context_active_but_get(C);
|
2015-04-27 01:17:51 +10:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
if (but) {
|
|
|
|
return UI_but_online_manual_id(but, r_str, maxlength);
|
|
|
|
}
|
2015-04-27 01:17:51 +10:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
*r_str = '\0';
|
|
|
|
return false;
|
2015-04-27 01:17:51 +10:00
|
|
|
}
|
|
|
|
|
2014-02-08 09:03:25 +11:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/* 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.
|
|
|
|
*
|
|
|
|
* \{ */
|
|
|
|
|
2014-02-09 13:56:49 +01:00
|
|
|
struct uiButStore {
|
2019-04-17 06:17:24 +02:00
|
|
|
struct uiButStore *next, *prev;
|
|
|
|
uiBlock *block;
|
|
|
|
ListBase items;
|
2014-02-09 13:56:49 +01:00
|
|
|
};
|
2014-02-08 09:03:25 +11:00
|
|
|
|
2014-02-09 13:56:49 +01:00
|
|
|
struct uiButStoreElem {
|
2019-04-17 06:17:24 +02:00
|
|
|
struct uiButStoreElem *next, *prev;
|
|
|
|
uiBut **but_p;
|
2014-02-09 13:56:49 +01:00
|
|
|
};
|
2014-02-08 09:03:25 +11:00
|
|
|
|
|
|
|
/**
|
2014-03-15 20:08:29 +11:00
|
|
|
* Create a new button store, the caller must manage and run #UI_butstore_free
|
2014-02-08 09:03:25 +11:00
|
|
|
*/
|
|
|
|
uiButStore *UI_butstore_create(uiBlock *block)
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
uiButStore *bs_handle = MEM_callocN(sizeof(uiButStore), __func__);
|
2014-02-08 09:03:25 +11:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
bs_handle->block = block;
|
|
|
|
BLI_addtail(&block->butstore, bs_handle);
|
2014-02-08 09:03:25 +11:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
return bs_handle;
|
2014-02-08 09:03:25 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
void UI_butstore_free(uiBlock *block, uiButStore *bs_handle)
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
/* Workaround for button store being moved into new block,
|
2019-04-17 08:44:38 +02:00
|
|
|
* 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.
|
2019-04-17 06:17:24 +02:00
|
|
|
*
|
|
|
|
* 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);
|
2020-04-08 22:36:58 +02:00
|
|
|
BLI_assert(BLI_findindex(&block->butstore, bs_handle) != -1);
|
2019-04-17 06:17:24 +02:00
|
|
|
BLI_remlink(&block->butstore, bs_handle);
|
|
|
|
|
|
|
|
MEM_freeN(bs_handle);
|
2014-02-08 09:03:25 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
bool UI_butstore_is_valid(uiButStore *bs)
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
return (bs->block != NULL);
|
2014-02-08 09:03:25 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
bool UI_butstore_is_registered(uiBlock *block, uiBut *but)
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
uiButStore *bs_handle;
|
2014-02-08 09:03:25 +11:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
for (bs_handle = block->butstore.first; bs_handle; bs_handle = bs_handle->next) {
|
|
|
|
uiButStoreElem *bs_elem;
|
2014-02-08 09:03:25 +11:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
for (bs_elem = bs_handle->items.first; bs_elem; bs_elem = bs_elem->next) {
|
|
|
|
if (*bs_elem->but_p == but) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-02-08 09:03:25 +11:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
return false;
|
2014-02-08 09:03:25 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
void UI_butstore_register(uiButStore *bs_handle, uiBut **but_p)
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
uiButStoreElem *bs_elem = MEM_callocN(sizeof(uiButStoreElem), __func__);
|
|
|
|
BLI_assert(*but_p);
|
|
|
|
bs_elem->but_p = but_p;
|
2014-02-08 09:03:25 +11:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
BLI_addtail(&bs_handle->items, bs_elem);
|
2014-02-08 09:03:25 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
void UI_butstore_unregister(uiButStore *bs_handle, uiBut **but_p)
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
uiButStoreElem *bs_elem, *bs_elem_next;
|
2014-02-08 09:03:25 +11:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2014-02-08 09:03:25 +11:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
BLI_assert(0);
|
2014-02-08 09:03:25 +11:00
|
|
|
}
|
|
|
|
|
2014-12-01 23:30:54 +01:00
|
|
|
/**
|
|
|
|
* Update the pointer for a registered button.
|
|
|
|
*/
|
|
|
|
bool UI_butstore_register_update(uiBlock *block, uiBut *but_dst, const uiBut *but_src)
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
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;
|
2014-12-01 23:30:54 +01:00
|
|
|
}
|
|
|
|
|
2014-02-08 09:03:25 +11:00
|
|
|
/**
|
|
|
|
* NULL all pointers, don't free since the owner needs to be able to inspect.
|
|
|
|
*/
|
|
|
|
void UI_butstore_clear(uiBlock *block)
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
uiButStore *bs_handle;
|
2014-02-08 09:03:25 +11:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
for (bs_handle = block->butstore.first; bs_handle; bs_handle = bs_handle->next) {
|
|
|
|
uiButStoreElem *bs_elem;
|
2014-02-08 09:03:25 +11:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
bs_handle->block = NULL;
|
2014-02-08 09:03:25 +11:00
|
|
|
|
2019-04-17 06:17:24 +02:00
|
|
|
for (bs_elem = bs_handle->items.first; bs_elem; bs_elem = bs_elem->next) {
|
|
|
|
*bs_elem->but_p = NULL;
|
|
|
|
}
|
|
|
|
}
|
2014-02-08 09:03:25 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Map freed buttons from the old block and update pointers.
|
|
|
|
*/
|
|
|
|
void UI_butstore_update(uiBlock *block)
|
|
|
|
{
|
2019-04-17 06:17:24 +02:00
|
|
|
uiButStore *bs_handle;
|
|
|
|
|
|
|
|
/* move this list to the new block */
|
|
|
|
if (block->oldblock) {
|
|
|
|
if (block->oldblock->butstore.first) {
|
2020-04-08 22:36:58 +02:00
|
|
|
BLI_movelisttolist(&block->butstore, &block->oldblock->butstore);
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-02-08 09:03:25 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
/** \} */
|