Merge branch 'master' into sculpt-dev
This commit is contained in:
commit
a3e4f9d9a6
|
@ -174,7 +174,7 @@ class SpellChecker:
|
|||
"premultiply", "premultiplied",
|
||||
"prepass",
|
||||
"prepend",
|
||||
"preprocess", "preprocessing",
|
||||
"preprocess", "preprocessing", "preprocessor",
|
||||
"preseek",
|
||||
"promillage",
|
||||
"pushdown",
|
||||
|
@ -549,7 +549,7 @@ class SpellChecker:
|
|||
"freestyle",
|
||||
"enum", "enums",
|
||||
"gizmogroup",
|
||||
"gons", # N-Gons
|
||||
"gon", "gons", # N-Gon(s)
|
||||
"gpencil",
|
||||
"idcol",
|
||||
"keyframe", "keyframes", "keyframing", "keyframed",
|
||||
|
|
|
@ -592,7 +592,7 @@ class GreasePencilMaterialsPanel:
|
|||
if show_full_ui:
|
||||
row = layout.row()
|
||||
|
||||
row.template_ID(ob, "active_material", new="material.new", live_icon=True)
|
||||
row.template_ID(ob, "active_material", new="material.new", duplicate="material.duplicate", live_icon=True)
|
||||
|
||||
slot = context.material_slot
|
||||
if slot:
|
||||
|
|
|
@ -93,10 +93,10 @@ class NODE_HT_header(Header):
|
|||
|
||||
# Show material.new when no active ID/slot exists
|
||||
if not id_from and ob_type in types_that_support_material:
|
||||
row.template_ID(ob, "active_material", new="material.new")
|
||||
row.template_ID(ob, "active_material", new="material.new", duplicate="material.duplicate")
|
||||
# Material ID, but not for Lights
|
||||
if id_from and ob_type != 'LIGHT':
|
||||
row.template_ID(id_from, "active_material", new="material.new")
|
||||
row.template_ID(id_from, "active_material", new="material.new", duplicate="material.duplicate")
|
||||
|
||||
if snode.shader_type == 'WORLD':
|
||||
NODE_MT_editor_menus.draw_collapsible(context, layout)
|
||||
|
@ -109,7 +109,7 @@ class NODE_HT_header(Header):
|
|||
|
||||
row = layout.row()
|
||||
row.enabled = not snode.pin
|
||||
row.template_ID(scene, "world", new="world.new")
|
||||
row.template_ID(scene, "world", new="world.new", duplicate="world.duplicate")
|
||||
|
||||
if snode.shader_type == 'LINESTYLE':
|
||||
view_layer = context.view_layer
|
||||
|
|
|
@ -410,6 +410,10 @@ class OUTLINER_PT_filter(Panel):
|
|||
row = sub.row()
|
||||
row.label(icon='EMPTY_DATA')
|
||||
row.prop(space, "use_filter_object_empty", text="Empties")
|
||||
row = sub.row()
|
||||
if bpy.data.libraries:
|
||||
row.label(icon='LIBRARY_DATA_OVERRIDE')
|
||||
row.prop(space, "use_filter_lib_override", text="Library Overrides")
|
||||
|
||||
if (
|
||||
bpy.data.curves or
|
||||
|
|
|
@ -95,6 +95,10 @@ struct IDOverrideLibraryProperty *BKE_lib_override_library_property_get(
|
|||
struct IDOverrideLibrary *override, const char *rna_path, bool *r_created);
|
||||
void BKE_lib_override_library_property_delete(struct IDOverrideLibrary *override,
|
||||
struct IDOverrideLibraryProperty *override_property);
|
||||
bool BKE_lib_override_rna_property_find(struct PointerRNA *idpoin,
|
||||
const struct IDOverrideLibraryProperty *library_prop,
|
||||
struct PointerRNA *r_override_poin,
|
||||
struct PropertyRNA **r_override_prop);
|
||||
|
||||
struct IDOverrideLibraryPropertyOperation *BKE_lib_override_library_property_operation_find(
|
||||
struct IDOverrideLibraryProperty *override_property,
|
||||
|
@ -131,7 +135,7 @@ bool BKE_lib_override_library_status_check_local(struct Main *bmain, struct ID *
|
|||
bool BKE_lib_override_library_status_check_reference(struct Main *bmain, struct ID *local);
|
||||
|
||||
bool BKE_lib_override_library_operations_create(struct Main *bmain, struct ID *local);
|
||||
void BKE_lib_override_library_main_operations_create(struct Main *bmain, const bool force_auto);
|
||||
bool BKE_lib_override_library_main_operations_create(struct Main *bmain, const bool force_auto);
|
||||
|
||||
void BKE_lib_override_library_id_reset(struct Main *bmain, struct ID *id_root);
|
||||
void BKE_lib_override_library_id_hierarchy_reset(struct Main *bmain, struct ID *id_root);
|
||||
|
|
|
@ -96,6 +96,12 @@ typedef struct UndoStep {
|
|||
/* Over alloc 'type->struct_size'. */
|
||||
} UndoStep;
|
||||
|
||||
typedef enum UndoPushReturn {
|
||||
UNDO_PUSH_RET_FAILURE = 0,
|
||||
UNDO_PUSH_RET_SUCCESS = (1 << 0),
|
||||
UNDO_PUSH_RET_OVERRIDE_CHANGED = (1 << 1),
|
||||
} UndoPushReturn;
|
||||
|
||||
typedef void (*UndoTypeForEachIDRefFn)(void *user_data, struct UndoRefID *id_ref);
|
||||
|
||||
typedef struct UndoType {
|
||||
|
@ -172,11 +178,11 @@ UndoStep *BKE_undosys_step_push_init_with_type(UndoStack *ustack,
|
|||
const UndoType *ut);
|
||||
UndoStep *BKE_undosys_step_push_init(UndoStack *ustack, struct bContext *C, const char *name);
|
||||
|
||||
bool BKE_undosys_step_push_with_type(UndoStack *ustack,
|
||||
struct bContext *C,
|
||||
const char *name,
|
||||
const UndoType *ut);
|
||||
bool BKE_undosys_step_push(UndoStack *ustack, struct bContext *C, const char *name);
|
||||
UndoPushReturn BKE_undosys_step_push_with_type(UndoStack *ustack,
|
||||
struct bContext *C,
|
||||
const char *name,
|
||||
const UndoType *ut);
|
||||
UndoPushReturn BKE_undosys_step_push(UndoStack *ustack, struct bContext *C, const char *name);
|
||||
|
||||
UndoStep *BKE_undosys_step_find_by_name_with_type(UndoStack *ustack,
|
||||
const char *name,
|
||||
|
|
|
@ -57,6 +57,8 @@
|
|||
#include "RNA_access.h"
|
||||
#include "RNA_types.h"
|
||||
|
||||
#include "atomic_ops.h"
|
||||
|
||||
#define OVERRIDE_AUTO_CHECK_DELAY 0.2 /* 200ms between auto-override checks. */
|
||||
//#define DEBUG_OVERRIDE_TIMEIT
|
||||
|
||||
|
@ -982,6 +984,23 @@ IDOverrideLibraryProperty *BKE_lib_override_library_property_get(IDOverrideLibra
|
|||
return op;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the RNA-property matching the \a library_prop override property. Used for UI to query
|
||||
* additional data about the overridden property (e.g. UI name).
|
||||
*
|
||||
* \param idpoin: Pointer to the override ID.
|
||||
* \param library_prop: The library override property to find the matching RNA property for.
|
||||
*/
|
||||
bool BKE_lib_override_rna_property_find(PointerRNA *idpoin,
|
||||
const IDOverrideLibraryProperty *library_prop,
|
||||
PointerRNA *r_override_poin,
|
||||
PropertyRNA **r_override_prop)
|
||||
{
|
||||
BLI_assert(RNA_struct_is_ID(idpoin->type) && ID_IS_OVERRIDE_LIBRARY(idpoin->data));
|
||||
return RNA_path_resolve_property(
|
||||
idpoin, library_prop->rna_path, r_override_poin, r_override_prop);
|
||||
}
|
||||
|
||||
void lib_override_library_property_copy(IDOverrideLibraryProperty *op_dst,
|
||||
IDOverrideLibraryProperty *op_src)
|
||||
{
|
||||
|
@ -1370,19 +1389,20 @@ bool BKE_lib_override_library_status_check_reference(Main *bmain, ID *local)
|
|||
* since it has to go over all properties in depth (all overridable ones at least).
|
||||
* Generating differential values and applying overrides are much cheaper.
|
||||
*
|
||||
* \return true if a new overriding op was created, or some local data was reset. */
|
||||
* \return true if any library operation was created.
|
||||
*/
|
||||
bool BKE_lib_override_library_operations_create(Main *bmain, ID *local)
|
||||
{
|
||||
BLI_assert(local->override_library != NULL);
|
||||
const bool is_template = (local->override_library->reference == NULL);
|
||||
bool ret = false;
|
||||
bool created = false;
|
||||
|
||||
if (!is_template) {
|
||||
/* Do not attempt to generate overriding rules from an empty place-holder generated by link
|
||||
* code when it cannot find the actual library/ID. Much better to keep the local data-block as
|
||||
* is in the file in that case, until broken lib is fixed. */
|
||||
if (ID_MISSING(local->override_library->reference)) {
|
||||
return ret;
|
||||
return created;
|
||||
}
|
||||
|
||||
if (GS(local->name) == ID_OB) {
|
||||
|
@ -1412,14 +1432,16 @@ bool BKE_lib_override_library_operations_create(Main *bmain, ID *local)
|
|||
local->override_library,
|
||||
RNA_OVERRIDE_COMPARE_CREATE | RNA_OVERRIDE_COMPARE_RESTORE,
|
||||
&report_flags);
|
||||
|
||||
if (report_flags & RNA_OVERRIDE_MATCH_RESULT_CREATED) {
|
||||
ret = true;
|
||||
created = true;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (report_flags & RNA_OVERRIDE_MATCH_RESULT_RESTORED) {
|
||||
printf("We did restore some properties of %s from its reference.\n", local->name);
|
||||
}
|
||||
if (ret) {
|
||||
if (report_flags & RNA_OVERRIDE_MATCH_RESULT_CREATED) {
|
||||
printf("We did generate library override rules for %s\n", local->name);
|
||||
}
|
||||
else {
|
||||
|
@ -1427,19 +1449,28 @@ bool BKE_lib_override_library_operations_create(Main *bmain, ID *local)
|
|||
}
|
||||
#endif
|
||||
}
|
||||
return ret;
|
||||
return created;
|
||||
}
|
||||
|
||||
struct LibOverrideOpCreateData {
|
||||
Main *bmain;
|
||||
bool changed;
|
||||
};
|
||||
|
||||
static void lib_override_library_operations_create_cb(TaskPool *__restrict pool, void *taskdata)
|
||||
{
|
||||
Main *bmain = BLI_task_pool_user_data(pool);
|
||||
struct LibOverrideOpCreateData *create_data = BLI_task_pool_user_data(pool);
|
||||
ID *id = taskdata;
|
||||
|
||||
BKE_lib_override_library_operations_create(bmain, id);
|
||||
if (BKE_lib_override_library_operations_create(create_data->bmain, id)) {
|
||||
/* Technically no need for atomic, all jobs write the same value and we only care if one did
|
||||
* it. But play safe and avoid implicit assumptions. */
|
||||
atomic_fetch_and_or_uint8((uint8_t *)&create_data->changed, true);
|
||||
}
|
||||
}
|
||||
|
||||
/** Check all overrides from given \a bmain and create/update overriding operations as needed. */
|
||||
void BKE_lib_override_library_main_operations_create(Main *bmain, const bool force_auto)
|
||||
bool BKE_lib_override_library_main_operations_create(Main *bmain, const bool force_auto)
|
||||
{
|
||||
ID *id;
|
||||
|
||||
|
@ -1464,7 +1495,8 @@ void BKE_lib_override_library_main_operations_create(Main *bmain, const bool for
|
|||
}
|
||||
}
|
||||
|
||||
TaskPool *task_pool = BLI_task_pool_create(bmain, TASK_PRIORITY_HIGH);
|
||||
struct LibOverrideOpCreateData create_pool_data = {.bmain = bmain, .changed = false};
|
||||
TaskPool *task_pool = BLI_task_pool_create(&create_pool_data, TASK_PRIORITY_HIGH);
|
||||
|
||||
FOREACH_MAIN_ID_BEGIN (bmain, id) {
|
||||
if (ID_IS_OVERRIDE_LIBRARY_REAL(id) &&
|
||||
|
@ -1503,6 +1535,8 @@ void BKE_lib_override_library_main_operations_create(Main *bmain, const bool for
|
|||
#ifdef DEBUG_OVERRIDE_TIMEIT
|
||||
TIMEIT_END_AVERAGED(BKE_lib_override_library_main_operations_create);
|
||||
#endif
|
||||
|
||||
return create_pool_data.changed;
|
||||
}
|
||||
|
||||
static bool lib_override_library_id_reset_do(Main *bmain, ID *id_root)
|
||||
|
|
|
@ -845,9 +845,7 @@ static void mesh_tessface_clear_intern(Mesh *mesh, int free_customdata)
|
|||
|
||||
Mesh *BKE_mesh_add(Main *bmain, const char *name)
|
||||
{
|
||||
Mesh *me;
|
||||
|
||||
me = BKE_id_new(bmain, ID_ME, name);
|
||||
Mesh *me = BKE_id_new(bmain, ID_ME, name);
|
||||
|
||||
return me;
|
||||
}
|
||||
|
@ -1007,10 +1005,9 @@ BMesh *BKE_mesh_to_bmesh_ex(const Mesh *me,
|
|||
const struct BMeshCreateParams *create_params,
|
||||
const struct BMeshFromMeshParams *convert_params)
|
||||
{
|
||||
BMesh *bm;
|
||||
const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(me);
|
||||
|
||||
bm = BM_mesh_create(&allocsize, create_params);
|
||||
BMesh *bm = BM_mesh_create(&allocsize, create_params);
|
||||
BM_mesh_bm_from_me(bm, me, convert_params);
|
||||
|
||||
return bm;
|
||||
|
@ -1162,17 +1159,14 @@ void BKE_mesh_texspace_copy_from_object(Mesh *me, Object *ob)
|
|||
float (*BKE_mesh_orco_verts_get(Object *ob))[3]
|
||||
{
|
||||
Mesh *me = ob->data;
|
||||
MVert *mvert = NULL;
|
||||
Mesh *tme = me->texcomesh ? me->texcomesh : me;
|
||||
int a, totvert;
|
||||
float(*vcos)[3] = NULL;
|
||||
|
||||
/* Get appropriate vertex coordinates */
|
||||
vcos = MEM_calloc_arrayN(me->totvert, sizeof(*vcos), "orco mesh");
|
||||
mvert = tme->mvert;
|
||||
totvert = min_ii(tme->totvert, me->totvert);
|
||||
float(*vcos)[3] = MEM_calloc_arrayN(me->totvert, sizeof(*vcos), "orco mesh");
|
||||
MVert *mvert = tme->mvert;
|
||||
int totvert = min_ii(tme->totvert, me->totvert);
|
||||
|
||||
for (a = 0; a < totvert; a++, mvert++) {
|
||||
for (int a = 0; a < totvert; a++, mvert++) {
|
||||
copy_v3_v3(vcos[a], mvert->co);
|
||||
}
|
||||
|
||||
|
@ -1182,18 +1176,17 @@ float (*BKE_mesh_orco_verts_get(Object *ob))[3]
|
|||
void BKE_mesh_orco_verts_transform(Mesh *me, float (*orco)[3], int totvert, int invert)
|
||||
{
|
||||
float loc[3], size[3];
|
||||
int a;
|
||||
|
||||
BKE_mesh_texspace_get(me->texcomesh ? me->texcomesh : me, loc, size);
|
||||
|
||||
if (invert) {
|
||||
for (a = 0; a < totvert; a++) {
|
||||
for (int a = 0; a < totvert; a++) {
|
||||
float *co = orco[a];
|
||||
madd_v3_v3v3v3(co, loc, co, size);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (a = 0; a < totvert; a++) {
|
||||
for (int a = 0; a < totvert; a++) {
|
||||
float *co = orco[a];
|
||||
co[0] = (co[0] - loc[0]) / size[0];
|
||||
co[1] = (co[1] - loc[1]) / size[1];
|
||||
|
@ -1274,7 +1267,6 @@ int test_index_face(MFace *mface, CustomData *fdata, int mfindex, int nr)
|
|||
|
||||
Mesh *BKE_mesh_from_object(Object *ob)
|
||||
{
|
||||
|
||||
if (ob == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -1414,8 +1406,7 @@ void BKE_mesh_smooth_flag_set(Mesh *me, const bool use_smooth)
|
|||
*/
|
||||
int poly_find_loop_from_vert(const MPoly *poly, const MLoop *loopstart, uint vert)
|
||||
{
|
||||
int j;
|
||||
for (j = 0; j < poly->totloop; j++, loopstart++) {
|
||||
for (int j = 0; j < poly->totloop; j++, loopstart++) {
|
||||
if (loopstart->v == vert) {
|
||||
return j;
|
||||
}
|
||||
|
@ -1682,11 +1673,9 @@ void BKE_mesh_mselect_validate(Mesh *me)
|
|||
*/
|
||||
int BKE_mesh_mselect_find(Mesh *me, int index, int type)
|
||||
{
|
||||
int i;
|
||||
|
||||
BLI_assert(ELEM(type, ME_VSEL, ME_ESEL, ME_FSEL));
|
||||
|
||||
for (i = 0; i < me->totselect; i++) {
|
||||
for (int i = 0; i < me->totselect; i++) {
|
||||
if ((me->mselect[i].index == index) && (me->mselect[i].type == type)) {
|
||||
return i;
|
||||
}
|
||||
|
|
|
@ -154,12 +154,10 @@ int BKE_mesh_runtime_looptri_len(const Mesh *mesh)
|
|||
/* This is a ported copy of dm_getLoopTriArray(dm). */
|
||||
const MLoopTri *BKE_mesh_runtime_looptri_ensure(Mesh *mesh)
|
||||
{
|
||||
MLoopTri *looptri;
|
||||
|
||||
ThreadMutex *mesh_eval_mutex = (ThreadMutex *)mesh->runtime.eval_mutex;
|
||||
BLI_mutex_lock(mesh_eval_mutex);
|
||||
|
||||
looptri = mesh->runtime.looptris.array;
|
||||
MLoopTri *looptri = mesh->runtime.looptris.array;
|
||||
|
||||
if (looptri != NULL) {
|
||||
BLI_assert(BKE_mesh_runtime_looptri_len(mesh) == mesh->runtime.looptris.len);
|
||||
|
@ -180,8 +178,7 @@ void BKE_mesh_runtime_verttri_from_looptri(MVertTri *r_verttri,
|
|||
const MLoopTri *looptri,
|
||||
int looptri_num)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < looptri_num; i++) {
|
||||
for (int i = 0; i < looptri_num; i++) {
|
||||
r_verttri[i].tri[0] = mloop[looptri[i].tri[0]].v;
|
||||
r_verttri[i].tri[1] = mloop[looptri[i].tri[1]].v;
|
||||
r_verttri[i].tri[2] = mloop[looptri[i].tri[2]].v;
|
||||
|
|
|
@ -60,25 +60,30 @@ bool multires_reshape_assign_final_coords_from_ccg(const MultiresReshapeContext
|
|||
CCG_grid_elem_co(&reshape_level_key, ccg_grid, x, y),
|
||||
sizeof(float[3]));
|
||||
|
||||
if (reshape_level_key.has_mask) {
|
||||
/* Assert about a non-NULL `grid_element.mask` may fail here, this code may be called
|
||||
* from cleanup code during COW evaluation phase by depsgraph (e.g.
|
||||
* `object_update_from_subsurf_ccg` call in `BKE_object_free_derived_caches`).
|
||||
*
|
||||
* `reshape_level_key.has_mask` is ultimately set from MultiRes modifier apply code
|
||||
* (through `multires_as_ccg` -> `multires_ccg_settings_init`), when object is in sculpt
|
||||
* mode only, and there is matching loop cdlayer.
|
||||
*
|
||||
* `grid_element.mask` is directly set from existing matching loop cdlayer during
|
||||
* initialization of `MultiresReshapeContext` struct.
|
||||
*
|
||||
* Since ccg data is preserved during undos, we may end up with a state where there is no
|
||||
* mask data in mesh loops' cdlayer, while ccg's `has_mask` is still set to true.
|
||||
*/
|
||||
// BLI_assert(grid_element.mask != NULL);
|
||||
if (grid_element.mask != NULL) {
|
||||
*grid_element.mask = *CCG_grid_elem_mask(&reshape_level_key, ccg_grid, x, y);
|
||||
}
|
||||
/* NOTE: The sculpt mode might have SubdivCCG's data out of sync from what is stored in
|
||||
* the original object. This happens upon the following scenario:
|
||||
*
|
||||
* - User enters sculpt mode of the default cube object.
|
||||
* - Sculpt mode creates new `layer`
|
||||
* - User does some strokes.
|
||||
* - User used undo until sculpt mode is exited.
|
||||
*
|
||||
* In an ideal world the sculpt mode will take care of keeping CustomData and CCG layers in
|
||||
* sync by doing proper pushes to a local sculpt undo stack.
|
||||
*
|
||||
* Since the proper solution needs time to be implemented, consider the target object
|
||||
* the source of truth of which data layers are to be updated during reshape. This means,
|
||||
* for example, that if the undo system says object does not have paint mask layer, it is
|
||||
* not to be updated.
|
||||
*
|
||||
* This is a fragile logic, and is only working correctly because the code path is only
|
||||
* used by sculpt changes. In other usecases the code might not catch inconsistency and
|
||||
* silently do wrong decision. */
|
||||
/* NOTE: There is a known bug in Undo code that results in first Sculpt step
|
||||
* after a Memfile one to never be undone (see T83806). This might be the root cause of
|
||||
* this inconsistency. */
|
||||
if (reshape_level_key.has_mask && grid_element.mask != NULL) {
|
||||
*grid_element.mask = *CCG_grid_elem_mask(&reshape_level_key, ccg_grid, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -342,9 +342,10 @@ static bool undosys_stack_push_main(UndoStack *ustack, const char *name, struct
|
|||
CLOG_INFO(&LOG, 1, "'%s'", name);
|
||||
bContext *C_temp = CTX_create();
|
||||
CTX_data_main_set(C_temp, bmain);
|
||||
bool ok = BKE_undosys_step_push_with_type(ustack, C_temp, name, BKE_UNDOSYS_TYPE_MEMFILE);
|
||||
UndoPushReturn ret = BKE_undosys_step_push_with_type(
|
||||
ustack, C_temp, name, BKE_UNDOSYS_TYPE_MEMFILE);
|
||||
CTX_free(C_temp);
|
||||
return ok;
|
||||
return (ret & UNDO_PUSH_RET_SUCCESS);
|
||||
}
|
||||
|
||||
void BKE_undosys_stack_init_from_main(UndoStack *ustack, struct Main *bmain)
|
||||
|
@ -495,18 +496,21 @@ UndoStep *BKE_undosys_step_push_init(UndoStack *ustack, bContext *C, const char
|
|||
/**
|
||||
* \param C: Can be NULL from some callers if their encoding function doesn't need it
|
||||
*/
|
||||
bool BKE_undosys_step_push_with_type(UndoStack *ustack,
|
||||
bContext *C,
|
||||
const char *name,
|
||||
const UndoType *ut)
|
||||
UndoPushReturn BKE_undosys_step_push_with_type(UndoStack *ustack,
|
||||
bContext *C,
|
||||
const char *name,
|
||||
const UndoType *ut)
|
||||
{
|
||||
UNDO_NESTED_ASSERT(false);
|
||||
undosys_stack_validate(ustack, false);
|
||||
bool is_not_empty = ustack->step_active != NULL;
|
||||
UndoPushReturn retval = UNDO_PUSH_RET_FAILURE;
|
||||
|
||||
/* Might not be final place for this to be called - probably only want to call it from some
|
||||
* undo handlers, not all of them? */
|
||||
BKE_lib_override_library_main_operations_create(G_MAIN, false);
|
||||
if (BKE_lib_override_library_main_operations_create(G_MAIN, false)) {
|
||||
retval |= UNDO_PUSH_RET_OVERRIDE_CHANGED;
|
||||
}
|
||||
|
||||
/* Remove all undos after (also when 'ustack->step_active == NULL'). */
|
||||
while (ustack->steps.last != ustack->step_active) {
|
||||
|
@ -558,7 +562,7 @@ bool BKE_undosys_step_push_with_type(UndoStack *ustack,
|
|||
if (!undosys_step_encode(C, G_MAIN, ustack, us)) {
|
||||
MEM_freeN(us);
|
||||
undosys_stack_validate(ustack, true);
|
||||
return false;
|
||||
return retval;
|
||||
}
|
||||
ustack->step_active = us;
|
||||
BLI_addtail(&ustack->steps, us);
|
||||
|
@ -589,10 +593,10 @@ bool BKE_undosys_step_push_with_type(UndoStack *ustack,
|
|||
}
|
||||
|
||||
undosys_stack_validate(ustack, true);
|
||||
return true;
|
||||
return (retval | UNDO_PUSH_RET_SUCCESS);
|
||||
}
|
||||
|
||||
bool BKE_undosys_step_push(UndoStack *ustack, bContext *C, const char *name)
|
||||
UndoPushReturn BKE_undosys_step_push(UndoStack *ustack, bContext *C, const char *name)
|
||||
{
|
||||
UNDO_NESTED_ASSERT(false);
|
||||
const UndoType *ut = ustack->step_init ? ustack->step_init->type :
|
||||
|
|
|
@ -3421,7 +3421,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
|
|||
}
|
||||
case SPACE_OUTLINER: {
|
||||
SpaceOutliner *space_outliner = (SpaceOutliner *)sl;
|
||||
space_outliner->filter &= ~(SO_FILTER_UNUSED_1 | SO_FILTER_UNUSED_5 |
|
||||
space_outliner->filter &= ~(SO_FILTER_CLEARED_1 | SO_FILTER_UNUSED_5 |
|
||||
SO_FILTER_OB_STATE_SELECTABLE);
|
||||
space_outliner->storeflag &= ~(SO_TREESTORE_UNUSED_1);
|
||||
break;
|
||||
|
|
|
@ -229,7 +229,7 @@ enum {
|
|||
UI_BUT_VALUE_CLEAR = 1 << 30,
|
||||
|
||||
/** RNA property of the button is overridden from linked reference data. */
|
||||
UI_BUT_OVERRIDEN = 1u << 31u,
|
||||
UI_BUT_OVERRIDDEN = 1u << 31u,
|
||||
};
|
||||
|
||||
/* Default font size for normal text. */
|
||||
|
|
|
@ -1592,10 +1592,10 @@ void ui_but_override_flag(Main *bmain, uiBut *but)
|
|||
bmain, &but->rnapoin, but->rnaprop, but->rnaindex);
|
||||
|
||||
if (override_status & RNA_OVERRIDE_STATUS_OVERRIDDEN) {
|
||||
but->flag |= UI_BUT_OVERRIDEN;
|
||||
but->flag |= UI_BUT_OVERRIDDEN;
|
||||
}
|
||||
else {
|
||||
but->flag &= ~UI_BUT_OVERRIDEN;
|
||||
but->flag &= ~UI_BUT_OVERRIDDEN;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4537,7 +4537,7 @@ static uiBut *ui_def_but_rna(uiBlock *block,
|
|||
block, type, retval, str, x, y, width, height, NULL, min, max, a1, a2, tip);
|
||||
|
||||
if (but->type == UI_BTYPE_NUM) {
|
||||
/* Set default values, can be overriden later. */
|
||||
/* Set default values, can be overridden later. */
|
||||
UI_but_number_step_size_set(but, a1);
|
||||
UI_but_number_precision_set(but, a2);
|
||||
}
|
||||
|
|
|
@ -169,7 +169,7 @@ void ui_but_anim_decorate_update_from_flag(uiButDecorator *decorator_but)
|
|||
else if (flag & UI_BUT_ANIMATED) {
|
||||
but->icon = ICON_DECORATE_ANIMATE;
|
||||
}
|
||||
else if (flag & UI_BUT_OVERRIDEN) {
|
||||
else if (flag & UI_BUT_OVERRIDDEN) {
|
||||
but->icon = ICON_DECORATE_OVERRIDE;
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -784,7 +784,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but)
|
|||
/* Override Operators */
|
||||
uiItemS(layout);
|
||||
|
||||
if (but->flag & UI_BUT_OVERRIDEN) {
|
||||
if (but->flag & UI_BUT_OVERRIDDEN) {
|
||||
if (is_array_component) {
|
||||
#if 0 /* Disabled for now. */
|
||||
ot = WM_operatortype_find("UI_OT_override_type_set_button", false);
|
||||
|
|
|
@ -2540,7 +2540,7 @@ static const uchar *widget_color_blend_from_flags(const uiWidgetStateColors *wco
|
|||
if (state & UI_BUT_DRIVEN) {
|
||||
return wcol_state->inner_driven_sel;
|
||||
}
|
||||
if (state & UI_BUT_OVERRIDEN) {
|
||||
if (state & UI_BUT_OVERRIDDEN) {
|
||||
return wcol_state->inner_overridden_sel;
|
||||
}
|
||||
return NULL;
|
||||
|
@ -3824,7 +3824,7 @@ static void widget_swatch(
|
|||
|
||||
ui_but_v3_get(but, col);
|
||||
|
||||
if ((state & (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | UI_BUT_OVERRIDEN |
|
||||
if ((state & (UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN | UI_BUT_OVERRIDDEN |
|
||||
UI_BUT_REDALERT)) ||
|
||||
(but->drawflag & UI_BUT_ANIMATED_CHANGED)) {
|
||||
/* draw based on state - color for keyed etc */
|
||||
|
|
|
@ -3611,7 +3611,7 @@ static int spacedata_cleanup_exec(bContext *C, wmOperator *op)
|
|||
static void SCREEN_OT_spacedata_cleanup(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Cleanup Space Data";
|
||||
ot->name = "Clean Up Space Data";
|
||||
ot->description = "Remove unused settings for invisible editors";
|
||||
ot->idname = "SCREEN_OT_spacedata_cleanup";
|
||||
|
||||
|
|
|
@ -493,7 +493,7 @@ static bool parent_clear_poll(bContext *C,
|
|||
case ID_OB:
|
||||
return ELEM(tselem->type, TSE_MODIFIER_BASE, TSE_CONSTRAINT_BASE);
|
||||
case ID_GR:
|
||||
return event->shift;
|
||||
return event->shift || ELEM(tselem->type, TSE_LIBRARY_OVERRIDE_BASE);
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -2180,6 +2180,10 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
|
|||
data.icon = ICON_MODIFIER_DATA;
|
||||
data.drag_id = tselem->id;
|
||||
break;
|
||||
case TSE_LIBRARY_OVERRIDE_BASE:
|
||||
case TSE_LIBRARY_OVERRIDE:
|
||||
data.icon = ICON_LIBRARY_DATA_OVERRIDE;
|
||||
break;
|
||||
case TSE_LINKED_OB:
|
||||
data.icon = ICON_OBJECT_DATA;
|
||||
break;
|
||||
|
|
|
@ -331,7 +331,8 @@ static void do_item_rename(ARegion *region,
|
|||
TSE_POSEGRP_BASE,
|
||||
TSE_R_LAYER_BASE,
|
||||
TSE_SCENE_COLLECTION_BASE,
|
||||
TSE_VIEW_COLLECTION_BASE)) {
|
||||
TSE_VIEW_COLLECTION_BASE,
|
||||
TSE_LIBRARY_OVERRIDE_BASE)) {
|
||||
BKE_report(reports, RPT_WARNING, "Cannot edit builtin name");
|
||||
}
|
||||
else if (ELEM(tselem->type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP)) {
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
#include "BKE_idtype.h"
|
||||
#include "BKE_layer.h"
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_lib_override.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_modifier.h"
|
||||
#include "BKE_outliner_treehash.h"
|
||||
|
@ -640,6 +641,30 @@ static void outliner_add_object_contents(SpaceOutliner *space_outliner,
|
|||
}
|
||||
}
|
||||
|
||||
static void outliner_add_library_override_contents(SpaceOutliner *soops, TreeElement *te, ID *id)
|
||||
{
|
||||
if (!id->override_library) {
|
||||
return;
|
||||
}
|
||||
|
||||
PointerRNA idpoin;
|
||||
RNA_id_pointer_create(id, &idpoin);
|
||||
|
||||
PointerRNA override_ptr;
|
||||
PropertyRNA *override_prop;
|
||||
int index = 0;
|
||||
LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) {
|
||||
if (!BKE_lib_override_rna_property_find(&idpoin, op, &override_ptr, &override_prop)) {
|
||||
BLI_assert(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
TreeElement *ten = outliner_add_element(
|
||||
soops, &te->subtree, id, te, TSE_LIBRARY_OVERRIDE, index++);
|
||||
ten->name = RNA_property_ui_name(override_prop);
|
||||
}
|
||||
}
|
||||
|
||||
/* Can be inlined if necessary. */
|
||||
static void outliner_add_id_contents(SpaceOutliner *space_outliner,
|
||||
TreeElement *te,
|
||||
|
@ -903,6 +928,17 @@ static void outliner_add_id_contents(SpaceOutliner *space_outliner,
|
|||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
const bool lib_overrides_visible = !SUPPORT_FILTER_OUTLINER(space_outliner) ||
|
||||
((space_outliner->filter & SO_FILTER_NO_LIB_OVERRIDE) == 0);
|
||||
|
||||
if (lib_overrides_visible && ID_IS_OVERRIDE_LIBRARY(id)) {
|
||||
TreeElement *ten = outliner_add_element(
|
||||
space_outliner, &te->subtree, id, te, TSE_LIBRARY_OVERRIDE_BASE, 0);
|
||||
|
||||
ten->name = IFACE_("Library Overrides");
|
||||
outliner_add_library_override_contents(space_outliner, ten, id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -112,6 +112,13 @@ static void outliner_main_region_listener(wmWindow *UNUSED(win),
|
|||
|
||||
/* context changes */
|
||||
switch (wmn->category) {
|
||||
case NC_WM:
|
||||
switch (wmn->data) {
|
||||
case ND_LIB_OVERRIDE_CHANGED:
|
||||
ED_region_tag_redraw(region);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case NC_SCENE:
|
||||
switch (wmn->data) {
|
||||
case ND_OB_ACTIVE:
|
||||
|
@ -152,8 +159,6 @@ static void outliner_main_region_listener(wmWindow *UNUSED(win),
|
|||
case NC_OBJECT:
|
||||
switch (wmn->data) {
|
||||
case ND_TRANSFORM:
|
||||
/* transform doesn't change outliner data */
|
||||
break;
|
||||
case ND_BONE_ACTIVE:
|
||||
case ND_BONE_SELECT:
|
||||
case ND_DRAW:
|
||||
|
|
|
@ -145,12 +145,14 @@ void ED_undo_push(bContext *C, const char *str)
|
|||
}
|
||||
}
|
||||
|
||||
UndoPushReturn push_retval;
|
||||
|
||||
/* Only apply limit if this is the last undo step. */
|
||||
if (wm->undo_stack->step_active && (wm->undo_stack->step_active->next == NULL)) {
|
||||
BKE_undosys_stack_limit_steps_and_memory(wm->undo_stack, steps - 1, 0);
|
||||
}
|
||||
|
||||
BKE_undosys_step_push(wm->undo_stack, C, str);
|
||||
push_retval = BKE_undosys_step_push(wm->undo_stack, C, str);
|
||||
|
||||
if (U.undomemory != 0) {
|
||||
const size_t memory_limit = (size_t)U.undomemory * 1024 * 1024;
|
||||
|
@ -160,6 +162,10 @@ void ED_undo_push(bContext *C, const char *str)
|
|||
if (CLOG_CHECK(&LOG, 1)) {
|
||||
BKE_undosys_print(wm->undo_stack);
|
||||
}
|
||||
|
||||
if (push_retval & UNDO_PUSH_RET_OVERRIDE_CHANGED) {
|
||||
WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -117,6 +117,8 @@ enum {
|
|||
#define TSE_SCENE_OBJECTS_BASE 41
|
||||
#define TSE_GPENCIL_EFFECT_BASE 42
|
||||
#define TSE_GPENCIL_EFFECT 43
|
||||
#define TSE_LIBRARY_OVERRIDE_BASE 44
|
||||
#define TSE_LIBRARY_OVERRIDE 45
|
||||
|
||||
/* Check whether given TreeStoreElem should have a real ID in its ->id member. */
|
||||
#define TSE_IS_REAL_ID(_tse) \
|
||||
|
|
|
@ -305,8 +305,9 @@ typedef enum eSpaceOutliner_Flag {
|
|||
|
||||
/* SpaceOutliner.filter */
|
||||
typedef enum eSpaceOutliner_Filter {
|
||||
SO_FILTER_SEARCH = (1 << 0), /* Run-time flag. */
|
||||
SO_FILTER_UNUSED_1 = (1 << 1), /* cleared */
|
||||
SO_FILTER_SEARCH = (1 << 0), /* Run-time flag. */
|
||||
SO_FILTER_CLEARED_1 = (1 << 1),
|
||||
SO_FILTER_NO_LIB_OVERRIDE = SO_FILTER_CLEARED_1, /* re-use */
|
||||
SO_FILTER_NO_OBJECT = (1 << 2),
|
||||
SO_FILTER_NO_OB_CONTENT = (1 << 3), /* Not only mesh, but modifiers, constraints, ... */
|
||||
SO_FILTER_NO_CHILDREN = (1 << 4),
|
||||
|
@ -339,7 +340,7 @@ typedef enum eSpaceOutliner_Filter {
|
|||
|
||||
#define SO_FILTER_ANY \
|
||||
(SO_FILTER_NO_OB_CONTENT | SO_FILTER_NO_CHILDREN | SO_FILTER_OB_TYPE | SO_FILTER_OB_STATE | \
|
||||
SO_FILTER_NO_COLLECTION)
|
||||
SO_FILTER_NO_COLLECTION | SO_FILTER_NO_LIB_OVERRIDE)
|
||||
|
||||
/* SpaceOutliner.filter_state */
|
||||
typedef enum eSpaceOutliner_StateFilter {
|
||||
|
|
|
@ -60,7 +60,7 @@ const EnumPropertyItem rna_enum_fmodifier_type_items[] = {
|
|||
"Envelope",
|
||||
"Reshape F-Curve values, e.g. change amplitude of movements"},
|
||||
{FMODIFIER_TYPE_CYCLES, "CYCLES", 0, "Cycles", "Cyclic extend/repeat keyframe sequence"},
|
||||
{FMODIFIER_TYPE_NOISE, "NOISE", 0, "Noise", "Add pseudorandom noise on top of F-Curves"},
|
||||
{FMODIFIER_TYPE_NOISE, "NOISE", 0, "Noise", "Add pseudo-random noise on top of F-Curves"},
|
||||
/*{FMODIFIER_TYPE_FILTER, "FILTER", 0, "Filter", ""},*/ /* FIXME: not implemented yet! */
|
||||
/*{FMODIFIER_TYPE_PYTHON, "PYTHON", 0, "Python", ""},*/ /* FIXME: not implemented yet! */
|
||||
{FMODIFIER_TYPE_LIMITS,
|
||||
|
|
|
@ -3442,6 +3442,13 @@ static void rna_def_space_outliner(BlenderRNA *brna)
|
|||
RNA_def_property_enum_items(prop, rna_enum_id_type_items);
|
||||
RNA_def_property_ui_text(prop, "Filter by Type", "Data-block type to show");
|
||||
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID);
|
||||
|
||||
prop = RNA_def_property(srna, "use_filter_lib_override", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_negative_sdna(prop, NULL, "filter", SO_FILTER_NO_LIB_OVERRIDE);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Show Library Overrides",
|
||||
"For libraries with overrides created, show the overridden values");
|
||||
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_OUTLINER, NULL);
|
||||
}
|
||||
|
||||
static void rna_def_space_view3d_shading(BlenderRNA *brna)
|
||||
|
|
|
@ -1448,7 +1448,7 @@ static void rna_def_panel(BlenderRNA *brna)
|
|||
RNA_def_property_string_sdna(prop, NULL, "type->parent_id");
|
||||
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Parent ID Name", "If this is set, the panel becomes a subpanel");
|
||||
prop, "Parent ID Name", "If this is set, the panel becomes a sub-panel");
|
||||
|
||||
prop = RNA_def_property(srna, "bl_ui_units_x", PROP_INT, PROP_UNSIGNED);
|
||||
RNA_def_property_int_sdna(prop, NULL, "type->ui_units_x");
|
||||
|
|
|
@ -2455,7 +2455,7 @@ static void rna_def_userdef_theme_space_graph(BlenderRNA *brna)
|
|||
prop = RNA_def_property(srna, "dopesheet_subchannel", PROP_FLOAT, PROP_COLOR_GAMMA);
|
||||
RNA_def_property_float_sdna(prop, NULL, "ds_subchannel");
|
||||
RNA_def_property_array(prop, 3);
|
||||
RNA_def_property_ui_text(prop, "Dope Sheet Subchannel", "");
|
||||
RNA_def_property_ui_text(prop, "Dope Sheet Sub-channel", "");
|
||||
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
|
||||
|
||||
prop = RNA_def_property(srna, "channel_group", PROP_FLOAT, PROP_COLOR_GAMMA);
|
||||
|
@ -3334,7 +3334,7 @@ static void rna_def_userdef_theme_space_action(BlenderRNA *brna)
|
|||
prop = RNA_def_property(srna, "dopesheet_subchannel", PROP_FLOAT, PROP_COLOR_GAMMA);
|
||||
RNA_def_property_float_sdna(prop, NULL, "ds_subchannel");
|
||||
RNA_def_property_array(prop, 4);
|
||||
RNA_def_property_ui_text(prop, "Dope Sheet Subchannel", "");
|
||||
RNA_def_property_ui_text(prop, "Dope Sheet Sub-channel", "");
|
||||
RNA_def_property_update(prop, 0, "rna_userdef_theme_update");
|
||||
|
||||
prop = RNA_def_property(srna, "channels", PROP_FLOAT, PROP_COLOR_GAMMA);
|
||||
|
|
|
@ -82,40 +82,42 @@ static void get_instanced_data__collection(
|
|||
bke::PersistentCollectionHandle collection_handle =
|
||||
params.get_input<bke::PersistentCollectionHandle>("Collection");
|
||||
Collection *collection = params.handle_map().lookup(collection_handle);
|
||||
if (collection != nullptr) {
|
||||
const bool use_whole_collection = node.custom2 == 0;
|
||||
if (use_whole_collection) {
|
||||
if (collection == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool use_whole_collection = node.custom2 == 0;
|
||||
if (use_whole_collection) {
|
||||
InstancedData instance;
|
||||
instance.type = INSTANCE_DATA_TYPE_COLLECTION;
|
||||
instance.data.collection = collection;
|
||||
r_instances_data.fill(instance);
|
||||
}
|
||||
else {
|
||||
Vector<InstancedData> possible_instances;
|
||||
/* Direct child objects are instanced as objects. */
|
||||
LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) {
|
||||
Object *object = cob->ob;
|
||||
InstancedData instance;
|
||||
instance.type = INSTANCE_DATA_TYPE_OBJECT;
|
||||
instance.data.object = object;
|
||||
possible_instances.append(instance);
|
||||
}
|
||||
/* Direct child collections are instanced as collections. */
|
||||
LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
|
||||
Collection *child_collection = child->collection;
|
||||
InstancedData instance;
|
||||
instance.type = INSTANCE_DATA_TYPE_COLLECTION;
|
||||
instance.data.collection = collection;
|
||||
r_instances_data.fill(instance);
|
||||
instance.data.collection = child_collection;
|
||||
possible_instances.append(instance);
|
||||
}
|
||||
else {
|
||||
Vector<InstancedData> possible_instances;
|
||||
/* Direct child objects are instanced as objects. */
|
||||
LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) {
|
||||
Object *object = cob->ob;
|
||||
InstancedData instance;
|
||||
instance.type = INSTANCE_DATA_TYPE_OBJECT;
|
||||
instance.data.object = object;
|
||||
possible_instances.append(instance);
|
||||
}
|
||||
/* Direct child collections are instanced as collections. */
|
||||
LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
|
||||
Collection *child_collection = child->collection;
|
||||
InstancedData instance;
|
||||
instance.type = INSTANCE_DATA_TYPE_COLLECTION;
|
||||
instance.data.collection = child_collection;
|
||||
possible_instances.append(instance);
|
||||
}
|
||||
|
||||
if (!possible_instances.is_empty()) {
|
||||
const int seed = params.get_input<int>("Seed");
|
||||
Array<uint32_t> ids = get_geometry_element_ids_as_uints(component, ATTR_DOMAIN_POINT);
|
||||
for (const int i : r_instances_data.index_range()) {
|
||||
const int index = BLI_hash_int_2d(ids[i], seed) % possible_instances.size();
|
||||
r_instances_data[i] = possible_instances[index];
|
||||
}
|
||||
if (!possible_instances.is_empty()) {
|
||||
const int seed = params.get_input<int>("Seed");
|
||||
Array<uint32_t> ids = get_geometry_element_ids_as_uints(component, ATTR_DOMAIN_POINT);
|
||||
for (const int i : r_instances_data.index_range()) {
|
||||
const int index = BLI_hash_int_2d(ids[i], seed) % possible_instances.size();
|
||||
r_instances_data[i] = possible_instances[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -312,6 +312,7 @@ typedef struct wmNotifier {
|
|||
#define ND_JOB (5 << 16)
|
||||
#define ND_UNDO (6 << 16)
|
||||
#define ND_XR_DATA_CHANGED (7 << 16)
|
||||
#define ND_LIB_OVERRIDE_CHANGED (8 << 16)
|
||||
|
||||
/* NC_SCREEN */
|
||||
#define ND_LAYOUTBROWSE (1 << 16)
|
||||
|
|
|
@ -2263,7 +2263,7 @@ static int wm_handler_fileselect_do(bContext *C,
|
|||
continue;
|
||||
}
|
||||
|
||||
if (ctx_area->full) {
|
||||
if (file_area->full) {
|
||||
/* Users should not be able to maximize/fullscreen an area in a temporary screen. So if
|
||||
* there's a maximized file browser in a temporary screen, it was likely opened by
|
||||
* #EVT_FILESELECT_FULL_OPEN. */
|
||||
|
|
Loading…
Reference in New Issue