Blender V4.3
outliner_edit.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2004 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <cstring>
10#include <iostream>
11#include <ostream>
12
13#include <fmt/format.h>
14
15#include "MEM_guardedalloc.h"
16
17#include "DNA_ID.h"
18#include "DNA_anim_types.h"
20#include "DNA_object_types.h"
21#include "DNA_scene_types.h"
22
23#include "BLI_blenlib.h"
24#include "BLI_dynstr.h"
25#include "BLI_path_utils.hh"
26#include "BLI_utildefines.h"
27
28#include "BLT_translation.hh"
29
30#include "BLF_api.hh"
31
32#include "BKE_action.hh"
33#include "BKE_animsys.h"
34#include "BKE_appdir.hh"
35#include "BKE_armature.hh"
37#include "BKE_blendfile.hh"
38#include "BKE_context.hh"
39#include "BKE_idtype.hh"
40#include "BKE_layer.hh"
41#include "BKE_lib_id.hh"
42#include "BKE_lib_override.hh"
43#include "BKE_lib_query.hh"
44#include "BKE_lib_remap.hh"
45#include "BKE_main.hh"
46#include "BKE_object.hh"
47#include "BKE_report.hh"
48#include "BKE_screen.hh"
49#include "BKE_workspace.hh"
50
51#include "DEG_depsgraph.hh"
53
54#include "ED_keyframing.hh"
55#include "ED_outliner.hh"
56#include "ED_screen.hh"
57#include "ED_select_utils.hh"
58
59#include "WM_api.hh"
60#include "WM_types.hh"
61
62#include "UI_interface.hh"
63#include "UI_view2d.hh"
64
65#include "RNA_access.hh"
66#include "RNA_define.hh"
67#include "RNA_enum_types.hh"
68#include "RNA_path.hh"
69
70#include "GPU_material.hh"
71
72#include "outliner_intern.hh"
74#include "tree/tree_iterator.hh"
75
76#include "wm_window.hh"
77
78using namespace blender::ed::outliner;
79
80namespace blender::ed::outliner {
81
82static void outliner_show_active(SpaceOutliner *space_outliner,
83 ARegion *region,
84 TreeElement *te,
85 ID *id);
86
87/* -------------------------------------------------------------------- */
91static void outliner_copybuffer_filepath_get(char filepath[FILE_MAX], size_t filepath_maxncpy)
92{
93 /* NOTE: this uses the same path as the 3D viewport. */
94 BLI_path_join(filepath, filepath_maxncpy, BKE_tempdir_base(), "copybuffer.blend");
95}
96
99/* -------------------------------------------------------------------- */
103static int outliner_highlight_update_invoke(bContext *C, wmOperator * /*op*/, const wmEvent *event)
104{
105 /* stop highlighting if out of area */
106 if (!ED_screen_area_active(C)) {
108 }
109
110 /* Drag and drop does its own highlighting. */
112 if (wm->drags.first) {
114 }
115
116 ARegion *region = CTX_wm_region(C);
117 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
118
119 float view_mval[2];
121 &region->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]);
122
124 space_outliner, &space_outliner->tree, view_mval[1]);
125
126 TreeElement *icon_te = nullptr;
127 bool is_over_icon = false;
128 if (hovered_te) {
130 space_outliner, hovered_te, view_mval[0], nullptr, &is_over_icon);
131 }
132
133 bool changed = false;
134
135 if (!hovered_te || !is_over_icon || !(hovered_te->store_elem->flag & TSE_HIGHLIGHTED) ||
136 !(icon_te->store_elem->flag & TSE_HIGHLIGHTED_ICON))
137 {
138 /* Clear highlights when nothing is hovered or when a new item is hovered. */
139 changed = outliner_flag_set(*space_outliner, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false);
140 if (hovered_te) {
141 hovered_te->store_elem->flag |= TSE_HIGHLIGHTED;
142 changed = true;
143 }
144 if (is_over_icon) {
146 changed = true;
147 }
148 }
149
150 if (changed) {
152 }
153
155}
156
158{
159 ot->name = "Update Highlight";
160 ot->idname = "OUTLINER_OT_highlight_update";
161 ot->description = "Update the item highlight based on the current mouse position";
162
164
166}
167
170/* -------------------------------------------------------------------- */
174void outliner_item_openclose(TreeElement *te, bool open, bool toggle_all)
175{
176 /* Only allow opening elements with children. */
178 return;
179 }
180
181 /* Don't allow collapsing the scene collection. */
182 TreeStoreElem *tselem = TREESTORE(te);
183 if (tselem->type == TSE_VIEW_COLLECTION_BASE) {
184 return;
185 }
186
187 if (open) {
188 tselem->flag &= ~TSE_CLOSED;
189 }
190 else {
191 tselem->flag |= TSE_CLOSED;
192 }
193
194 if (toggle_all) {
196 }
197}
198
204
206{
207 ARegion *region = CTX_wm_region(C);
208 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
209
210 float view_mval[2];
212 &region->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]);
213
214 if (event->type == MOUSEMOVE) {
215 TreeElement *te = outliner_find_item_at_y(space_outliner, &space_outliner->tree, view_mval[1]);
216
218
219 /* Only openclose if mouse is not over the previously toggled element */
220 if (te && TREESTORE(te) != data->prev_tselem) {
221
222 /* Only toggle openclose on the same level as the first clicked element */
223 if (te->xs == data->x_location) {
224 outliner_item_openclose(te, data->open, false);
225
227 }
228 }
229
230 if (te) {
231 data->prev_tselem = TREESTORE(te);
232 }
233 else {
234 data->prev_tselem = nullptr;
235 }
236 }
237 else if (event->val == KM_RELEASE) {
239
240 return OPERATOR_FINISHED;
241 }
242
244}
245
247{
248 ARegion *region = CTX_wm_region(C);
249 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
250
251 const bool toggle_all = RNA_boolean_get(op->ptr, "all");
252
253 float view_mval[2];
254
255 int mval[2];
256 WM_event_drag_start_mval(event, region, mval);
257
258 UI_view2d_region_to_view(&region->v2d, mval[0], mval[1], &view_mval[0], &view_mval[1]);
259
260 TreeElement *te = outliner_find_item_at_y(space_outliner, &space_outliner->tree, view_mval[1]);
261
262 if (te && outliner_item_is_co_within_close_toggle(te, view_mval[0])) {
263 TreeStoreElem *tselem = TREESTORE(te);
264
265 const bool open = (tselem->flag & TSE_CLOSED) ||
266 (toggle_all && outliner_flag_is_any_test(&te->subtree, TSE_CLOSED, 1));
267
268 outliner_item_openclose(te, open, toggle_all);
270
271 /* Only toggle once for single click toggling */
272 if ((event->type == LEFTMOUSE) && (event->val != KM_CLICK_DRAG)) {
273 return OPERATOR_FINISHED;
274 }
275
276 /* Store last expanded tselem and x coordinate of disclosure triangle */
277 OpenCloseData *toggle_data = MEM_cnew<OpenCloseData>("open_close_data");
278 toggle_data->prev_tselem = tselem;
279 toggle_data->open = open;
280 toggle_data->x_location = te->xs;
281
282 /* Store the first clicked on element */
283 op->customdata = toggle_data;
284
287 }
288
290}
291
293{
294 ot->name = "Open/Close";
295 ot->idname = "OUTLINER_OT_item_openclose";
296 ot->description = "Toggle whether item under cursor is enabled or closed";
297
300
302
303 RNA_def_boolean(ot->srna, "all", false, "All", "Close or open all items");
304}
305
308/* -------------------------------------------------------------------- */
312static void do_item_rename(ARegion *region,
313 TreeElement *te,
314 TreeStoreElem *tselem,
315 ReportList *reports)
316{
317 bool add_textbut = false;
318
319 /* FIXME: These info messages are often useless, they should be either reworded to be more
320 * informative for the user, or purely removed? */
321
322 /* Can't rename rna datablocks entries or listbases. */
323 if (ELEM(tselem->type,
325 TSE_NLA,
339 TSE_ID_BASE) ||
341 {
342 BKE_report(reports, RPT_INFO, "Not an editable name");
343 }
344 else if (ELEM(tselem->type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP)) {
345 BKE_report(reports, RPT_INFO, "Sequence names are not editable from the Outliner");
346 }
347 else if (TSE_IS_REAL_ID(tselem) && !ID_IS_EDITABLE(tselem->id)) {
348 BKE_report(reports, RPT_INFO, "External library data is not editable");
349 }
350 else if (TSE_IS_REAL_ID(tselem) && ID_IS_OVERRIDE_LIBRARY(tselem->id)) {
351 BKE_report(reports, RPT_INFO, "Overridden data-blocks names are not editable");
352 }
355
356 if (collection->flag & COLLECTION_IS_MASTER) {
357 BKE_report(reports, RPT_INFO, "Not an editable name");
358 }
359 else {
360 add_textbut = true;
361 }
362 }
363 else if (te->idcode == ID_LI) {
364 BKE_report(reports, RPT_INFO, "Library path is not editable, use the Relocate operation");
365 }
366 else {
367 add_textbut = true;
368 }
369
370 if (add_textbut) {
371 tselem->flag |= TSE_TEXTBUT;
372 ED_region_tag_redraw(region);
373 }
374}
375
377 ReportList *reports,
378 Scene * /*scene*/,
379 TreeElement *te,
380 TreeStoreElem * /*tsep*/,
381 TreeStoreElem *tselem)
382{
383 ARegion *region = CTX_wm_region(C);
384 do_item_rename(region, te, tselem, reports);
385}
386
388 ReportList *reports)
389{
390 TreeElement *active_element = outliner_find_element_with_flag(&space_outliner->tree, TSE_ACTIVE);
391
392 if (!active_element) {
393 BKE_report(reports, RPT_WARNING, "No active item to rename");
394 return nullptr;
395 }
396
397 return active_element;
398}
399
401 ARegion *region,
402 const wmEvent *event)
403{
404 float fmval[2];
405 UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
406
407 TreeElement *hovered = outliner_find_item_at_y(space_outliner, &space_outliner->tree, fmval[1]);
408 if (hovered && outliner_item_is_co_over_name(hovered, fmval[0])) {
409 return hovered;
410 }
411
412 return nullptr;
413}
414
416{
417 ARegion *region = CTX_wm_region(C);
418 View2D *v2d = &region->v2d;
419 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
420 const bool use_active = RNA_boolean_get(op->ptr, "use_active");
421
422 TreeElement *te = use_active ? outliner_item_rename_find_active(space_outliner, op->reports) :
423 outliner_item_rename_find_hovered(space_outliner, region, event);
424 if (!te) {
426 }
427
428 /* Force element into view. */
429 outliner_show_active(space_outliner, region, te, TREESTORE(te)->id);
430 int size_y = BLI_rcti_size_y(&v2d->mask) + 1;
431 int ytop = (te->ys + (size_y / 2));
432 int delta_y = ytop - v2d->cur.ymax;
433 outliner_scroll_view(space_outliner, region, delta_y);
434
435 do_item_rename(region, te, TREESTORE(te), op->reports);
436
437 return OPERATOR_FINISHED;
438}
439
441{
442 PropertyRNA *prop;
443
444 ot->name = "Rename";
445 ot->idname = "OUTLINER_OT_item_rename";
446 ot->description = "Rename the active element";
447
449
451
452 /* Flags. No undo, since this operator only activate the name editing text field in the Outliner,
453 * but does not actually change anything. */
455
456 prop = RNA_def_boolean(ot->srna,
457 "use_active",
458 false,
459 "Use Active",
460 "Rename the active item, rather than the one the mouse is over");
462}
463
466/* -------------------------------------------------------------------- */
470static void id_delete_tag(bContext *C, ReportList *reports, TreeElement *te, TreeStoreElem *tselem)
471{
472 Main *bmain = CTX_data_main(C);
473 ID *id = tselem->id;
474
475 BLI_assert(id != nullptr);
476 BLI_assert(((tselem->type == TSE_SOME_ID) && (te->idcode != 0)) ||
477 (tselem->type == TSE_LAYER_COLLECTION));
479
480 if (ID_IS_OVERRIDE_LIBRARY(id)) {
482 (id->override_library->flag & LIBOVERRIDE_FLAG_NO_HIERARCHY) == 0)
483 {
484 BKE_reportf(reports,
486 "Cannot delete library override id '%s', it is part of an override hierarchy",
487 id->name);
488 return;
489 }
490 }
491
492 if (te->idcode == ID_LI && ((Library *)id)->runtime.parent != nullptr) {
493 BKE_reportf(reports, RPT_WARNING, "Cannot delete indirectly linked library '%s'", id->name);
494 return;
495 }
496 if (id->tag & ID_TAG_INDIRECT) {
497 BKE_reportf(reports, RPT_WARNING, "Cannot delete indirectly linked id '%s'", id->name);
498 return;
499 }
500 if (ID_REAL_USERS(id) <= 1 && BKE_library_ID_is_indirectly_used(bmain, id)) {
501 BKE_reportf(reports,
503 "Cannot delete id '%s', indirectly used data-blocks need at least one user",
504 id->name);
505 return;
506 }
507 if (te->idcode == ID_WS) {
509 if (id->tag & ID_TAG_PRE_EXISTING) {
511 reports, RPT_WARNING, "Cannot delete currently visible workspace id '%s'", id->name);
513 return;
514 }
516 }
517
518 id->tag |= ID_TAG_DOIT;
519
520 WM_event_add_notifier(C, NC_WINDOW, nullptr);
521}
522
524 ReportList *reports,
525 Scene * /*scene*/,
526 TreeElement *te,
527 TreeStoreElem * /*tsep*/,
528 TreeStoreElem *tselem)
529{
530 id_delete_tag(C, reports, te, tselem);
531}
532
534 ReportList *reports,
535 TreeElement *te,
536 const float mval[2])
537{
538 int id_tagged_num = 0;
539
540 if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) {
541 TreeStoreElem *tselem = TREESTORE(te);
542
543 if (te->idcode != 0 && tselem->id) {
544 if (te->idcode == ID_LI && ((Library *)tselem->id)->runtime.parent) {
545 BKE_reportf(reports,
547 "Cannot delete indirectly linked library '%s'",
548 ((Library *)tselem->id)->runtime.filepath_abs);
549 }
550 else {
551 id_delete_tag(C, reports, te, tselem);
552 id_tagged_num++;
553 }
554 }
555 }
556 else {
557 LISTBASE_FOREACH (TreeElement *, te_sub, &te->subtree) {
558 if ((id_tagged_num += outliner_id_delete_tag(C, reports, te_sub, mval)) != 0) {
559 break;
560 }
561 }
562 }
563
564 return id_tagged_num;
565}
566
567static int outliner_id_delete_invoke(bContext *C, wmOperator *op, const wmEvent *event)
568{
569 Main *bmain = CTX_data_main(C);
570 ARegion *region = CTX_wm_region(C);
571 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
572 float fmval[2];
573
574 BLI_assert(region && space_outliner);
575
576 UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
577
578 int id_tagged_num = 0;
579 BKE_main_id_tag_all(bmain, ID_TAG_DOIT, false);
580 LISTBASE_FOREACH (TreeElement *, te, &space_outliner->tree) {
581 if ((id_tagged_num += outliner_id_delete_tag(C, op->reports, te, fmval)) != 0) {
582 break;
583 }
584 }
585 if (id_tagged_num == 0) {
586 BKE_main_id_tag_all(bmain, ID_TAG_DOIT, false);
587 return OPERATOR_CANCELLED;
588 }
589
591 BKE_main_id_tag_all(bmain, ID_TAG_DOIT, false);
592 return OPERATOR_FINISHED;
593}
594
596{
597 ot->name = "Delete Data-Block";
598 ot->idname = "OUTLINER_OT_id_delete";
599 ot->description = "Delete the ID under cursor";
600
603
604 /* Flags. */
606}
607
610/* -------------------------------------------------------------------- */
615{
616 Main *bmain = CTX_data_main(C);
617 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
618
619 const short id_type = short(RNA_enum_get(op->ptr, "id_type"));
620 ID *old_id = static_cast<ID *>(
621 BLI_findlink(which_libbase(CTX_data_main(C), id_type), RNA_enum_get(op->ptr, "old_id")));
622 ID *new_id = static_cast<ID *>(
623 BLI_findlink(which_libbase(CTX_data_main(C), id_type), RNA_enum_get(op->ptr, "new_id")));
624
625 /* check for invalid states */
626 if (space_outliner == nullptr) {
627 return OPERATOR_CANCELLED;
628 }
629
630 if (!(old_id && new_id && (old_id != new_id) && (GS(old_id->name) == GS(new_id->name)))) {
633 "Invalid old/new ID pair ('%s' / '%s')",
634 old_id ? old_id->name : "Invalid ID",
635 new_id ? new_id->name : "Invalid ID");
636 return OPERATOR_CANCELLED;
637 }
638
639 if (!ID_IS_EDITABLE(old_id)) {
642 "Old ID '%s' is linked from a library, indirect usages of this data-block will "
643 "not be remapped",
644 old_id->name);
645 }
646
649
651
652 /* recreate dependency graph to include new objects */
654
655 /* Free gpu materials, some materials depend on existing objects,
656 * such as lights so freeing correctly refreshes. */
657 GPU_materials_free(bmain);
658
659 WM_event_add_notifier(C, NC_WINDOW, nullptr);
660
661 return OPERATOR_FINISHED;
662}
663
665 wmOperator *op,
666 ListBase *tree,
667 const float y)
668{
670 if (y > te->ys && y < te->ys + UI_UNIT_Y) {
671 TreeStoreElem *tselem = TREESTORE(te);
672
673 if ((tselem->type == TSE_SOME_ID) && tselem->id) {
674 RNA_enum_set(op->ptr, "id_type", GS(tselem->id->name));
675 RNA_enum_set_identifier(C, op->ptr, "new_id", tselem->id->name + 2);
676 RNA_enum_set_identifier(C, op->ptr, "old_id", tselem->id->name + 2);
677 return true;
678 }
679 }
680 if (outliner_id_remap_find_tree_element(C, op, &te->subtree, y)) {
681 return true;
682 }
683 }
684 return false;
685}
686
687static int outliner_id_remap_invoke(bContext *C, wmOperator *op, const wmEvent *event)
688{
689 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
690 ARegion *region = CTX_wm_region(C);
691 float fmval[2];
692
693 if (!RNA_property_is_set(op->ptr, RNA_struct_find_property(op->ptr, "id_type"))) {
694 UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
695
696 outliner_id_remap_find_tree_element(C, op, &space_outliner->tree, fmval[1]);
697 }
698
699 return WM_operator_props_dialog_popup(C, op, 400, IFACE_("Remap Data ID"), IFACE_("Remap"));
700}
701
704 PropertyRNA * /*prop*/,
705 bool *r_free)
706{
707 if (C == nullptr) {
709 }
710
711 EnumPropertyItem item_tmp = {0}, *item = nullptr;
712 int totitem = 0;
713 int i = 0;
714
715 short id_type = short(RNA_enum_get(ptr, "id_type"));
716 ID *id = static_cast<ID *>(which_libbase(CTX_data_main(C), id_type)->first);
717
718 for (; id; id = static_cast<ID *>(id->next)) {
719 item_tmp.identifier = item_tmp.name = id->name + 2;
720 item_tmp.value = i++;
721 RNA_enum_item_add(&item, &totitem, &item_tmp);
722 }
723
724 RNA_enum_item_end(&item, &totitem);
725 *r_free = true;
726
727 return item;
728}
729
731{
732 PropertyRNA *prop;
733
734 /* identifiers */
735 ot->name = "Outliner ID Data Remap";
736 ot->idname = "OUTLINER_OT_id_remap";
737
738 /* callbacks */
742
743 /* Flags. */
745
746 prop = RNA_def_enum(ot->srna, "id_type", rna_enum_id_type_items, ID_OB, "ID Type", "");
748 /* Changing ID type wont make sense, would return early with "Invalid old/new ID pair" anyways.
749 */
751
752 prop = RNA_def_enum(
753 ot->srna, "old_id", rna_enum_dummy_NULL_items, 0, "Old ID", "Old ID to replace");
756
758 "new_id",
760 0,
761 "New ID",
762 "New ID to remap all selected IDs' users to");
765}
766
768 ReportList * /*reports*/,
769 Scene * /*scene*/,
770 TreeElement * /*te*/,
771 TreeStoreElem * /*tsep*/,
772 TreeStoreElem *tselem)
773{
774 wmOperatorType *ot = WM_operatortype_find("OUTLINER_OT_id_remap", false);
775 PointerRNA op_props;
776
777 BLI_assert(tselem->id != nullptr);
778
780
781 RNA_enum_set(&op_props, "id_type", GS(tselem->id->name));
782 RNA_enum_set_identifier(C, &op_props, "old_id", tselem->id->name + 2);
783
784 WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &op_props, nullptr);
785
787}
788
791/* -------------------------------------------------------------------- */
795static int outliner_id_copy_tag(SpaceOutliner *space_outliner,
796 ListBase *tree,
798{
799 using namespace blender::bke::blendfile;
800
801 int num_ids = 0;
802
804 TreeStoreElem *tselem = TREESTORE(te);
805
806 /* Add selected item and all of its dependencies to the copy buffer. */
807 if (tselem->flag & TSE_SELECTED && ELEM(tselem->type, TSE_SOME_ID, TSE_LAYER_COLLECTION)) {
808 copybuffer.id_add(tselem->id,
809 PartialWriteContext::IDAddOptions{PartialWriteContext::IDAddOperations(
810 PartialWriteContext::IDAddOperations::SET_FAKE_USER |
811 PartialWriteContext::IDAddOperations::SET_CLIPBOARD_MARK |
812 PartialWriteContext::IDAddOperations::ADD_DEPENDENCIES)},
813 nullptr);
814 num_ids++;
815 }
816
817 /* go over sub-tree */
818 num_ids += outliner_id_copy_tag(space_outliner, &te->subtree, copybuffer);
819 }
820
821 return num_ids;
822}
823
825{
826 using namespace blender::bke::blendfile;
827
828 Main *bmain = CTX_data_main(C);
829 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
831
832 const int num_ids = outliner_id_copy_tag(space_outliner, &space_outliner->tree, copybuffer);
833 if (num_ids == 0) {
834 BKE_report(op->reports, RPT_INFO, "No selected data-blocks to copy");
835 return OPERATOR_CANCELLED;
836 }
837
838 char filepath[FILE_MAX];
839 outliner_copybuffer_filepath_get(filepath, sizeof(filepath));
840 copybuffer.write(filepath, *op->reports);
841
842 BKE_reportf(op->reports, RPT_INFO, "Copied %d selected data-block(s)", num_ids);
843
844 return OPERATOR_FINISHED;
845}
846
848{
849 /* identifiers */
850 ot->name = "Outliner ID Data Copy";
851 ot->idname = "OUTLINER_OT_id_copy";
852 ot->description = "Copy the selected data-blocks to the internal clipboard";
853
854 /* callbacks */
857
858 /* Flags, don't need any undo here (this operator does not change anything in Blender data). */
859 ot->flag = 0;
860}
861
864/* -------------------------------------------------------------------- */
869{
870 char filepath[FILE_MAX];
872
873 outliner_copybuffer_filepath_get(filepath, sizeof(filepath));
874
875 const int num_pasted = BKE_copybuffer_paste(C, filepath, flag, op->reports, 0);
876 if (num_pasted == 0) {
877 BKE_report(op->reports, RPT_INFO, "No data to paste");
878 return OPERATOR_CANCELLED;
879 }
880
881 WM_event_add_notifier(C, NC_WINDOW, nullptr);
882
883 BKE_reportf(op->reports, RPT_INFO, "%d data-block(s) pasted", num_pasted);
884
885 return OPERATOR_FINISHED;
886}
887
889{
890 /* identifiers */
891 ot->name = "Outliner ID Data Paste";
892 ot->idname = "OUTLINER_OT_id_paste";
893 ot->description = "Paste data-blocks from the internal clipboard";
894
895 /* callbacks */
898
899 /* flags */
901}
902
905/* -------------------------------------------------------------------- */
909static int lib_relocate(
910 bContext *C, TreeElement *te, TreeStoreElem *tselem, wmOperatorType *ot, const bool reload)
911{
912 PointerRNA op_props;
913 int ret = 0;
914
915 BLI_assert(te->idcode == ID_LI && tselem->id != nullptr);
917
919
920 RNA_string_set(&op_props, "library", tselem->id->name + 2);
921
922 if (reload) {
923 Library *lib = (Library *)tselem->id;
924 char dir[FILE_MAXDIR], filename[FILE_MAX];
925
927 lib->runtime.filepath_abs, dir, sizeof(dir), filename, sizeof(filename));
928
929 printf("%s, %s\n", tselem->id->name, lib->runtime.filepath_abs);
930
931 /* We assume if both paths in lib are not the same then `lib->filepath` was relative. */
933 &op_props, "relative_path", BLI_path_cmp(lib->runtime.filepath_abs, lib->filepath) != 0);
934
935 RNA_string_set(&op_props, "directory", dir);
936 RNA_string_set(&op_props, "filename", filename);
937
938 ret = WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &op_props, nullptr);
939 }
940 else {
941 ret = WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &op_props, nullptr);
942 }
943
945
946 return ret;
947}
948
950 bContext *C, ReportList *reports, TreeElement *te, const float mval[2], const bool reload)
951{
952 if (mval[1] > te->ys && mval[1] < te->ys + UI_UNIT_Y) {
953 TreeStoreElem *tselem = TREESTORE(te);
954
955 if (te->idcode == ID_LI && tselem->id) {
956 if (((Library *)tselem->id)->runtime.parent && !reload) {
957 BKE_reportf(reports,
959 "Cannot relocate indirectly linked library '%s'",
960 ((Library *)tselem->id)->runtime.filepath_abs);
961 return OPERATOR_CANCELLED;
962 }
963
964 wmOperatorType *ot = WM_operatortype_find(reload ? "WM_OT_lib_reload" : "WM_OT_lib_relocate",
965 false);
966 return lib_relocate(C, te, tselem, ot, reload);
967 }
968 }
969 else {
970 LISTBASE_FOREACH (TreeElement *, te_sub, &te->subtree) {
971 int ret;
972 if ((ret = outliner_lib_relocate_invoke_do(C, reports, te_sub, mval, reload))) {
973 return ret;
974 }
975 }
976 }
977
978 return 0;
979}
980
982{
983 ARegion *region = CTX_wm_region(C);
984 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
985 float fmval[2];
986
987 BLI_assert(region && space_outliner);
988
989 UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
990
991 LISTBASE_FOREACH (TreeElement *, te, &space_outliner->tree) {
992 int ret;
993
994 if ((ret = outliner_lib_relocate_invoke_do(C, op->reports, te, fmval, false))) {
995 return ret;
996 }
997 }
998
999 return OPERATOR_CANCELLED;
1000}
1001
1003{
1004 ot->name = "Relocate Library";
1005 ot->idname = "OUTLINER_OT_lib_relocate";
1006 ot->description = "Relocate the library under cursor";
1007
1010
1011 /* Flags. */
1013}
1014
1016 ReportList * /*reports*/,
1017 Scene * /*scene*/,
1018 TreeElement *te,
1019 TreeStoreElem * /*tsep*/,
1020 TreeStoreElem *tselem)
1021{
1022 /* XXX: This does not work with several items
1023 * (it is only called once in the end, due to the 'deferred'
1024 * file-browser invocation through event system...). */
1025
1026 wmOperatorType *ot = WM_operatortype_find("WM_OT_lib_relocate", false);
1027
1028 lib_relocate(C, te, tselem, ot, false);
1029}
1030
1032{
1033 ARegion *region = CTX_wm_region(C);
1034 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
1035 float fmval[2];
1036
1037 BLI_assert(region && space_outliner);
1038
1039 UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]);
1040
1041 LISTBASE_FOREACH (TreeElement *, te, &space_outliner->tree) {
1042 int ret;
1043
1044 if ((ret = outliner_lib_relocate_invoke_do(C, op->reports, te, fmval, true))) {
1045 return ret;
1046 }
1047 }
1048
1049 return OPERATOR_CANCELLED;
1050}
1051
1054/* -------------------------------------------------------------------- */
1059{
1060 ot->name = "Reload Library";
1061 ot->idname = "OUTLINER_OT_lib_reload";
1062 ot->description = "Reload the library under cursor";
1063
1066
1067 /* Flags. */
1069}
1070
1072 ReportList * /*reports*/,
1073 Scene * /*scene*/,
1074 TreeElement *te,
1075 TreeStoreElem * /*tsep*/,
1076 TreeStoreElem *tselem)
1077{
1078 wmOperatorType *ot = WM_operatortype_find("WM_OT_lib_reload", false);
1079
1080 lib_relocate(C, te, tselem, ot, true);
1081}
1082
1085/* -------------------------------------------------------------------- */
1089static int outliner_count_levels(ListBase *lb, const int curlevel)
1090{
1091 int level = curlevel;
1092
1093 LISTBASE_FOREACH (TreeElement *, te, lb) {
1094 int lev = outliner_count_levels(&te->subtree, curlevel + 1);
1095 if (lev > level) {
1096 level = lev;
1097 }
1098 }
1099 return level;
1100}
1101
1102int outliner_flag_is_any_test(ListBase *lb, short flag, const int curlevel)
1103{
1104 LISTBASE_FOREACH (TreeElement *, te, lb) {
1105 TreeStoreElem *tselem = TREESTORE(te);
1106 if (tselem->flag & flag) {
1107 return curlevel;
1108 }
1109
1110 int level = outliner_flag_is_any_test(&te->subtree, flag, curlevel + 1);
1111 if (level) {
1112 return level;
1113 }
1114 }
1115 return 0;
1116}
1117
1118bool outliner_flag_set(const SpaceOutliner &space_outliner, const short flag, const short set)
1119{
1120 return outliner_flag_set(space_outliner.tree, flag, set);
1121}
1122
1123bool outliner_flag_set(const ListBase &lb, const short flag, const short set)
1124{
1125 bool changed = false;
1126
1127 tree_iterator::all(lb, [&](TreeElement *te) {
1128 TreeStoreElem *tselem = TREESTORE(te);
1129 bool has_flag = (tselem->flag & flag);
1130 if (set == 0) {
1131 if (has_flag) {
1132 tselem->flag &= ~flag;
1133 changed = true;
1134 }
1135 }
1136 else if (!has_flag) {
1137 tselem->flag |= flag;
1138 changed = true;
1139 }
1140 });
1141
1142 return changed;
1143}
1144
1145bool outliner_flag_flip(const SpaceOutliner &space_outliner, const short flag)
1146{
1147 return outliner_flag_flip(space_outliner.tree, flag);
1148}
1149
1150bool outliner_flag_flip(const ListBase &lb, const short flag)
1151{
1152 bool changed = false;
1153
1154 tree_iterator::all(lb, [&](TreeElement *te) {
1155 TreeStoreElem *tselem = TREESTORE(te);
1156 tselem->flag ^= flag;
1157 });
1158
1159 return changed;
1160}
1161
1164/* -------------------------------------------------------------------- */
1169{
1170 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
1171 ARegion *region = CTX_wm_region(C);
1172
1173 if (outliner_flag_is_any_test(&space_outliner->tree, TSE_CLOSED, 1)) {
1174 outliner_flag_set(*space_outliner, TSE_CLOSED, 0);
1175 }
1176 else {
1177 outliner_flag_set(*space_outliner, TSE_CLOSED, 1);
1178 }
1179
1180 ED_region_tag_redraw(region);
1181
1182 return OPERATOR_FINISHED;
1183}
1184
1186{
1187 /* identifiers */
1188 ot->name = "Expand/Collapse All";
1189 ot->idname = "OUTLINER_OT_expanded_toggle";
1190 ot->description = "Expand/Collapse all items";
1191
1192 /* callbacks */
1195
1196 /* no undo or registry, UI option */
1197}
1198
1201/* -------------------------------------------------------------------- */
1206{
1207 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
1208 ARegion *region = CTX_wm_region(C);
1209 Scene *scene = CTX_data_scene(C);
1210 int action = RNA_enum_get(op->ptr, "action");
1211 if (action == SEL_TOGGLE) {
1212 action = outliner_flag_is_any_test(&space_outliner->tree, TSE_SELECTED, 1) ? SEL_DESELECT :
1213 SEL_SELECT;
1214 }
1215
1216 switch (action) {
1217 case SEL_SELECT:
1218 outliner_flag_set(*space_outliner, TSE_SELECTED, 1);
1219 break;
1220 case SEL_DESELECT:
1221 outliner_flag_set(*space_outliner, TSE_SELECTED, 0);
1222 break;
1223 case SEL_INVERT:
1224 outliner_flag_flip(*space_outliner, TSE_SELECTED);
1225 break;
1226 }
1227
1228 ED_outliner_select_sync_from_outliner(C, space_outliner);
1229
1233
1234 return OPERATOR_FINISHED;
1235}
1236
1238{
1239 /* identifiers */
1240 ot->name = "Toggle Selected";
1241 ot->idname = "OUTLINER_OT_select_all";
1242 ot->description = "Toggle the Outliner selection of items";
1243
1244 /* callbacks */
1247
1248 /* no undo or registry */
1249
1250 /* rna */
1252}
1253
1256/* -------------------------------------------------------------------- */
1261{
1262 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
1263 ScrArea *area = CTX_wm_area(C);
1265 UI_textbutton_activate_rna(C, region, space_outliner, "filter_text");
1266
1267 return OPERATOR_FINISHED;
1268}
1269
1271{
1272 /* Identifiers. */
1273 ot->name = "Filter";
1274 ot->description = "Start entering filter text";
1275 ot->idname = "OUTLINER_OT_start_filter";
1276
1277 /* Callbacks. */
1280}
1281
1283{
1284 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
1285 space_outliner->search_string[0] = '\0';
1287 return OPERATOR_FINISHED;
1288}
1289
1291{
1292 /* Identifiers. */
1293 ot->name = "Clear Filter";
1294 ot->description = "Clear the search filter";
1295 ot->idname = "OUTLINER_OT_clear_filter";
1296
1297 /* Callbacks. */
1300}
1301
1304/* -------------------------------------------------------------------- */
1308void outliner_set_coordinates(const ARegion *region, const SpaceOutliner *space_outliner)
1309{
1310 int starty = int(region->v2d.tot.ymax) - UI_UNIT_Y;
1311
1312 tree_iterator::all_open(*space_outliner, [&](TreeElement *te) {
1313 /* store coord and continue, we need coordinates for elements outside view too */
1314 te->xs = 0;
1315 te->ys = float(starty);
1316 starty -= UI_UNIT_Y;
1317 });
1318}
1319
1320/* return 1 when levels were opened */
1322{
1323 TreeStoreElem *tselem;
1324 int retval = 0;
1325
1326 for (te = te->parent; te; te = te->parent) {
1327 tselem = TREESTORE(te);
1328 if (tselem->flag & TSE_CLOSED) {
1329 tselem->flag &= ~TSE_CLOSED;
1330 retval = 1;
1331 }
1332 }
1333 return retval;
1334}
1335
1340 SpaceOutliner *space_outliner,
1341 const Scene *scene,
1342 ViewLayer *view_layer)
1343{
1344 TreeElement *te;
1345
1346 BKE_view_layer_synced_ensure(scene, view_layer);
1347 Object *obact = BKE_view_layer_active_object_get(view_layer);
1348
1349 if (!obact) {
1350 return nullptr;
1351 }
1352
1353 te = outliner_find_id(space_outliner, &space_outliner->tree, &obact->id);
1354
1355 if (te != nullptr && obact->type == OB_ARMATURE) {
1356 /* traverse down the bone hierarchy in case of armature */
1357 TreeElement *te_obact = te;
1358
1359 if (obact->mode & OB_MODE_POSE) {
1360 Object *obpose = BKE_object_pose_armature_get(obact);
1361 bPoseChannel *pchan = BKE_pose_channel_active(obpose, false);
1362 if (pchan) {
1363 te = outliner_find_posechannel(&te_obact->subtree, pchan);
1364 }
1365 }
1366 else if (obact->mode & OB_MODE_EDIT) {
1367 EditBone *ebone = CTX_data_active_bone(C);
1368 if (ebone) {
1369 te = outliner_find_editbone(&te_obact->subtree, ebone);
1370 }
1371 }
1372 }
1373
1374 return te;
1375}
1376
1377static void outliner_show_active(SpaceOutliner *space_outliner,
1378 ARegion *region,
1379 TreeElement *te,
1380 ID *id)
1381{
1382 /* open up tree to active object/bone */
1383 if (TREESTORE(te)->id == id) {
1384 if (outliner_open_back(te)) {
1385 outliner_set_coordinates(region, space_outliner);
1386 }
1387 return;
1388 }
1389
1390 LISTBASE_FOREACH (TreeElement *, ten, &te->subtree) {
1391 outliner_show_active(space_outliner, region, ten, id);
1392 }
1393}
1394
1396{
1397 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
1398 const Scene *scene = CTX_data_scene(C);
1399 ViewLayer *view_layer = CTX_data_view_layer(C);
1400 ARegion *region = CTX_wm_region(C);
1401 View2D *v2d = &region->v2d;
1402
1404 C, space_outliner, scene, view_layer);
1405
1406 if (active_element) {
1407 ID *id = TREESTORE(active_element)->id;
1408
1409 /* Expand all elements in the outliner with matching ID */
1410 LISTBASE_FOREACH (TreeElement *, te, &space_outliner->tree) {
1411 outliner_show_active(space_outliner, region, te, id);
1412 }
1413
1414 /* Also open back from the active_element (only done for the first found occurrence of ID
1415 * though). */
1416 outliner_show_active(space_outliner, region, active_element, id);
1417
1418 /* Center view on first element found */
1419 int size_y = BLI_rcti_size_y(&v2d->mask) + 1;
1420 int ytop = (active_element->ys + (size_y / 2));
1421 int delta_y = ytop - v2d->cur.ymax;
1422
1423 outliner_scroll_view(space_outliner, region, delta_y);
1424 }
1425 else {
1426 return OPERATOR_CANCELLED;
1427 }
1428
1430
1431 return OPERATOR_FINISHED;
1432}
1433
1435{
1436 /* identifiers */
1437 ot->name = "Show Active";
1438 ot->idname = "OUTLINER_OT_show_active";
1439 ot->description =
1440 "Open up the tree and adjust the view so that the active object is shown centered";
1441
1442 /* callbacks */
1445}
1446
1449/* -------------------------------------------------------------------- */
1454{
1455 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
1456 ARegion *region = CTX_wm_region(C);
1457 int size_y = BLI_rcti_size_y(&region->v2d.mask) + 1;
1458
1459 bool up = RNA_boolean_get(op->ptr, "up");
1460
1461 if (!up) {
1462 size_y = -size_y;
1463 }
1464
1465 outliner_scroll_view(space_outliner, region, size_y);
1466
1468
1469 return OPERATOR_FINISHED;
1470}
1471
1473{
1474 PropertyRNA *prop;
1475
1476 /* identifiers */
1477 ot->name = "Scroll Page";
1478 ot->idname = "OUTLINER_OT_scroll_page";
1479 ot->description = "Scroll page up or down";
1480
1481 /* callbacks */
1484
1485 /* properties */
1486 prop = RNA_def_boolean(ot->srna, "up", false, "Up", "Scroll up one page");
1488}
1489
1492/* -------------------------------------------------------------------- */
1496/* helper function for Show/Hide one level operator */
1497static void outliner_openclose_level(ListBase *lb, int curlevel, int level, int open)
1498{
1499 LISTBASE_FOREACH (TreeElement *, te, lb) {
1500 TreeStoreElem *tselem = TREESTORE(te);
1501
1502 if (open) {
1503 if (curlevel <= level) {
1504 tselem->flag &= ~TSE_CLOSED;
1505 }
1506 }
1507 else {
1508 if (curlevel >= level) {
1509 tselem->flag |= TSE_CLOSED;
1510 }
1511 }
1512
1513 outliner_openclose_level(&te->subtree, curlevel + 1, level, open);
1514 }
1515}
1516
1518{
1519 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
1520 ARegion *region = CTX_wm_region(C);
1521 const bool add = RNA_boolean_get(op->ptr, "open");
1522 int level;
1523
1524 level = outliner_flag_is_any_test(&space_outliner->tree, TSE_CLOSED, 1);
1525 if (add == 1) {
1526 if (level) {
1527 outliner_openclose_level(&space_outliner->tree, 1, level, 1);
1528 }
1529 }
1530 else {
1531 if (level == 0) {
1532 level = outliner_count_levels(&space_outliner->tree, 0);
1533 }
1534 if (level) {
1535 outliner_openclose_level(&space_outliner->tree, 1, level - 1, 0);
1536 }
1537 }
1538
1539 ED_region_tag_redraw(region);
1540
1541 return OPERATOR_FINISHED;
1542}
1543
1545{
1546 PropertyRNA *prop;
1547
1548 /* identifiers */
1549 ot->name = "Show/Hide One Level";
1550 ot->idname = "OUTLINER_OT_show_one_level";
1551 ot->description = "Expand/collapse all entries by one level";
1552
1553 /* callbacks */
1556
1557 /* no undo or registry, UI option */
1558
1559 /* properties */
1560 prop = RNA_def_boolean(ot->srna, "open", true, "Open", "Expand all entries one level deep");
1562}
1563
1566/* -------------------------------------------------------------------- */
1575{
1576 LISTBASE_FOREACH (TreeElement *, te, lb) {
1577 TreeStoreElem *tselem = TREESTORE(te);
1578 if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) {
1579 return 1;
1580 }
1581 if (subtree_has_objects(&te->subtree)) {
1582 return 1;
1583 }
1584 }
1585 return 0;
1586}
1587
1588/* Helper function for Show Hierarchy operator */
1589static void tree_element_show_hierarchy(Scene *scene, SpaceOutliner *space_outliner)
1590{
1591 /* open all object elems, close others */
1592 tree_iterator::all_open(*space_outliner, [&](TreeElement *te) {
1593 TreeStoreElem *tselem = TREESTORE(te);
1594
1595 if (ELEM(tselem->type,
1600 {
1601 if (te->idcode == ID_SCE) {
1602 if (tselem->id != (ID *)scene) {
1603 tselem->flag |= TSE_CLOSED;
1604 }
1605 else {
1606 tselem->flag &= ~TSE_CLOSED;
1607 }
1608 }
1609 else if (te->idcode == ID_OB) {
1610 if (subtree_has_objects(&te->subtree)) {
1611 tselem->flag &= ~TSE_CLOSED;
1612 }
1613 else {
1614 tselem->flag |= TSE_CLOSED;
1615 }
1616 }
1617 }
1618 else {
1619 tselem->flag |= TSE_CLOSED;
1620 }
1621 });
1622}
1623
1624/* show entire object level hierarchy */
1626{
1627 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
1628 ARegion *region = CTX_wm_region(C);
1629 Scene *scene = CTX_data_scene(C);
1630
1631 /* recursively open/close levels */
1632 tree_element_show_hierarchy(scene, space_outliner);
1633
1634 ED_region_tag_redraw(region);
1635
1636 return OPERATOR_FINISHED;
1637}
1638
1640{
1641 /* identifiers */
1642 ot->name = "Show Hierarchy";
1643 ot->idname = "OUTLINER_OT_show_hierarchy";
1644 ot->description = "Open all object entries and close all others";
1645
1646 /* callbacks */
1648 /* TODO: shouldn't be allowed in RNA views... */
1650
1651 /* no undo or registry, UI option */
1652}
1653
1656/* -------------------------------------------------------------------- */
1664{
1665 ScrArea *area = CTX_wm_area(C);
1666 if ((area) && (area->spacetype == SPACE_OUTLINER)) {
1667 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
1668 return (space_outliner->outlinevis == SO_DATA_API);
1669 }
1670 return false;
1671}
1672
1673/* Helper func to extract an RNA path from selected tree element
1674 * NOTE: the caller must zero-out all values of the pointers that it passes here first, as
1675 * this function does not do that yet
1676 */
1678 TreeStoreElem *tselem,
1679 ID **id,
1680 char **path,
1681 int *array_index,
1682 short *flag,
1683 short * /*groupmode*/)
1684{
1685 ListBase hierarchy = {nullptr, nullptr};
1686 char *newpath = nullptr;
1687
1688 /* optimize tricks:
1689 * - Don't do anything if the selected item is a 'struct', but arrays are allowed
1690 */
1691 if (tselem->type == TSE_RNA_STRUCT) {
1692 return;
1693 }
1694
1695 /* Overview of Algorithm:
1696 * 1. Go up the chain of parents until we find the 'root', taking note of the
1697 * levels encountered in reverse-order (i.e. items are added to the start of the list
1698 * for more convenient looping later)
1699 * 2. Walk down the chain, adding from the first ID encountered
1700 * (which will become the 'ID' for the KeyingSet Path), and build a
1701 * path as we step through the chain
1702 */
1703
1704 /* step 1: flatten out hierarchy of parents into a flat chain */
1705 for (TreeElement *tem = te->parent; tem; tem = tem->parent) {
1706 LinkData *ld = MEM_cnew<LinkData>("LinkData for tree_element_to_path()");
1707 ld->data = tem;
1708 BLI_addhead(&hierarchy, ld);
1709 }
1710
1711 /* step 2: step down hierarchy building the path
1712 * (NOTE: addhead in previous loop was needed so that we can loop like this) */
1713 LISTBASE_FOREACH (LinkData *, ld, &hierarchy) {
1714 /* get data */
1715 TreeElement *tem = (TreeElement *)ld->data;
1717 PointerRNA ptr = tem_rna->get_pointer_rna();
1718
1719 /* check if we're looking for first ID, or appending to path */
1720 if (*id) {
1721 /* just 'append' property to path
1722 * - to prevent memory leaks, we must write to newpath not path,
1723 * then free old path + swap them.
1724 */
1726 PropertyRNA *prop = tem_rna_prop->get_property_rna();
1727
1728 if (RNA_property_type(prop) == PROP_POINTER) {
1729 /* for pointer we just append property name */
1730 newpath = RNA_path_append(*path, &ptr, prop, 0, nullptr);
1731 }
1732 else if (RNA_property_type(prop) == PROP_COLLECTION) {
1733 char buf[128], *name;
1734
1735 TreeElement *temnext = (TreeElement *)(ld->next->data);
1736 PointerRNA nextptr = tree_element_cast<TreeElementRNACommon>(temnext)->get_pointer_rna();
1737 name = RNA_struct_name_get_alloc(&nextptr, buf, sizeof(buf), nullptr);
1738
1739 if (name) {
1740 /* if possible, use name as a key in the path */
1741 newpath = RNA_path_append(*path, nullptr, prop, 0, name);
1742
1743 if (name != buf) {
1744 MEM_freeN(name);
1745 }
1746 }
1747 else {
1748 /* otherwise use index */
1749 int index = 0;
1750
1751 LISTBASE_FOREACH (TreeElement *, temsub, &tem->subtree) {
1752 if (temsub == temnext) {
1753 break;
1754 }
1755 index++;
1756 }
1757 newpath = RNA_path_append(*path, nullptr, prop, index, nullptr);
1758 }
1759
1760 ld = ld->next;
1761 }
1762 }
1763
1764 if (newpath) {
1765 if (*path) {
1766 MEM_freeN(*path);
1767 }
1768 *path = newpath;
1769 newpath = nullptr;
1770 }
1771 }
1772 else {
1773 /* no ID, so check if entry is RNA-struct,
1774 * and if that RNA-struct is an ID datablock to extract info from. */
1776 /* ptr->data not ptr->owner_id seems to be the one we want,
1777 * since ptr->data is sometimes the owner of this ID? */
1778 if (RNA_struct_is_ID(ptr.type)) {
1779 *id = static_cast<ID *>(ptr.data);
1780
1781 /* clear path */
1782 if (*path) {
1783 MEM_freeN(*path);
1784 path = nullptr;
1785 }
1786 }
1787 }
1788 }
1789 }
1790
1791 /* step 3: if we've got an ID, add the current item to the path */
1792 if (*id) {
1793 /* add the active property to the path */
1794 PropertyRNA *prop = tree_element_cast<TreeElementRNACommon>(te)->get_property_rna();
1795
1796 /* array checks */
1797 if (tselem->type == TSE_RNA_ARRAY_ELEM) {
1798 /* item is part of an array, so must set the array_index */
1799 *array_index = te->index;
1800 }
1801 else if (RNA_property_array_check(prop)) {
1802 /* entire array was selected, so keyframe all */
1804 }
1805
1806 /* path */
1807 newpath = RNA_path_append(*path, nullptr, prop, 0, nullptr);
1808 if (*path) {
1809 MEM_freeN(*path);
1810 }
1811 *path = newpath;
1812 }
1813
1814 /* free temp data */
1815 BLI_freelistN(&hierarchy);
1816}
1817
1820/* -------------------------------------------------------------------- */
1830enum {
1833} /*eDrivers_EditModes*/;
1834
1835/* Iterate over tree, finding and working on selected items */
1836static void do_outliner_drivers_editop(SpaceOutliner *space_outliner,
1837 ReportList *reports,
1838 short mode)
1839{
1840 tree_iterator::all_open(*space_outliner, [&](TreeElement *te) {
1841 TreeStoreElem *tselem = TREESTORE(te);
1842
1843 /* if item is selected, perform operation */
1844 if (!(tselem->flag & TSE_SELECTED)) {
1845 return;
1846 }
1847
1848 ID *id = nullptr;
1849 char *path = nullptr;
1850 int array_index = 0;
1851 short flag = 0;
1852 short groupmode = KSP_GROUP_KSNAME;
1853
1855 PointerRNA ptr = te_rna ? te_rna->get_pointer_rna() : PointerRNA_NULL;
1856 PropertyRNA *prop = te_rna ? te_rna->get_property_rna() : nullptr;
1857
1858 /* check if RNA-property described by this selected element is an animatable prop */
1859 if (prop && RNA_property_anim_editable(&ptr, prop)) {
1860 /* get id + path + index info from the selected element */
1861 tree_element_to_path(te, tselem, &id, &path, &array_index, &flag, &groupmode);
1862 }
1863
1864 /* only if ID and path were set, should we perform any actions */
1865 if (id && path) {
1866 short dflags = CREATEDRIVER_WITH_DEFAULT_DVAR;
1867 int arraylen = 1;
1868
1869 /* array checks */
1870 if (flag & KSP_FLAG_WHOLE_ARRAY) {
1871 /* entire array was selected, so add drivers for all */
1872 arraylen = RNA_property_array_length(&ptr, prop);
1873 }
1874 else {
1875 arraylen = array_index;
1876 }
1877
1878 /* we should do at least one step */
1879 if (arraylen == array_index) {
1880 arraylen++;
1881 }
1882
1883 /* for each array element we should affect, add driver */
1884 for (; array_index < arraylen; array_index++) {
1885 /* action depends on mode */
1886 switch (mode) {
1887 case DRIVERS_EDITMODE_ADD: {
1888 /* add a new driver with the information obtained (only if valid) */
1889 ANIM_add_driver(reports, id, path, array_index, dflags, DRIVER_TYPE_PYTHON);
1890 break;
1891 }
1893 ANIM_remove_driver(id, path, array_index);
1894 break;
1895 }
1896 }
1897 }
1898
1899 /* free path, since it had to be generated */
1900 MEM_freeN(path);
1901 }
1902 });
1903}
1904
1907/* -------------------------------------------------------------------- */
1912{
1913 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
1914
1915 /* check for invalid states */
1916 if (space_outliner == nullptr) {
1917 return OPERATOR_CANCELLED;
1918 }
1919
1920 /* recursively go into tree, adding selected items */
1922
1923 /* send notifiers */
1924 WM_event_add_notifier(C, NC_ANIMATION | ND_FCURVES_ORDER, nullptr); /* XXX */
1925
1926 return OPERATOR_FINISHED;
1927}
1928
1930{
1931 /* api callbacks */
1932 ot->idname = "OUTLINER_OT_drivers_add_selected";
1933 ot->name = "Add Drivers for Selected";
1934 ot->description = "Add drivers to selected items";
1935
1936 /* api callbacks */
1939
1940 /* flags */
1942}
1943
1946/* -------------------------------------------------------------------- */
1951{
1952 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
1953
1954 /* check for invalid states */
1955 if (space_outliner == nullptr) {
1956 return OPERATOR_CANCELLED;
1957 }
1958
1959 /* recursively go into tree, adding selected items */
1961
1962 /* send notifiers */
1963 WM_event_add_notifier(C, ND_KEYS, nullptr); /* XXX */
1964
1965 return OPERATOR_FINISHED;
1966}
1967
1969{
1970 /* identifiers */
1971 ot->idname = "OUTLINER_OT_drivers_delete_selected";
1972 ot->name = "Delete Drivers for Selected";
1973 ot->description = "Delete drivers assigned to selected items";
1974
1975 /* api callbacks */
1978
1979 /* flags */
1981}
1982
1985/* -------------------------------------------------------------------- */
1995enum {
1998} /*eKeyingSet_EditModes*/;
1999
2000/* Find the 'active' KeyingSet, and add if not found (if adding is allowed). */
2001/* TODO: should this be an API func? */
2002static KeyingSet *verify_active_keyingset(Scene *scene, short add)
2003{
2004 KeyingSet *ks = nullptr;
2005
2006 /* sanity check */
2007 if (scene == nullptr) {
2008 return nullptr;
2009 }
2010
2011 /* try to find one from scene */
2012 if (scene->active_keyingset > 0) {
2013 ks = static_cast<KeyingSet *>(BLI_findlink(&scene->keyingsets, scene->active_keyingset - 1));
2014 }
2015
2016 /* Add if none found */
2017 /* XXX the default settings have yet to evolve. */
2018 if ((add) && (ks == nullptr)) {
2019 ks = BKE_keyingset_add(&scene->keyingsets, nullptr, nullptr, KEYINGSET_ABSOLUTE, 0);
2020 scene->active_keyingset = BLI_listbase_count(&scene->keyingsets);
2021 }
2022
2023 return ks;
2024}
2025
2026/* Iterate over tree, finding and working on selected items */
2028 KeyingSet *ks,
2029 const short mode)
2030{
2031 tree_iterator::all_open(*space_outliner, [&](TreeElement *te) {
2032 TreeStoreElem *tselem = TREESTORE(te);
2033
2034 /* if item is selected, perform operation */
2035 if (!(tselem->flag & TSE_SELECTED)) {
2036 return;
2037 }
2038
2039 ID *id = nullptr;
2040 char *path = nullptr;
2041 int array_index = 0;
2042 short flag = 0;
2043 short groupmode = KSP_GROUP_KSNAME;
2044
2045 /* check if RNA-property described by this selected element is an animatable prop */
2047 PointerRNA ptr = te_rna->get_pointer_rna();
2048 if (te_rna && te_rna->get_property_rna() &&
2050 {
2051 /* get id + path + index info from the selected element */
2052 tree_element_to_path(te, tselem, &id, &path, &array_index, &flag, &groupmode);
2053 }
2054
2055 /* only if ID and path were set, should we perform any actions */
2056 if (id && path) {
2057 /* action depends on mode */
2058 switch (mode) {
2060 /* add a new path with the information obtained (only if valid) */
2061 /* TODO: what do we do with group name?
2062 * for now, we don't supply one, and just let this use the KeyingSet name */
2063 BKE_keyingset_add_path(ks, id, nullptr, path, array_index, flag, groupmode);
2065 break;
2066 }
2068 /* find the relevant path, then remove it from the KeyingSet */
2069 KS_Path *ksp = BKE_keyingset_find_path(ks, id, nullptr, path, array_index, groupmode);
2070
2071 if (ksp) {
2072 /* free path's data */
2073 BKE_keyingset_free_path(ks, ksp);
2074
2075 ks->active_path = 0;
2076 }
2077 break;
2078 }
2079 }
2080
2081 /* free path, since it had to be generated */
2082 MEM_freeN(path);
2083 }
2084 });
2085}
2086
2089/* -------------------------------------------------------------------- */
2094{
2095 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
2096 Scene *scene = CTX_data_scene(C);
2097 KeyingSet *ks = verify_active_keyingset(scene, 1);
2098
2099 /* check for invalid states */
2100 if (ks == nullptr) {
2101 BKE_report(op->reports, RPT_ERROR, "Operation requires an active keying set");
2102 return OPERATOR_CANCELLED;
2103 }
2104 if (space_outliner == nullptr) {
2105 return OPERATOR_CANCELLED;
2106 }
2107
2108 /* recursively go into tree, adding selected items */
2110
2111 /* send notifiers */
2113
2114 return OPERATOR_FINISHED;
2115}
2116
2118{
2119 /* identifiers */
2120 ot->idname = "OUTLINER_OT_keyingset_add_selected";
2121 ot->name = "Keying Set Add Selected";
2122 ot->description = "Add selected items (blue-gray rows) to active Keying Set";
2123
2124 /* api callbacks */
2127
2128 /* flags */
2130}
2131
2134/* -------------------------------------------------------------------- */
2139{
2140 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
2141 Scene *scene = CTX_data_scene(C);
2142 KeyingSet *ks = verify_active_keyingset(scene, 1);
2143
2144 /* check for invalid states */
2145 if (space_outliner == nullptr) {
2146 return OPERATOR_CANCELLED;
2147 }
2148
2149 /* recursively go into tree, adding selected items */
2151
2152 /* send notifiers */
2154
2155 return OPERATOR_FINISHED;
2156}
2157
2159{
2160 /* identifiers */
2161 ot->idname = "OUTLINER_OT_keyingset_remove_selected";
2162 ot->name = "Keying Set Remove Selected";
2163 ot->description = "Remove selected items (blue-gray rows) from active Keying Set";
2164
2165 /* api callbacks */
2168
2169 /* flags */
2171}
2172
2175/* -------------------------------------------------------------------- */
2180{
2181 ScrArea *area = CTX_wm_area(C);
2182 if (area != nullptr && area->spacetype == SPACE_OUTLINER) {
2183 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
2184 return (space_outliner->outlinevis == SO_ID_ORPHANS);
2185 }
2186 return true;
2187}
2188
2189static void unused_message_gen(std::string &message,
2190 const std::array<int, INDEX_ID_MAX> &num_tagged)
2191{
2192 bool is_first = true;
2193 if (num_tagged[INDEX_ID_NULL] == 0) {
2194 message += IFACE_("None");
2195 return;
2196 }
2197
2198 /* NOTE: Index is looped in reversed order, since typically 'higher level' IDs (like Collections
2199 * or Objects) have a higher index than 'lower level' ones like object data, materials, etc.
2200 *
2201 * It makes more sense to present to the user the deleted numbers of Collections or Objects
2202 * before the ones for object data or Materials. */
2203 for (int i = INDEX_ID_MAX - 2; i >= 0; i--) {
2204 if (num_tagged[i] != 0) {
2205 message += fmt::format(
2206 "{}{} {}",
2207 (is_first) ? "" : ", ",
2208 num_tagged[i],
2209 (num_tagged[i] > 1) ?
2212 is_first = false;
2213 }
2214 }
2215}
2216
2218{
2219 /* Computation of unused data amounts, with all options ON.
2220 * Used to estimate the maximum required width for the dialog. */
2221 Main *bmain = CTX_data_main(C);
2223 data.do_local_ids = true;
2224 data.do_linked_ids = true;
2225 data.do_recursive = true;
2227
2228 std::string unused_message = "";
2229 const uiStyle *style = UI_style_get_dpi();
2230 unused_message_gen(unused_message, data.num_local);
2231 float max_messages_width = BLF_width(
2232 style->widget.uifont_id, unused_message.c_str(), BLF_DRAW_STR_DUMMY_MAX);
2233
2234 unused_message = "";
2235 unused_message_gen(unused_message, data.num_linked);
2236 max_messages_width = std::max(
2237 max_messages_width,
2238 BLF_width(style->widget.uifont_id, unused_message.c_str(), BLF_DRAW_STR_DUMMY_MAX));
2239
2240 return int(std::max(max_messages_width, 300.0f));
2241}
2242
2244{
2245 if (op->customdata) {
2246 MEM_delete(static_cast<LibQueryUnusedIDsData *>(op->customdata));
2247 op->customdata = nullptr;
2248 }
2249}
2250
2252{
2253 Main *bmain = CTX_data_main(C);
2254 LibQueryUnusedIDsData &data = *static_cast<LibQueryUnusedIDsData *>(op->customdata);
2255
2256 data.do_local_ids = RNA_boolean_get(op->ptr, "do_local_ids");
2257 data.do_linked_ids = RNA_boolean_get(op->ptr, "do_linked_ids");
2258 data.do_recursive = RNA_boolean_get(op->ptr, "do_recursive");
2259
2261
2262 /* Always assume count changed, and request a redraw. */
2263 return true;
2264}
2265
2266static int outliner_orphans_purge_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
2267{
2268 op->customdata = MEM_new<LibQueryUnusedIDsData>(__func__);
2269
2270 /* Compute expected amounts of deleted IDs and store them in 'cached' operator properties. */
2272
2274 op,
2276 IFACE_("Purge Unused Data from This File"),
2277 IFACE_("Delete"));
2278}
2279
2281{
2282 Main *bmain = CTX_data_main(C);
2283 ScrArea *area = CTX_wm_area(C);
2284 SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
2285
2286 if (!op->customdata) {
2287 op->customdata = MEM_new<LibQueryUnusedIDsData>(__func__);
2288 }
2289 LibQueryUnusedIDsData &data = *static_cast<LibQueryUnusedIDsData *>(op->customdata);
2290
2291 data.do_local_ids = RNA_boolean_get(op->ptr, "do_local_ids");
2292 data.do_linked_ids = RNA_boolean_get(op->ptr, "do_linked_ids");
2293 data.do_recursive = RNA_boolean_get(op->ptr, "do_recursive");
2294
2295 /* Tag all IDs to delete. */
2297
2298 if (data.num_total[INDEX_ID_NULL] == 0) {
2299 BKE_report(op->reports, RPT_INFO, "No orphaned data-blocks to purge");
2300 MEM_delete(static_cast<LibQueryUnusedIDsData *>(op->customdata));
2301 op->customdata = nullptr;
2302 return OPERATOR_CANCELLED;
2303 }
2304
2306
2307 BKE_reportf(op->reports, RPT_INFO, "Deleted %d data-block(s)", data.num_total[INDEX_ID_NULL]);
2308
2309 /* XXX: tree management normally happens from draw_outliner(), but when
2310 * you're clicking to fast on Delete object from context menu in
2311 * outliner several mouse events can be handled in one cycle without
2312 * handling notifiers/redraw which leads to deleting the same object twice.
2313 * cleanup tree here to prevent such cases. */
2314 if ((area != nullptr) && (area->spacetype == SPACE_OUTLINER)) {
2315 outliner_cleanup_tree(space_outliner);
2316 }
2317
2319 WM_event_add_notifier(C, NC_ID | NA_REMOVED, nullptr);
2320 /* Force full redraw of the UI. */
2322
2324
2325 return OPERATOR_FINISHED;
2326}
2327
2332
2334{
2335 uiLayout *layout = op->layout;
2336 PointerRNA *ptr = op->ptr;
2337 if (!op->customdata) {
2338 /* This should only happen on 'adjust last operation' case, since `invoke` will not have been
2339 * called then before showing the UI (the 'redo panel' UI uses WM-stored operator properties
2340 * and a newly-created operator).
2341 *
2342 * Since that operator is not 'registered' for adjusting from undo stack, this should never
2343 * happen currently. */
2345 op->customdata = MEM_new<LibQueryUnusedIDsData>(__func__);
2346 }
2347 LibQueryUnusedIDsData &data = *static_cast<LibQueryUnusedIDsData *>(op->customdata);
2348
2349 std::string unused_message = "";
2350 unused_message_gen(unused_message, data.num_local);
2351 uiLayout *column = uiLayoutColumn(layout, true);
2352 uiItemR(column, ptr, "do_local_ids", UI_ITEM_NONE, nullptr, ICON_NONE);
2353 uiLayout *row = uiLayoutRow(column, true);
2354 uiItemS_ex(row, 2.67f);
2355 uiItemL(row, unused_message.c_str(), ICON_NONE);
2356
2357 unused_message = "";
2358 unused_message_gen(unused_message, data.num_linked);
2359 column = uiLayoutColumn(layout, true);
2360 uiItemR(column, ptr, "do_linked_ids", UI_ITEM_NONE, nullptr, ICON_NONE);
2361 row = uiLayoutRow(column, true);
2362 uiItemS_ex(row, 2.67f);
2363 uiItemL(row, unused_message.c_str(), ICON_NONE);
2364
2365 uiItemR(layout, ptr, "do_recursive", UI_ITEM_NONE, nullptr, ICON_NONE);
2366}
2367
2369{
2370 /* identifiers */
2371 ot->idname = "OUTLINER_OT_orphans_purge";
2372 ot->name = "Purge All";
2373 ot->description = "Clear all orphaned data-blocks without any users from the file";
2374
2375 /* callbacks */
2379
2383
2384 /* flags */
2385 /* NOTE: No #OPTYPE_REGISTER, since this operator should not be 'adjustable'. */
2386 ot->flag = OPTYPE_UNDO;
2387
2388 /* Actual user-visible settings. */
2390 "do_local_ids",
2391 true,
2392 "Local Data-blocks",
2393 "Include unused local data-blocks into deletion");
2395 "do_linked_ids",
2396 true,
2397 "Linked Data-blocks",
2398 "Include unused linked data-blocks into deletion");
2399
2401 "do_recursive",
2402 true,
2403 "Recursive Delete",
2404 "Recursively check for indirectly unused data-blocks, ensuring that no orphaned "
2405 "data-blocks remain after execution");
2406}
2407
2410/* -------------------------------------------------------------------- */
2414static int outliner_orphans_manage_invoke(bContext *C, wmOperator * /*op*/, const wmEvent *event)
2415{
2416 const int sizex = int(450.0f * UI_SCALE_FAC);
2417 const int sizey = int(450.0f * UI_SCALE_FAC);
2418 const rcti window_rect = {
2419 /*xmin*/ event->xy[0],
2420 /*xmax*/ event->xy[0] + sizex,
2421 /*ymin*/ event->xy[1],
2422 /*ymax*/ event->xy[1] + sizey,
2423 };
2424
2425 if (WM_window_open(C,
2426 IFACE_("Manage Unused Data"),
2427 &window_rect,
2429 false,
2430 false,
2431 true,
2433 nullptr,
2434 nullptr) != nullptr)
2435 {
2436 SpaceOutliner *soutline = CTX_wm_space_outliner(C);
2437 soutline->outlinevis = SO_ID_ORPHANS;
2438 return OPERATOR_FINISHED;
2439 }
2440 return OPERATOR_CANCELLED;
2441}
2442
2444{
2445 /* identifiers */
2446 ot->idname = "OUTLINER_OT_orphans_manage";
2447 ot->name = "Manage Unused Data";
2448 ot->description = "Open a window to manage unused data";
2449
2450 /* callbacks */
2452
2453 /* flags */
2455}
2456
2459} // namespace blender::ed::outliner
Blender kernel action and pose functionality.
bPoseChannel * BKE_pose_channel_active(Object *ob, bool check_bonecoll)
struct KS_Path * BKE_keyingset_find_path(struct KeyingSet *ks, struct ID *id, const char group_name[], const char rna_path[], int array_index, int group_mode)
Definition anim_sys.cc:84
struct KeyingSet * BKE_keyingset_add(struct ListBase *list, const char idname[], const char name[], short flag, short keyingflag)
Definition anim_sys.cc:134
struct KS_Path * BKE_keyingset_add_path(struct KeyingSet *ks, struct ID *id, const char group_name[], const char rna_path[], int array_index, short flag, short groupmode)
Definition anim_sys.cc:164
void BKE_keyingset_free_path(struct KeyingSet *ks, struct KS_Path *ksp)
Definition anim_sys.cc:227
const char * BKE_tempdir_base() ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL
Definition appdir.cc:1211
int BKE_copybuffer_paste(bContext *C, const char *libname, int flag, ReportList *reports, uint64_t id_types_mask)
SpaceOutliner * CTX_wm_space_outliner(const bContext *C)
ScrArea * CTX_wm_area(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
EditBone * CTX_data_active_bone(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
short BKE_idtype_index_to_idcode(int idtype_index)
Definition idtype.cc:347
const char * BKE_idtype_idcode_to_name(short idcode)
Definition idtype.cc:168
const char * BKE_idtype_idcode_to_name_plural(short idcode)
Definition idtype.cc:175
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
Object * BKE_view_layer_active_object_get(const ViewLayer *view_layer)
void size_t BKE_id_multi_tagged_delete(Main *bmain) ATTR_NONNULL()
void BKE_main_id_tag_idcode(Main *mainvar, short type, int tag, bool value)
Definition lib_id.cc:1191
void BKE_main_lib_objects_recalc_all(Main *bmain)
Definition lib_id.cc:1267
void BKE_main_id_tag_all(Main *mainvar, int tag, bool value)
Definition lib_id.cc:1198
void BKE_lib_query_unused_ids_tag(Main *bmain, int tag, LibQueryUnusedIDsData &parameters)
Definition lib_query.cc:962
bool BKE_library_ID_is_indirectly_used(Main *bmain, void *idv)
Definition lib_query.cc:613
void BKE_lib_query_unused_ids_amounts(Main *bmain, LibQueryUnusedIDsData &parameters)
Definition lib_query.cc:904
@ ID_REMAP_SKIP_NEVER_NULL_USAGE
@ ID_REMAP_SKIP_INDIRECT_USAGE
void void BKE_libblock_remap(Main *bmain, void *old_idv, void *new_idv, int remap_flags) ATTR_NONNULL(1
ListBase * which_libbase(Main *bmain, short type)
Definition main.cc:842
const char * BKE_main_blendfile_path(const Main *bmain) ATTR_NONNULL()
Definition main.cc:832
General operations, lookup, etc. for blender objects.
Object * BKE_object_pose_armature_get(Object *ob)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
ARegion * BKE_area_find_region_type(const ScrArea *area, int region_type)
Definition screen.cc:815
void BKE_workspace_id_tag_all_visible(Main *bmain, int tag) ATTR_NONNULL()
Definition workspace.cc:546
#define BLF_DRAW_STR_DUMMY_MAX
Definition BLF_api.hh:393
float BLF_width(int fontid, const char *str, size_t str_len, ResultBLF *r_info=nullptr) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(2)
Definition blf.cc:791
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
A dynamically sized string ADT.
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
void BLI_addhead(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:90
#define LISTBASE_FOREACH(type, var, list)
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:496
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
#define FILE_MAX
#define BLI_path_join(...)
void BLI_path_split_dir_file(const char *filepath, char *dir, size_t dir_maxncpy, char *file, size_t file_maxncpy) ATTR_NONNULL(1
#define FILE_MAXDIR
#define BLI_path_cmp
BLI_INLINE int BLI_rcti_size_y(const struct rcti *rct)
Definition BLI_rect.h:193
#define UNUSED_VARS_NDEBUG(...)
#define ELEM(...)
#define BLT_I18NCONTEXT_ID_ID
#define IFACE_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_relations_tag_update(Main *bmain)
ID and Library types, which are fundamental for SDNA.
@ ID_RECALC_SELECT
Definition DNA_ID.h:1068
#define ID_IS_OVERRIDE_LIBRARY_REAL(_id)
Definition DNA_ID.h:676
#define ID_IS_EDITABLE(_id)
Definition DNA_ID.h:658
@ ID_TAG_INDIRECT
Definition DNA_ID.h:794
@ ID_TAG_PRE_EXISTING
Definition DNA_ID.h:872
@ ID_TAG_DOIT
Definition DNA_ID.h:1003
#define INDEX_ID_MAX
Definition DNA_ID.h:1328
@ LIBOVERRIDE_FLAG_NO_HIERARCHY
Definition DNA_ID.h:362
@ INDEX_ID_NULL
Definition DNA_ID.h:1325
#define ID_REAL_USERS(id)
Definition DNA_ID.h:637
#define ID_IS_OVERRIDE_LIBRARY(_id)
Definition DNA_ID.h:683
@ ID_LI
@ ID_WS
@ ID_SCE
@ ID_OB
@ KEYINGSET_ABSOLUTE
@ DRIVER_TYPE_PYTHON
@ KSP_GROUP_KSNAME
@ KSP_FLAG_WHOLE_ARRAY
Object groups, one object can be in many groups at once.
@ COLLECTION_IS_MASTER
@ OB_MODE_EDIT
@ OB_MODE_POSE
Object is a sort of wrapper for general info.
@ OB_ARMATURE
#define TSE_IS_REAL_ID(_tse)
@ TSE_CONSTRAINT_BASE
@ TSE_MODIFIER_BASE
@ TSE_SEQUENCE_DUP
@ TSE_RNA_ARRAY_ELEM
@ TSE_SEQUENCE
@ TSE_VIEW_COLLECTION_BASE
@ TSE_ANIM_DATA
@ TSE_RNA_PROPERTY
@ TSE_LIBRARY_OVERRIDE_BASE
@ TSE_DEFGROUP_BASE
@ TSE_SCENE_COLLECTION_BASE
@ TSE_SCENE_OBJECTS_BASE
@ TSE_R_LAYER_BASE
@ TSE_LAYER_COLLECTION
@ TSE_SEQ_STRIP
@ TSE_GENERIC_LABEL
@ TSE_NLA
@ TSE_ID_BASE
@ TSE_SOME_ID
@ TSE_DRIVER_BASE
@ TSE_BONE_COLLECTION_BASE
@ TSE_RNA_STRUCT
@ TSE_POSE_BASE
@ TSE_HIGHLIGHTED_ANY
@ TSE_SELECTED
@ TSE_CLOSED
@ TSE_HIGHLIGHTED_ICON
@ TSE_HIGHLIGHTED
@ TSE_DRAG_ANY
@ TSE_ACTIVE
@ TSE_TEXTBUT
@ RGN_TYPE_HEADER
@ SPACE_OUTLINER
@ FILE_ACTIVE_COLLECTION
@ FILE_AUTOSELECT
@ SO_DATA_API
@ SO_ID_ORPHANS
#define UI_SCALE_FAC
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
@ RPT_ERROR_INVALID_INPUT
@ CREATEDRIVER_WITH_DEFAULT_DVAR
void ED_outliner_select_sync_from_outliner(bContext *C, SpaceOutliner *space_outliner)
bool ED_operator_region_outliner_active(bContext *C)
void ED_area_tag_redraw(ScrArea *area)
Definition area.cc:708
int ED_screen_area_active(const bContext *C)
void ED_region_tag_redraw_no_rebuild(ARegion *region)
Definition area.cc:653
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:634
bool ED_operator_outliner_active(bContext *C)
@ SEL_SELECT
@ SEL_INVERT
@ SEL_DESELECT
@ SEL_TOGGLE
void GPU_materials_free(Main *bmain)
Read Guarded memory(de)allocation.
@ PROP_POINTER
Definition RNA_types.hh:70
@ PROP_COLLECTION
Definition RNA_types.hh:71
PropertyFlag
Definition RNA_types.hh:201
@ PROP_ENUM_NO_TRANSLATE
Definition RNA_types.hh:321
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
@ PROP_HIDDEN
Definition RNA_types.hh:239
constexpr PointerRNA PointerRNA_NULL
Definition RNA_types.hh:45
#define UI_UNIT_Y
bool UI_textbutton_activate_rna(const bContext *C, ARegion *region, const void *rna_poin_data, const char *rna_prop_id)
void uiItemL(uiLayout *layout, const char *name, int icon)
uiLayout * uiLayoutRow(uiLayout *layout, bool align)
const uiStyle * UI_style_get_dpi()
void uiItemS_ex(uiLayout *layout, float factor, LayoutSeparatorType type=LayoutSeparatorType::Auto)
#define UI_ITEM_NONE
uiLayout * uiLayoutColumn(uiLayout *layout, bool align)
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
void UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
Definition view2d.cc:1663
@ WIN_ALIGN_LOCATION_CENTER
Definition WM_api.hh:340
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_WINDOW
Definition WM_types.hh:342
#define NC_ID
Definition WM_types.hh:362
#define NC_ANIMATION
Definition WM_types.hh:355
@ KM_CLICK_DRAG
Definition WM_types.hh:292
@ KM_RELEASE
Definition WM_types.hh:285
#define ND_OB_SELECT
Definition WM_types.hh:409
#define ND_KEYINGSET
Definition WM_types.hh:415
#define NC_SCENE
Definition WM_types.hh:345
#define ND_FCURVES_ORDER
Definition WM_types.hh:466
#define NA_REMOVED
Definition WM_types.hh:553
@ WM_OP_INVOKE_DEFAULT
Definition WM_types.hh:218
@ WM_OP_EXEC_DEFAULT
Definition WM_types.hh:225
#define ND_KEYS
Definition WM_types.hh:430
ID * id_add(const ID *id, IDAddOptions options, blender::FunctionRef< IDAddOperations(LibraryIDLinkCallbackData *cb_data, IDAddOptions options)> dependencies_filter_cb=nullptr)
virtual PropertyRNA * get_property_rna() const
#define printf
int ANIM_add_driver(ReportList *reports, ID *id, const char rna_path[], int array_index, short flag, int type)
Main Driver Management API calls.
Definition drivers.cc:399
bool ANIM_remove_driver(ID *id, const char rna_path[], int array_index)
Main Driver Management API calls.
Definition drivers.cc:523
KDTree_3d * tree
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
#define GS(x)
Definition iris.cc:202
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void all_open(const SpaceOutliner &space_outliner, const VisitorFn visitor)
void all(const SpaceOutliner &space_outliner, const VisitorFn visitor)
void OUTLINER_OT_expanded_toggle(wmOperatorType *ot)
static int outliner_highlight_update_invoke(bContext *C, wmOperator *, const wmEvent *event)
bool outliner_item_is_co_within_close_toggle(const TreeElement *te, float view_co_x)
int outliner_flag_is_any_test(ListBase *lb, short flag, const int curlevel)
static void outliner_openclose_level(ListBase *lb, int curlevel, int level, int open)
static void tree_element_show_hierarchy(Scene *scene, SpaceOutliner *space_outliner)
static int outliner_select_all_exec(bContext *C, wmOperator *op)
void outliner_tag_redraw_avoid_rebuild_on_open_change(const SpaceOutliner *space_outliner, ARegion *region)
void OUTLINER_OT_item_rename(wmOperatorType *ot)
static int outliner_keyingset_additems_exec(bContext *C, wmOperator *op)
static int subtree_has_objects(ListBase *lb)
static TreeElement * outliner_item_rename_find_hovered(const SpaceOutliner *space_outliner, ARegion *region, const wmEvent *event)
static void do_outliner_keyingset_editop(SpaceOutliner *space_outliner, KeyingSet *ks, const short mode)
void OUTLINER_OT_show_hierarchy(wmOperatorType *ot)
TreeElement * outliner_find_posechannel(ListBase *lb, const bPoseChannel *pchan)
static int outliner_drivers_deletesel_exec(bContext *C, wmOperator *op)
void OUTLINER_OT_highlight_update(wmOperatorType *ot)
static int outliner_id_delete_tag(bContext *C, ReportList *reports, TreeElement *te, const float mval[2])
bool outliner_is_collection_tree_element(const TreeElement *te)
static TreeElement * outliner_show_active_get_element(bContext *C, SpaceOutliner *space_outliner, const Scene *scene, ViewLayer *view_layer)
void OUTLINER_OT_drivers_add_selected(wmOperatorType *ot)
void OUTLINER_OT_select_all(wmOperatorType *ot)
TreeElement * outliner_find_item_at_x_in_row(const SpaceOutliner *space_outliner, TreeElement *parent_te, float view_co_x, bool *r_is_merged_icon, bool *r_is_over_icon)
void OUTLINER_OT_lib_relocate(wmOperatorType *ot)
static void do_outliner_drivers_editop(SpaceOutliner *space_outliner, ReportList *reports, short mode)
TreeElement * outliner_find_id(SpaceOutliner *space_outliner, ListBase *lb, const ID *id)
static void outliner_orphans_purge_ui(bContext *, wmOperator *op)
static int outliner_id_copy_tag(SpaceOutliner *space_outliner, ListBase *tree, blender::bke::blendfile::PartialWriteContext &copybuffer)
void id_delete_tag_fn(bContext *C, ReportList *reports, Scene *, TreeElement *te, TreeStoreElem *, TreeStoreElem *tselem)
static int outliner_clear_filter_exec(bContext *C, wmOperator *)
static int outliner_open_back(TreeElement *te)
static int unused_message_popup_width_compute(bContext *C)
static void outliner_orphans_purge_cancel(bContext *, wmOperator *op)
static int outliner_show_active_exec(bContext *C, wmOperator *)
bool outliner_flag_set(const SpaceOutliner &space_outliner, const short flag, const short set)
static int outliner_id_delete_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static const EnumPropertyItem * outliner_id_itemf(bContext *C, PointerRNA *ptr, PropertyRNA *, bool *r_free)
TreeElement * outliner_find_item_at_y(const SpaceOutliner *space_outliner, const ListBase *tree, float view_co_y)
void OUTLINER_OT_id_paste(wmOperatorType *ot)
static int outliner_one_level_exec(bContext *C, wmOperator *op)
void OUTLINER_OT_clear_filter(wmOperatorType *ot)
static int outliner_id_remap_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static int outliner_id_paste_exec(bContext *C, wmOperator *op)
void outliner_set_coordinates(const ARegion *region, const SpaceOutliner *space_outliner)
static void id_delete_tag(bContext *C, ReportList *reports, TreeElement *te, TreeStoreElem *tselem)
void OUTLINER_OT_start_filter(wmOperatorType *ot)
static int outliner_id_remap_exec(bContext *C, wmOperator *op)
void OUTLINER_OT_drivers_delete_selected(wmOperatorType *ot)
void OUTLINER_OT_show_one_level(wmOperatorType *ot)
static int lib_relocate(bContext *C, TreeElement *te, TreeStoreElem *tselem, wmOperatorType *ot, const bool reload)
void OUTLINER_OT_show_active(wmOperatorType *ot)
static bool outliner_id_remap_find_tree_element(bContext *C, wmOperator *op, ListBase *tree, const float y)
void OUTLINER_OT_id_copy(wmOperatorType *ot)
static void outliner_show_active(SpaceOutliner *space_outliner, ARegion *region, TreeElement *te, ID *id)
static int outliner_lib_relocate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void OUTLINER_OT_orphans_manage(wmOperatorType *ot)
TreeElement * outliner_find_editbone(ListBase *lb, const EditBone *ebone)
Collection * outliner_collection_from_tree_element(const TreeElement *te)
static void tree_element_to_path(TreeElement *te, TreeStoreElem *tselem, ID **id, char **path, int *array_index, short *flag, short *)
static int outliner_orphans_purge_exec(bContext *C, wmOperator *op)
void outliner_scroll_view(SpaceOutliner *space_outliner, ARegion *region, int delta_y)
bool outliner_flag_flip(const SpaceOutliner &space_outliner, const short flag)
void lib_relocate_fn(bContext *C, ReportList *, Scene *, TreeElement *te, TreeStoreElem *, TreeStoreElem *tselem)
static int outliner_show_hierarchy_exec(bContext *C, wmOperator *)
void OUTLINER_OT_lib_reload(wmOperatorType *ot)
static int outliner_id_copy_exec(bContext *C, wmOperator *op)
void outliner_item_openclose(TreeElement *te, bool open, bool toggle_all)
static int outliner_orphans_purge_invoke(bContext *C, wmOperator *op, const wmEvent *)
void OUTLINER_OT_scroll_page(wmOperatorType *ot)
void OUTLINER_OT_keyingset_remove_selected(wmOperatorType *ot)
void OUTLINER_OT_id_remap(wmOperatorType *ot)
void OUTLINER_OT_id_delete(wmOperatorType *ot)
TreeElementT * tree_element_cast(const TreeElement *te)
void OUTLINER_OT_keyingset_add_selected(wmOperatorType *ot)
static int outliner_scroll_page_exec(bContext *C, wmOperator *op)
static bool ed_operator_outliner_id_orphans_active(bContext *C)
static void do_item_rename(ARegion *region, TreeElement *te, TreeStoreElem *tselem, ReportList *reports)
static int outliner_item_openclose_modal(bContext *C, wmOperator *op, const wmEvent *event)
void id_remap_fn(bContext *C, ReportList *, Scene *, TreeElement *, TreeStoreElem *, TreeStoreElem *tselem)
static int outliner_start_filter_exec(bContext *C, wmOperator *)
static bool ed_operator_outliner_datablocks_active(bContext *C)
void lib_reload_fn(bContext *C, ReportList *, Scene *, TreeElement *te, TreeStoreElem *, TreeStoreElem *tselem)
void outliner_cleanup_tree(SpaceOutliner *space_outliner)
static int outliner_lib_reload_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void OUTLINER_OT_item_openclose(wmOperatorType *ot)
static void outliner_orphans_purge_cleanup(wmOperator *op)
bool outliner_item_is_co_over_name(const TreeElement *te, float view_co_x)
TreeElement * outliner_find_element_with_flag(const ListBase *lb, short flag)
static KeyingSet * verify_active_keyingset(Scene *scene, short add)
static int outliner_keyingset_removeitems_exec(bContext *C, wmOperator *)
static int outliner_item_rename_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static int outliner_toggle_expanded_exec(bContext *C, wmOperator *)
static int outliner_orphans_manage_invoke(bContext *C, wmOperator *, const wmEvent *event)
static int outliner_count_levels(ListBase *lb, const int curlevel)
void item_rename_fn(bContext *C, ReportList *reports, Scene *, TreeElement *te, TreeStoreElem *, TreeStoreElem *tselem)
static void outliner_copybuffer_filepath_get(char filepath[FILE_MAX], size_t filepath_maxncpy)
static void unused_message_gen(std::string &message, const std::array< int, INDEX_ID_MAX > &num_tagged)
static int outliner_item_openclose_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static int outliner_lib_relocate_invoke_do(bContext *C, ReportList *reports, TreeElement *te, const float mval[2], const bool reload)
static int outliner_drivers_addsel_exec(bContext *C, wmOperator *op)
static bool outliner_orphans_purge_check(bContext *C, wmOperator *op)
void OUTLINER_OT_orphans_purge(wmOperatorType *ot)
static TreeElement * outliner_item_rename_find_active(const SpaceOutliner *space_outliner, ReportList *reports)
#define TREESTORE(a)
return ret
const EnumPropertyItem rna_enum_id_type_items[]
Definition rna_ID.cc:35
bool RNA_property_array_check(PropertyRNA *prop)
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
bool RNA_struct_is_ID(const StructRNA *type)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
char * RNA_struct_name_get_alloc(PointerRNA *ptr, char *fixedbuf, int fixedlen, int *r_len)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
PropertyType RNA_property_type(PropertyRNA *prop)
void RNA_enum_set_identifier(bContext *C, PointerRNA *ptr, const char *name, const char *id)
bool RNA_property_anim_editable(const PointerRNA *ptr, PropertyRNA *prop_orig)
int RNA_property_array_length(PointerRNA *ptr, PropertyRNA *prop)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
void RNA_enum_set(PointerRNA *ptr, const char *name, int value)
int RNA_enum_get(PointerRNA *ptr, const char *name)
void RNA_def_property_enum_funcs_runtime(PropertyRNA *prop, EnumPropertyGetFunc getfunc, EnumPropertySetFunc setfunc, EnumPropertyItemFunc itemfunc)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
void RNA_enum_item_end(EnumPropertyItem **items, int *totitem)
void RNA_enum_item_add(EnumPropertyItem **items, int *totitem, const EnumPropertyItem *item)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_translation_context(PropertyRNA *prop, const char *context)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
char * RNA_path_append(const char *path, const PointerRNA *, PropertyRNA *prop, int intkey, const char *strkey)
Definition rna_path.cc:606
const EnumPropertyItem rna_enum_dummy_NULL_items[]
Definition rna_rna.cc:29
const char * identifier
Definition RNA_types.hh:506
const char * name
Definition RNA_types.hh:510
Definition DNA_ID.h:413
char name[66]
Definition DNA_ID.h:425
ListBase paths
void * data
void * first
StructRNA * type
Definition RNA_types.hh:41
void * data
Definition RNA_types.hh:42
char search_string[64]
float ymax
uiFontStyle widget
short val
Definition WM_types.hh:724
int mval[2]
Definition WM_types.hh:728
short type
Definition WM_types.hh:722
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1036
bool(* check)(bContext *C, wmOperator *op)
Definition WM_types.hh:1014
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
void(* ui)(bContext *C, wmOperator *op)
Definition WM_types.hh:1053
PropertyRNA * prop
Definition WM_types.hh:1092
StructRNA * srna
Definition WM_types.hh:1080
void(* cancel)(bContext *C, wmOperator *op)
Definition WM_types.hh:1028
struct ReportList * reports
struct uiLayout * layout
struct PointerRNA * ptr
static DynamicLibrary lib
void WM_event_drag_start_mval(const wmEvent *event, const ARegion *region, int r_mval[2])
void WM_main_add_notifier(uint type, void *reference)
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
int WM_operator_name_call_ptr(bContext *C, wmOperatorType *ot, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
@ MOUSEMOVE
@ LEFTMOUSE
PointerRNA * ptr
Definition wm_files.cc:4126
wmOperatorType * ot
Definition wm_files.cc:4125
void WM_operator_properties_select_all(wmOperatorType *ot)
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
int WM_operator_props_dialog_popup(bContext *C, wmOperator *op, int width, std::optional< std::string > title, std::optional< std::string > confirm_text, const bool cancel_default)
void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
void WM_operator_properties_free(PointerRNA *ptr)
wmWindow * WM_window_open(bContext *C, const char *title, const rcti *rect_unscaled, int space_type, bool toplevel, bool dialog, bool temp, eWindowAlignment alignment, void(*area_setup_fn)(bScreen *screen, ScrArea *area, void *user_data), void *area_setup_user_data)
Definition wm_window.cc:950
uint8_t flag
Definition wm_window.cc:138