Blender V5.0
ed_util_ops.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include <cstring>
12
13#include "DNA_space_types.h"
15
16#include "BLI_fileops.h"
17#include "BLI_utildefines.h"
18#include "BLI_vector.hh"
19
20#include "BKE_context.hh"
21#include "BKE_lib_id.hh"
22#include "BKE_lib_override.hh"
23#include "BKE_library.hh"
24#include "BKE_preview_image.hh"
25#include "BKE_report.hh"
26
27#include "ED_asset.hh"
28#include "ED_render.hh"
29#include "ED_undo.hh"
30#include "ED_util.hh"
31
32#include "RNA_access.hh"
33#include "RNA_prototypes.hh"
34
35#include "UI_interface.hh"
36
37#include "WM_api.hh"
38#include "WM_types.hh"
39
40/* -------------------------------------------------------------------- */
43
45{
47 PointerRNA idptr = CTX_data_pointer_get_type(C, "id", &RNA_ID);
48 if (idptr.data) {
49 ids.append(idptr);
50 }
51 return ids;
52}
53
55{
57
58 /* "selected_ids" context member. */
60 if (!ids.is_empty()) {
61 return ids;
62 }
63
64 /* "id" context member. */
66}
67
69
70/* -------------------------------------------------------------------- */
73
74static bool lib_id_preview_editing_poll_ex(const ID *id, const char **r_disabled_hint)
75{
76 if (!id) {
77 return false;
78 }
79 if (!ID_IS_EDITABLE(id)) {
80 if (r_disabled_hint) {
81 *r_disabled_hint = "Cannot edit external library data";
82 }
83 return false;
84 }
85 if (ID_IS_OVERRIDE_LIBRARY(id)) {
86 if (r_disabled_hint) {
87 *r_disabled_hint = "Cannot edit previews of overridden library data";
88 }
89 return false;
90 }
91 if (!BKE_previewimg_id_get_p(id)) {
92 if (r_disabled_hint) {
93 *r_disabled_hint = "Data-block does not support previews";
94 }
95 return false;
96 }
97
98 return true;
99}
100
102{
103 const PointerRNA idptr = CTX_data_pointer_get(C, "id");
104 BLI_assert(!idptr.data || RNA_struct_is_ID(idptr.type));
105
106 const ID *id = (ID *)idptr.data;
107 const char *disabled_hint = nullptr;
108 if (!lib_id_preview_editing_poll_ex(id, &disabled_hint)) {
109 CTX_wm_operator_poll_msg_set(C, disabled_hint);
110 return false;
111 }
112
113 return true;
114}
115
117{
118 /* #invoke() gets the ID from context and saves it in the custom data. */
119 if (op->customdata) {
120 return static_cast<ID *>(op->customdata);
121 }
122
123 PointerRNA idptr = CTX_data_pointer_get(C, "id");
124 return static_cast<ID *>(idptr.data);
125}
126
128{
129 char filepath[FILE_MAX];
130
131 RNA_string_get(op->ptr, "filepath", filepath);
132
133 if (!BLI_is_file(filepath)) {
134 BKE_reportf(op->reports, RPT_ERROR, "File not found '%s'", filepath);
135 return OPERATOR_CANCELLED;
136 }
137
139 if (!id) {
141 op->reports, RPT_ERROR, "Failed to set preview: no ID in context (incorrect context?)");
142 return OPERATOR_CANCELLED;
143 }
144
145 BKE_previewimg_id_custom_set(id, filepath);
146
148
149 return OPERATOR_FINISHED;
150}
151
159 wmOperator *op,
160 const wmEvent *event)
161{
163 return WM_operator_filesel(C, op, event);
164}
165
167{
168 ot->name = "Load Custom Preview";
169 ot->description = "Choose an image to help identify the data-block visually";
170 ot->idname = "ED_OT_lib_id_load_custom_preview";
171
172 /* API callbacks. */
176
177 /* flags */
179
187}
188
194{
196 for (PointerRNA &idptr : id_pointers) {
197 ID *id = static_cast<ID *>(idptr.data);
198
199 if (lib_id_preview_editing_poll_ex(id, nullptr)) {
200 foreach_id(id);
201 }
202 }
203}
204
215 bContext *C,
216 blender::FunctionRef<bool(const ID *, const char **r_disabled_hint)> additional_condition =
217 nullptr)
218{
220 if (id_pointers.is_empty()) {
221 CTX_wm_operator_poll_msg_set(C, "No data-block selected or active");
222 return false;
223 }
224
225 const char *disabled_hint = nullptr;
226
227 for (const PointerRNA &idptr : id_pointers) {
228 const ID *id = static_cast<const ID *>(idptr.data);
229
230 const char *iter_disabled_hint = nullptr;
231 if (lib_id_preview_editing_poll_ex(id, &iter_disabled_hint) &&
232 (!additional_condition || additional_condition(id, &iter_disabled_hint)))
233 {
234 /* Operator can run if there's at least one ID supporting previews. */
235 return true;
236 }
237
238 if (iter_disabled_hint && !disabled_hint) {
239 disabled_hint = iter_disabled_hint;
240 }
241 }
242
243 /* Will only hold the first disabled hint set. That often gives some more specific information,
244 * so it's more useful than a generic message. */
245 if (disabled_hint) {
246 CTX_wm_operator_poll_msg_set(C, disabled_hint);
247 }
248 else {
249 CTX_wm_operator_poll_msg_set(C, "None of the selected data-blocks supports previews");
250 }
251 return false;
252}
253
255{
256 return lib_id_batch_editing_preview_poll(C, [](const ID *id, const char **r_disabled_hint) {
257 return ED_preview_id_is_supported(id, r_disabled_hint);
258 });
259}
260
262{
263 using namespace blender::ed;
264
266
268 if (ED_preview_id_is_supported(id, nullptr)) {
269 PreviewImage *preview = BKE_previewimg_id_get(id);
270
271 if (preview) {
272 BKE_previewimg_clear(preview);
273 }
274
275 UI_icon_render_id(C, nullptr, id, ICON_SIZE_PREVIEW, true);
276 }
277 });
278
281
282 return OPERATOR_FINISHED;
283}
284
286{
287 ot->name = "Generate Preview";
288 ot->description = "Create an automatic preview for the selected data-block";
289 ot->idname = "ED_OT_lib_id_generate_preview";
290
291 /* API callbacks. */
294
295 /* flags */
297}
298
300{
301 /* This already checks if the IDs in context (e.g. selected in the Asset browser) can generate
302 * previews... */
304 return false;
305 }
306
307 /* ... but we also need to check this for the active object (since this is what is being
308 * rendered). */
309 Object *object_to_render = CTX_data_active_object(C);
310 if (object_to_render == nullptr) {
311 return false;
312 }
313 const char *disabled_hint = nullptr;
314 if (!ED_preview_id_is_supported(&object_to_render->id, &disabled_hint)) {
315 CTX_wm_operator_poll_msg_set(C, disabled_hint);
316 return false;
317 }
318
319 return true;
320}
321
323{
324 using namespace blender::ed;
325
327
328 Object *object_to_render = CTX_data_active_object(C);
329
332
333 PreviewImage *preview_image = BKE_previewimg_id_ensure(id);
335 C, nullptr, &object_to_render->id, ICON_SIZE_PREVIEW, true, preview_image);
336 });
337
340
341 return OPERATOR_FINISHED;
342}
343
345{
346 ot->name = "Generate Preview from Object";
347 ot->description = "Create a preview for this asset by rendering the active object";
348 ot->idname = "ED_OT_lib_id_generate_preview_from_object";
349
350 /* API callbacks. */
353
354 /* flags */
356}
357
359{
361 return false;
362 }
363
364 bool has_any_removable = false;
366 if (BKE_previewimg_id_get(id)) {
367 has_any_removable = true;
368 }
369 });
370
371 if (!has_any_removable) {
372 CTX_wm_operator_poll_msg_set(C, "No preview available to remove");
373 return false;
374 }
375
376 return true;
377}
378
387
389{
390 /* identifiers */
391 ot->name = "Remove Preview";
392 ot->description = "Remove the preview of this data-block";
393 ot->idname = "ED_OT_lib_id_remove_preview";
394
395 /* API callbacks. */
398
399 /* flags */
401}
402
404
405/* -------------------------------------------------------------------- */
408
410{
411 PropertyPointerRNA pprop;
413
415
416 if (pprop.prop) {
417 idptr = RNA_property_pointer_get(&pprop.ptr, pprop.prop);
418 }
419
420 if ((pprop.prop == nullptr) || RNA_pointer_is_null(&idptr) || !RNA_struct_is_ID(idptr.type)) {
422 op->reports, RPT_ERROR, "Incorrect context for running data-block fake user toggling");
423 return OPERATOR_CANCELLED;
424 }
425
426 ID *id = (ID *)idptr.data;
427
430 {
431 BKE_report(op->reports, RPT_ERROR, "Data-block type does not support fake user");
432 return OPERATOR_CANCELLED;
433 }
434
435 if (ID_FAKE_USERS(id)) {
437 }
438 else {
440 }
441
442 return OPERATOR_FINISHED;
443}
444
446{
447 /* identifiers */
448 ot->name = "Toggle Fake User";
449 ot->description = "Save this data-block even if it has no users";
450 ot->idname = "ED_OT_lib_id_fake_user_toggle";
451
452 /* API callbacks. */
454
455 /* flags */
457}
458
460{
461 PropertyPointerRNA pprop;
462 PointerRNA idptr;
463
465
466 if (pprop.prop) {
467 idptr = RNA_property_pointer_get(&pprop.ptr, pprop.prop);
468 }
469
470 if ((pprop.prop == nullptr) || RNA_pointer_is_null(&idptr) || !RNA_struct_is_ID(idptr.type)) {
472 op->reports, RPT_ERROR, "Incorrect context for running data-block fake user toggling");
473 return OPERATOR_CANCELLED;
474 }
475
476 idptr = {};
477 RNA_property_pointer_set(&pprop.ptr, pprop.prop, idptr, nullptr);
478 RNA_property_update(C, &pprop.ptr, pprop.prop);
479
480 return OPERATOR_FINISHED;
481}
482
484{
485 /* identifiers */
486 ot->name = "Unlink Data-Block";
487 ot->description = "Remove a usage of a data-block, clearing the assignment";
488 ot->idname = "ED_OT_lib_id_unlink";
489
490 /* API callbacks. */
491 ot->exec = lib_id_unlink_exec;
492
493 /* flags */
495}
496
498{
499 const PointerRNA id_ptr = CTX_data_pointer_get_type(C, "id", &RNA_ID);
500 const ID *id = static_cast<ID *>(id_ptr.data);
501
502 return id && ID_IS_OVERRIDE_LIBRARY_REAL(id) && !ID_IS_LINKED(id);
503}
504
506{
507 Main *bmain = CTX_data_main(C);
508 const PointerRNA id_ptr = CTX_data_pointer_get_type(C, "id", &RNA_ID);
509 ID *id = static_cast<ID *>(id_ptr.data);
510
511 const bool is_system_override = BKE_lib_override_library_is_system_defined(bmain, id);
512 if (is_system_override) {
513 /* A system override is not editable. Make it an editable (non-system-defined) one. */
514 id->override_library->flag &= ~LIBOVERRIDE_FLAG_SYSTEM_DEFINED;
515 }
516 else {
517 /* Reset override, which makes it non-editable (i.e. a system define override). */
518 BKE_lib_override_library_id_reset(bmain, id, true);
519
522 }
523
525
526 return OPERATOR_FINISHED;
527}
528
530{
531 /* identifiers */
532 ot->name = "Toggle Library Override Editable";
533 ot->description = "Set if this library override data-block can be edited";
534 ot->idname = "ED_OT_lib_id_override_editable_toggle";
535
536 /* API callbacks. */
539
540 /* flags */
542}
543
545
546/* -------------------------------------------------------------------- */
549
551{
552 Main *bmain = CTX_data_main(C);
554 return OPERATOR_FINISHED;
555}
556
558{
559 /* identifiers */
560 ot->name = "Flush Edits";
561 ot->description = "Flush edit data from active editing modes";
562 ot->idname = "ED_OT_flush_edits";
563
564 /* API callbacks. */
565 ot->exec = ed_flush_edits_exec;
566
567 /* flags */
568 ot->flag = OPTYPE_INTERNAL;
569}
570
572
PointerRNA CTX_data_pointer_get(const bContext *C, const char *member)
PointerRNA CTX_data_pointer_get_type(const bContext *C, const char *member, StructRNA *type)
void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg)
Object * CTX_data_active_object(const bContext *C)
Main * CTX_data_main(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
bool CTX_data_selected_ids(const bContext *C, blender::Vector< PointerRNA > *list)
bool BKE_id_is_editable(const Main *bmain, const ID *id)
Definition lib_id.cc:2523
void id_fake_user_set(ID *id)
Definition lib_id.cc:396
void id_fake_user_clear(ID *id)
Definition lib_id.cc:404
bool BKE_lib_override_library_is_system_defined(const Main *bmain, const ID *id)
void BKE_lib_override_library_id_reset(Main *bmain, ID *id_root, bool do_reset_system_override)
PreviewImage * BKE_previewimg_id_get(const ID *id)
PreviewImage * BKE_previewimg_id_ensure(ID *id)
PreviewImage ** BKE_previewimg_id_get_p(const ID *id)
void BKE_previewimg_id_custom_set(ID *id, const char *filepath)
void BKE_previewimg_clear(PreviewImage *prv)
void BKE_previewimg_id_free(ID *id)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
@ RPT_ERROR
Definition BKE_report.hh:39
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#define BLI_assert(a)
Definition BLI_assert.h:46
File and directory operations.
bool BLI_is_file(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:448
#define FILE_MAX
#define ELEM(...)
#define ID_FAKE_USERS(id)
Definition DNA_ID.h:655
#define ID_IS_OVERRIDE_LIBRARY_REAL(_id)
Definition DNA_ID.h:723
#define ID_IS_LINKED(_id)
Definition DNA_ID.h:694
#define ID_IS_EDITABLE(_id)
Definition DNA_ID.h:705
#define ID_IS_OVERRIDE_LIBRARY(_id)
Definition DNA_ID.h:730
@ LIBOVERRIDE_FLAG_SYSTEM_DEFINED
Definition DNA_ID.h:363
@ ICON_SIZE_PREVIEW
@ ID_WS
@ ID_TXT
@ ID_SCE
@ ID_SCR
@ ID_GR
@ ID_OB
@ FILE_SORT_DEFAULT
@ FILE_SPECIAL
@ FILE_TYPE_FOLDER
@ FILE_TYPE_IMAGE
@ FILE_DEFAULTDISPLAY
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
bool ED_preview_id_is_supported(const ID *id, const char **r_disabled_hint=nullptr)
void ED_preview_kill_jobs(wmWindowManager *wm, Main *bmain)
void ED_OT_redo(wmOperatorType *ot)
Definition ed_undo.cc:601
void ED_OT_undo_redo(wmOperatorType *ot)
Definition ed_undo.cc:613
void ED_OT_undo_push(wmOperatorType *ot)
Definition ed_undo.cc:570
void ED_OT_undo(wmOperatorType *ot)
Definition ed_undo.cc:558
void ED_OT_undo_history(wmOperatorType *ot)
Definition ed_undo.cc:754
bool ED_editors_flush_edits(Main *bmain)
Definition ed_util.cc:336
#define C
Definition RandGen.cpp:29
void UI_context_active_but_prop_get_templateID(const bContext *C, PointerRNA *r_ptr, PropertyRNA **r_prop)
void UI_icon_render_id(const bContext *C, Scene *scene, ID *id, enum eIconSizes size, bool use_job)
void UI_icon_render_id_ex(const bContext *C, Scene *scene, ID *id_to_render, const enum eIconSizes size, const bool use_job, PreviewImage *r_preview_image)
@ WM_FILESEL_FILEPATH
Definition WM_api.hh:1124
@ FILE_OPENFILE
Definition WM_api.hh:1133
#define NC_WINDOW
Definition WM_types.hh:375
#define NC_WM
Definition WM_types.hh:374
#define ND_LIB_OVERRIDE_CHANGED
Definition WM_types.hh:419
@ OPTYPE_INTERNAL
Definition WM_types.hh:202
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define NA_EDITED
Definition WM_types.hh:584
#define NC_ASSET
Definition WM_types.hh:404
#define ND_SPACE_VIEW3D
Definition WM_types.hh:528
#define NC_SPACE
Definition WM_types.hh:392
void append(const T &value)
bool is_empty() const
static bool lib_id_generate_preview_poll(bContext *C)
static wmOperatorStatus lib_id_fake_user_toggle_exec(bContext *C, wmOperator *op)
blender::Vector< PointerRNA > ED_operator_single_id_from_context_as_vec(const bContext *C)
static wmOperatorStatus lib_id_unlink_exec(bContext *C, wmOperator *op)
void ED_operatortypes_edutils()
static wmOperatorStatus lib_id_generate_preview_exec(bContext *C, wmOperator *)
static bool lib_id_batch_editing_preview_poll(bContext *C, blender::FunctionRef< bool(const ID *, const char **r_disabled_hint)> additional_condition=nullptr)
static void ED_OT_lib_id_generate_preview(wmOperatorType *ot)
static void lib_id_batch_edit_previews(bContext *C, blender::FunctionRef< void(ID *)> foreach_id)
static void ED_OT_lib_id_unlink(wmOperatorType *ot)
static bool lib_id_remove_preview_poll(bContext *C)
static wmOperatorStatus lib_id_remove_preview_exec(bContext *C, wmOperator *)
static wmOperatorStatus ed_flush_edits_exec(bContext *C, wmOperator *)
static wmOperatorStatus lib_id_generate_preview_from_object_exec(bContext *C, wmOperator *)
static void ED_OT_lib_id_generate_preview_from_object(wmOperatorType *ot)
static void ED_OT_lib_id_remove_preview(wmOperatorType *ot)
static wmOperatorStatus lib_id_load_custom_preview_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void ED_OT_flush_edits(wmOperatorType *ot)
static bool lib_id_preview_editing_poll_ex(const ID *id, const char **r_disabled_hint)
static wmOperatorStatus lib_id_load_custom_preview_exec(bContext *C, wmOperator *op)
static ID * lib_id_load_custom_preview_id_get(bContext *C, const wmOperator *op)
static bool lib_id_generate_preview_from_object_poll(bContext *C)
static void ED_OT_lib_id_override_editable_toggle(wmOperatorType *ot)
static bool lib_id_override_editable_toggle_poll(bContext *C)
static bool lib_id_preview_editing_poll(bContext *C)
static void ED_OT_lib_id_load_custom_preview(wmOperatorType *ot)
static void ED_OT_lib_id_fake_user_toggle(wmOperatorType *ot)
static wmOperatorStatus lib_id_override_editable_toggle_exec(bContext *C, wmOperator *)
blender::Vector< PointerRNA > ED_operator_get_ids_from_context_as_vec(const bContext *C)
#define GS(x)
bool RNA_struct_is_ID(const StructRNA *type)
void RNA_property_pointer_set(PointerRNA *ptr, PropertyRNA *prop, PointerRNA ptr_value, ReportList *reports)
const PointerRNA PointerRNA_NULL
PointerRNA RNA_property_pointer_get(PointerRNA *ptr, PropertyRNA *prop)
void RNA_property_update(bContext *C, PointerRNA *ptr, PropertyRNA *prop)
std::string RNA_string_get(PointerRNA *ptr, const char *name)
bool RNA_pointer_is_null(const PointerRNA *ptr)
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
StructRNA * type
Definition RNA_types.hh:52
void * data
Definition RNA_types.hh:53
PropertyRNA * prop
Definition RNA_types.hh:146
struct ReportList * reports
struct PointerRNA * ptr
void WM_main_add_notifier(uint type, void *reference)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4237
void WM_operator_properties_filesel(wmOperatorType *ot, const int filter, const short type, const eFileSel_Action action, const eFileSel_Flag flag, const short display, const short sort)
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))
wmOperatorStatus WM_operator_filesel(bContext *C, wmOperator *op, const wmEvent *)