Collection IO: Enable file exporters to be specified on Collections #116646
|
@ -280,6 +280,9 @@ class TOPBAR_MT_file(Menu):
|
|||
|
||||
layout.menu("TOPBAR_MT_file_import", icon='IMPORT')
|
||||
layout.menu("TOPBAR_MT_file_export", icon='EXPORT')
|
||||
row = layout.row()
|
||||
row.operator("wm.collection_export_all")
|
||||
row.enabled = any(len(coll.io_handlers) > 0 for coll in bpy.data.collections)
|
||||
|
||||
layout.separator()
|
||||
|
||||
|
|
|
@ -108,6 +108,7 @@ static void collection_gobject_hash_ensure(Collection *collection);
|
|||
static void collection_gobject_hash_update_object(Collection *collection,
|
||||
Object *ob_old,
|
||||
CollectionObject *cob);
|
||||
static void collection_io_handler_copy(Collection *collection, IOHandlerData *data);
|
||||
|
||||
/** \} */
|
||||
|
||||
|
@ -159,8 +160,6 @@ static void collection_copy_data(Main *bmain, ID *id_dst, const ID *id_src, cons
|
|||
BLI_listbase_clear(&collection_dst->runtime.parents);
|
||||
collection_dst->runtime.gobject_hash = nullptr;
|
||||
|
||||
/* TODO: Copy over io_handlers */
|
||||
|
||||
LISTBASE_FOREACH (CollectionChild *, child, &collection_src->children) {
|
||||
collection_child_add(
|
||||
bmain, collection_dst, child->collection, &child->light_linking, flag, false);
|
||||
|
@ -168,6 +167,9 @@ static void collection_copy_data(Main *bmain, ID *id_dst, const ID *id_src, cons
|
|||
LISTBASE_FOREACH (CollectionObject *, cob, &collection_src->gobject) {
|
||||
collection_object_add(bmain, collection_dst, cob->ob, &cob->light_linking, flag, false);
|
||||
}
|
||||
LISTBASE_FOREACH (IOHandlerData *, data, &collection_src->io_handlers) {
|
||||
collection_io_handler_copy(collection_dst, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void collection_free_data(ID *id)
|
||||
|
@ -1375,6 +1377,22 @@ static bool collection_object_remove(
|
|||
return true;
|
||||
}
|
||||
|
||||
static void collection_io_handler_copy(Collection *collection, IOHandlerData *data)
|
||||
{
|
||||
IOHandlerData *new_data = MEM_cnew<IOHandlerData>("IOHandlerData");
|
||||
STRNCPY(new_data->fh_idname, data->fh_idname);
|
||||
new_data->export_properties = IDP_CopyProperty(data->export_properties);
|
||||
new_data->flag = data->flag;
|
||||
|
||||
/* Clear the `filepath` property. */
|
||||
IDProperty *filepath = IDP_GetPropertyFromGroup(new_data->export_properties, "filepath");
|
||||
if (filepath) {
|
||||
IDP_ClearProperty(filepath);
|
||||
}
|
||||
|
||||
BLI_addtail(&collection->io_handlers, new_data);
|
||||
}
|
||||
|
||||
bool BKE_collection_object_add_notest(Main *bmain, Collection *collection, Object *ob)
|
||||
{
|
||||
if (ob == nullptr) {
|
||||
|
|
|
@ -2337,86 +2337,6 @@ void uiTemplateModifiers(uiLayout * /*layout*/, bContext *C)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma optimize("", off)
|
||||
#endif
|
||||
static wmOperator *minimal_operator_create(wmOperatorType *ot, PointerRNA *properties)
|
||||
{
|
||||
/* Copied from #wm_operator_create.
|
||||
* Create a slimmed down operator suitable only for UI drawing. */
|
||||
wmOperator *op = MEM_cnew<wmOperator>(ot->idname);
|
||||
STRNCPY(op->idname, ot->idname);
|
||||
op->type = ot;
|
||||
|
||||
/* Initialize properties but do not assume ownership of them.
|
||||
* This "minimal" operator owns nothing. */
|
||||
op->ptr = MEM_cnew<PointerRNA>("wmOperatorPtrRNA");
|
||||
op->properties = static_cast<IDProperty *>(properties->data);
|
||||
*op->ptr = *properties;
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
static void minimal_operator_free(wmOperator *op)
|
||||
{
|
||||
MEM_freeN(op->ptr);
|
||||
MEM_freeN(op);
|
||||
}
|
||||
|
||||
static void draw_export_controls(uiLayout *layout, const char *label, int index)
|
||||
{
|
||||
uiItemL(layout, label, ICON_NONE);
|
||||
uiItemIntO(layout, "", ICON_EXPORT, "COLLECTION_OT_io_handler_export", "index", index);
|
||||
uiItemIntO(layout, "", ICON_X, "COLLECTION_OT_io_handler_remove", "index", index);
|
||||
}
|
||||
|
||||
static void draw_export_properties(
|
||||
bContext *C, uiLayout *layout, ID *id, wmOperatorType *ot, IOHandlerData *data)
|
||||
{
|
||||
PointerRNA properties = RNA_pointer_create(id, ot->srna, data->export_properties);
|
||||
|
||||
uiLayout *box = uiLayoutBox(layout);
|
||||
uiItemR(box, &properties, "filepath", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
|
||||
wmOperator *op = minimal_operator_create(ot, &properties);
|
||||
op->layout = layout;
|
||||
op->type->ui(C, op);
|
||||
op->layout = nullptr;
|
||||
minimal_operator_free(op);
|
||||
}
|
||||
|
||||
void uiTemplateCollectionExporters(uiLayout *layout, bContext *C)
|
||||
{
|
||||
Collection *collection = CTX_data_collection(C);
|
||||
ListBase *io_handlers = &collection->io_handlers;
|
||||
|
||||
/* Draw all the IO handlers. */
|
||||
int index = 0;
|
||||
LISTBASE_FOREACH_INDEX (IOHandlerData *, data, io_handlers, index) {
|
||||
using namespace blender;
|
||||
bke::FileHandlerType *fh = bke::file_handler_find(data->fh_idname);
|
||||
if (!fh) {
|
||||
continue;
|
||||
}
|
||||
|
||||
wmOperatorType *ot = WM_operatortype_find(fh->export_operator, false);
|
||||
if (!ot) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PointerRNA io_handler_ptr = RNA_pointer_create(&collection->id, &RNA_IOHandlerData, data);
|
||||
|
||||
PanelLayout panel = uiLayoutPanelProp(C, layout, &io_handler_ptr, "is_open");
|
||||
draw_export_controls(panel.header, fh->label, index);
|
||||
if (panel.body) {
|
||||
draw_export_properties(C, panel.body, &collection->id, ot, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
# pragma optimize("", on)
|
||||
#endif
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@ -3039,6 +2959,86 @@ void uiTemplateOperatorRedoProperties(uiLayout *layout, const bContext *C)
|
|||
}
|
||||
}
|
||||
|
||||
static wmOperator *minimal_operator_create(wmOperatorType *ot, PointerRNA *properties)
|
||||
{
|
||||
/* Copied from #wm_operator_create.
|
||||
* Create a slimmed down operator suitable only for UI drawing. */
|
||||
wmOperator *op = MEM_cnew<wmOperator>(ot->idname);
|
||||
STRNCPY(op->idname, ot->idname);
|
||||
op->type = ot;
|
||||
|
||||
/* Initialize properties but do not assume ownership of them.
|
||||
* This "minimal" operator owns nothing. */
|
||||
op->ptr = MEM_cnew<PointerRNA>("wmOperatorPtrRNA");
|
||||
op->properties = static_cast<IDProperty *>(properties->data);
|
||||
*op->ptr = *properties;
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
static void minimal_operator_free(wmOperator *op)
|
||||
{
|
||||
MEM_freeN(op->ptr);
|
||||
MEM_freeN(op);
|
||||
}
|
||||
|
||||
static void draw_export_controls(uiLayout *layout, const std::string &label, int index, bool valid)
|
||||
{
|
||||
uiItemL(layout, label.c_str(), ICON_NONE);
|
||||
if (valid) {
|
||||
uiItemIntO(layout, "", ICON_EXPORT, "COLLECTION_OT_io_handler_export", "index", index);
|
||||
uiItemIntO(layout, "", ICON_X, "COLLECTION_OT_io_handler_remove", "index", index);
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_export_properties(
|
||||
bContext *C, uiLayout *layout, ID *id, wmOperatorType *ot, IOHandlerData *data)
|
||||
{
|
||||
PointerRNA properties = RNA_pointer_create(id, ot->srna, data->export_properties);
|
||||
|
||||
uiLayout *box = uiLayoutBox(layout);
|
||||
uiItemR(box, &properties, "filepath", UI_ITEM_NONE, nullptr, ICON_NONE);
|
||||
|
||||
wmOperator *op = minimal_operator_create(ot, &properties);
|
||||
op->type->flag &= ~OPTYPE_PRESET; /* TODO: Presets will not work currently. */
|
||||
template_operator_property_buts_draw_single(C, op, layout, UI_BUT_LABEL_ALIGN_NONE, 0);
|
||||
minimal_operator_free(op);
|
||||
}
|
||||
|
||||
void uiTemplateCollectionExporters(uiLayout *layout, bContext *C)
|
||||
{
|
||||
Collection *collection = CTX_data_collection(C);
|
||||
ListBase *io_handlers = &collection->io_handlers;
|
||||
|
||||
/* Draw all the IO handlers. */
|
||||
int index = 0;
|
||||
LISTBASE_FOREACH_INDEX (IOHandlerData *, data, io_handlers, index) {
|
||||
using namespace blender;
|
||||
PointerRNA io_handler_ptr = RNA_pointer_create(&collection->id, &RNA_IOHandlerData, data);
|
||||
PanelLayout panel = uiLayoutPanelProp(C, layout, &io_handler_ptr, "is_open");
|
||||
|
||||
bke::FileHandlerType *fh = bke::file_handler_find(data->fh_idname);
|
||||
if (!fh) {
|
||||
std::string label = std::string(IFACE_("Undefined")) + " " + data->fh_idname;
|
||||
draw_export_controls(panel.header, label, index, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
wmOperatorType *ot = WM_operatortype_find(fh->export_operator, false);
|
||||
if (!ot) {
|
||||
std::string label = std::string(IFACE_("Undefined")) + " " + fh->export_operator;
|
||||
draw_export_controls(panel.header, label, index, false);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string label(fh->label);
|
||||
draw_export_controls(panel.header, label, index, true);
|
||||
if (panel.body) {
|
||||
draw_export_properties(C, panel.body, &collection->id, ot, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -197,8 +197,7 @@ static void ui_obj_export_settings(uiLayout *layout, PointerRNA *imfptr)
|
|||
|
||||
static void wm_obj_export_draw(bContext * /*C*/, wmOperator *op)
|
||||
{
|
||||
PointerRNA ptr = RNA_pointer_create(nullptr, op->type->srna, op->properties);
|
||||
ui_obj_export_settings(op->layout, &ptr);
|
||||
ui_obj_export_settings(op->layout, op->ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -464,7 +464,7 @@ void COLLECTION_OT_io_handler_add(wmOperatorType *ot)
|
|||
|
||||
/* api callbacks */
|
||||
deadpin marked this conversation as resolved
Outdated
|
||||
ot->exec = collection_io_handler_add_exec;
|
||||
ot->poll = ED_operator_objectmode;
|
||||
ot->poll = WM_operator_winactive;
|
||||
|
||||
deadpin marked this conversation as resolved
Bastien Montagne
commented
Missing deg tagging of the modified collection (likely just using Same below in remove code. Missing deg tagging of the modified collection (likely just using `ID_RECALC_SYNC_TO_EVAL`?).
Same below in remove code.
|
||||
/* flags */
|
||||
deadpin marked this conversation as resolved
Outdated
Brecht Van Lommel
commented
This needs a poll function that checks This needs a poll function that checks `CTX_data_collection(C) != nullptr`.
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
@ -502,7 +502,7 @@ void COLLECTION_OT_io_handler_remove(wmOperatorType *ot)
|
|||
|
||||
/* api callbacks */
|
||||
ot->exec = collection_io_handler_remove_exec;
|
||||
deadpin marked this conversation as resolved
Outdated
Brecht Van Lommel
commented
`COLLECTION_OT_exporter_remove`
|
||||
ot->poll = ED_operator_objectmode;
|
||||
ot->poll = WM_operator_winactive;
|
||||
|
||||
deadpin marked this conversation as resolved
Outdated
Bastien Montagne
commented
Most likely have no consequences here, but would rather remove the link from the listbase before starting to free its data? Most likely have no consequences here, but would rather remove the link from the listbase before starting to free its data?
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
deadpin marked this conversation as resolved
Outdated
Brecht Van Lommel
commented
Same comment about poll function. Same comment about poll function.
|
||||
|
@ -555,7 +555,7 @@ void COLLECTION_OT_io_handler_export(wmOperatorType *ot)
|
|||
|
||||
/* api callbacks */
|
||||
ot->exec = collection_io_handler_export_exec;
|
||||
ot->poll = ED_operator_objectmode;
|
||||
ot->poll = WM_operator_winactive;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
@ -563,31 +563,78 @@ void COLLECTION_OT_io_handler_export(wmOperatorType *ot)
|
|||
RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "IO Handler index", 0, INT_MAX);
|
||||
}
|
||||
|
||||
static int collection_io_export_all_exec(bContext *C, wmOperator * /*op*/)
|
||||
static int collection_export(bContext *C, Collection *collection)
|
||||
{
|
||||
Collection *collection = CTX_data_collection(C);
|
||||
ListBase *io_handlers = &collection->io_handlers;
|
||||
|
||||
LISTBASE_FOREACH (IOHandlerData *, data, io_handlers) {
|
||||
io_handler_export(C, data, collection);
|
||||
|
||||
/* TODO: Should we continue calling operators if one fails? */
|
||||
/* TODO: What's the best way to surface the problem? Reports? */
|
||||
if (io_handler_export(C, data, collection) != OPERATOR_FINISHED) {
|
||||
deadpin marked this conversation as resolved
Outdated
Brecht Van Lommel
commented
`COLLECTION_OT_export` perhaps.
|
||||
/* Do not continue calling exporters if we encounter one that fails. */
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
deadpin marked this conversation as resolved
Outdated
Brecht Van Lommel
commented
Same comment about poll. Same comment about poll.
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int collection_io_export_all_exec(bContext *C, wmOperator * /*op*/)
|
||||
deadpin marked this conversation as resolved
Outdated
Brecht Van Lommel
commented
IO Handler index -> Exporter index IO Handler index -> Exporter index
|
||||
{
|
||||
Collection *collection = CTX_data_collection(C);
|
||||
return collection_export(C, collection);
|
||||
}
|
||||
|
||||
void COLLECTION_OT_io_export_all(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Export All";
|
||||
ot->description = "Export all configured IO Handlers";
|
||||
ot->description = "Invoke all configured exporters on this collection";
|
||||
ot->idname = "COLLECTION_OT_io_export_all";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = collection_io_export_all_exec;
|
||||
ot->poll = ED_operator_objectmode;
|
||||
ot->poll = WM_operator_winactive;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
static int collection_export_recursive(bContext *C, LayerCollection *layer_collection)
|
||||
{
|
||||
if (collection_export(C, layer_collection->collection) != OPERATOR_FINISHED) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (LayerCollection *, child, &layer_collection->layer_collections) {
|
||||
if (collection_export_recursive(C, child) != OPERATOR_FINISHED) {
|
||||
deadpin marked this conversation as resolved
Outdated
Brecht Van Lommel
commented
`COLLECTION_OT_export_all`
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
deadpin marked this conversation as resolved
Outdated
Brecht Van Lommel
commented
Same comment about poll. Same comment about poll.
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int wm_collection_export_all_exec(bContext *C, wmOperator * /*op*/)
|
||||
{
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
LISTBASE_FOREACH (LayerCollection *, layer_collection, &view_layer->layer_collections) {
|
||||
if (collection_export_recursive(C, layer_collection) != OPERATOR_FINISHED) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void WM_OT_collection_export_all(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Export All Collections";
|
||||
ot->description = "Invoke all configured exporters for all collections";
|
||||
ot->idname = "WM_OT_collection_export_all";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = wm_collection_export_all_exec;
|
||||
ot->poll = WM_operator_winactive;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
@ -609,7 +656,7 @@ static void collection_io_handler_menu_draw(const bContext * /*C*/, Menu *menu)
|
|||
}
|
||||
|
||||
if (!at_least_one) {
|
||||
uiItemL(layout, "No file handlers available", ICON_NONE);
|
||||
uiItemL(layout, IFACE_("No file handlers available"), ICON_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -619,7 +666,7 @@ void collection_io_handler_register()
|
|||
{
|
||||
MenuType *mt = MEM_cnew<MenuType>(__func__);
|
||||
STRNCPY(mt->idname, "COLLECTION_MT_io_handler_add");
|
||||
STRNCPY(mt->label, N_("Add IO Handler"));
|
||||
STRNCPY(mt->label, N_("Add Exporter"));
|
||||
mt->draw = collection_io_handler_menu_draw;
|
||||
|
||||
WM_menutype_add(mt);
|
||||
|
@ -627,6 +674,7 @@ void collection_io_handler_register()
|
|||
WM_operatortype_append(COLLECTION_OT_io_handler_remove);
|
||||
WM_operatortype_append(COLLECTION_OT_io_handler_export);
|
||||
WM_operatortype_append(COLLECTION_OT_io_export_all);
|
||||
WM_operatortype_append(WM_OT_collection_export_all);
|
||||
}
|
||||
|
||||
} // namespace blender::ed::object
|
||||
|
|
Loading…
Reference in New Issue
Rename
COLLECTION_OT_io_handler_add
COLLECTION_OT_exporter_add
. I don't really see a reason to deviate from the user interface name here.