Blender V5.0
spreadsheet_table.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2025 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BKE_viewer_path.hh"
6
7#include "BLI_hash.hh"
8#include "BLI_listbase.h"
9#include "BLI_string.h"
10
11#include "DNA_array_utils.hh"
12
13#include "BLO_read_write.hh"
14
15#include "spreadsheet_column.hh"
16#include "spreadsheet_table.hh"
17
19
21{
22 auto *table_id = MEM_callocN<SpreadsheetTableIDGeometry>(__func__);
23 table_id->base.type = SPREADSHEET_TABLE_ID_TYPE_GEOMETRY;
24 return table_id;
25}
26
43
45{
46 switch (eSpreadsheetTableIDType(src_table_id.type)) {
48 const auto &src = *reinterpret_cast<const SpreadsheetTableIDGeometry *>(&src_table_id);
49 auto *new_table_id = spreadsheet_table_id_new_geometry();
51 return &new_table_id->base;
52 }
53 }
54 return nullptr;
55}
56
58{
59 switch (eSpreadsheetTableIDType(table_id->type)) {
61 auto *table_id_ = reinterpret_cast<SpreadsheetTableIDGeometry *>(table_id);
62 BKE_viewer_path_clear(&table_id_->viewer_path);
63 MEM_SAFE_FREE(table_id_->instance_ids);
64 for (const int i : IndexRange(table_id_->bundle_path_num)) {
65 MEM_SAFE_FREE(table_id_->bundle_path[i].identifier);
66 }
67 MEM_SAFE_FREE(table_id_->bundle_path);
68 break;
69 }
70 }
71}
72
78
80 const SpreadsheetTableIDGeometry *table_id)
81{
82 BKE_viewer_path_blend_write(writer, &table_id->viewer_path);
84 writer, SpreadsheetInstanceID, table_id->instance_ids_num, table_id->instance_ids);
86 writer, SpreadsheetBundlePathElem, table_id->bundle_path_num, table_id->bundle_path);
87 for (const int i : IndexRange(table_id->bundle_path_num)) {
88 BLO_write_string(writer, table_id->bundle_path[i].identifier);
89 }
90}
91
93{
94 switch (eSpreadsheetTableIDType(table_id->type)) {
96 const auto *table_id_ = reinterpret_cast<const SpreadsheetTableIDGeometry *>(table_id);
99 break;
100 }
101 }
102}
103
105{
106 switch (eSpreadsheetTableIDType(table_id->type)) {
108 auto *table_id_ = reinterpret_cast<SpreadsheetTableIDGeometry *>(table_id);
109 BKE_viewer_path_blend_read_data(reader, &table_id_->viewer_path);
111 reader, SpreadsheetInstanceID, table_id_->instance_ids_num, &table_id_->instance_ids);
113 reader, SpreadsheetBundlePathElem, table_id_->bundle_path_num, &table_id_->bundle_path);
114 for (const int i : IndexRange(table_id_->bundle_path_num)) {
115 BLO_read_string(reader, &table_id_->bundle_path[i].identifier);
116 }
117 break;
118 }
119 }
120}
121
123 const bke::id::IDRemapper &mappings)
124{
125 switch (eSpreadsheetTableIDType(table_id.type)) {
127 auto *table_id_ = reinterpret_cast<SpreadsheetTableIDGeometry *>(&table_id);
128 BKE_viewer_path_id_remap(&table_id_->viewer_path, mappings);
129 break;
130 }
131 }
132}
133
135{
136 switch (eSpreadsheetTableIDType(table_id.type)) {
138 auto *table_id_ = reinterpret_cast<SpreadsheetTableIDGeometry *>(&table_id);
139 BKE_viewer_path_foreach_id(data, &table_id_->viewer_path);
140 break;
141 }
142 }
143}
144
146{
147 if (a.type != b.type) {
148 return false;
149 }
151 switch (type) {
153 const auto &a_ = *reinterpret_cast<const SpreadsheetTableIDGeometry *>(&a);
154 const auto &b_ = *reinterpret_cast<const SpreadsheetTableIDGeometry *>(&b);
156 &a_.viewer_path, &b_.viewer_path, VIEWER_PATH_EQUAL_FLAG_IGNORE_ITERATION) &&
157 a_.geometry_component_type == b_.geometry_component_type &&
158 a_.attribute_domain == b_.attribute_domain &&
159 a_.object_eval_state == b_.object_eval_state && a_.layer_index == b_.layer_index &&
160 blender::Span(a_.instance_ids, a_.instance_ids_num) ==
161 blender::Span(b_.instance_ids, b_.instance_ids_num) &&
162 blender::Span(a_.bundle_path, a_.bundle_path_num) ==
163 blender::Span(b_.bundle_path, b_.bundle_path_num);
164 }
165 }
166 return true;
167}
168
170{
171 SpreadsheetTable *spreadsheet_table = MEM_callocN<SpreadsheetTable>(__func__);
172 spreadsheet_table->id = table_id;
173 return spreadsheet_table;
174}
175
177{
179 new_table->num_columns = src_table.num_columns;
180 new_table->columns = MEM_calloc_arrayN<SpreadsheetColumn *>(src_table.num_columns, __func__);
181 for (const int i : IndexRange(src_table.num_columns)) {
182 new_table->columns[i] = spreadsheet_column_copy(src_table.columns[i]);
183 }
184 return new_table;
185}
186
188{
190 for (const int i : IndexRange(table->num_columns)) {
192 }
193 MEM_SAFE_FREE(table->columns);
194 MEM_freeN(table);
195}
196
198{
199 BLO_write_struct(writer, SpreadsheetTable, table);
200 spreadsheet_table_id_blend_write(writer, table->id);
201 BLO_write_pointer_array(writer, table->num_columns, table->columns);
202 for (const int i : IndexRange(table->num_columns)) {
204 }
205}
206
208{
209 BLO_read_struct(reader, SpreadsheetTableID, &table->id);
210 spreadsheet_table_id_blend_read(reader, table->id);
211 BLO_read_pointer_array(reader, table->num_columns, reinterpret_cast<void **>(&table->columns));
212 for (const int i : IndexRange(table->num_columns)) {
213 BLO_read_struct(reader, SpreadsheetColumn, &table->columns[i]);
214 spreadsheet_column_blend_read(reader, table->columns[i]);
215 }
216}
217
219{
220 spreadsheet_table_id_remap_id(*table.id, mappings);
221}
222
227
229 const SpreadsheetTableID &table_id)
230{
231 return const_cast<SpreadsheetTable *>(
232 spreadsheet_table_find(const_cast<const SpaceSpreadsheet &>(sspreadsheet), table_id));
233}
234
236 const SpreadsheetTableID &table_id)
237{
238 for (const SpreadsheetTable *table : Span{sspreadsheet.tables, sspreadsheet.num_tables}) {
239 if (spreadsheet_table_id_match(table_id, *table->id)) {
240 return table;
241 }
242 }
243 return nullptr;
244}
245
247{
249 sspreadsheet.num_tables + 1, __func__);
250 std::copy_n(sspreadsheet.tables, sspreadsheet.num_tables, new_tables);
251 new_tables[sspreadsheet.num_tables] = table;
252 MEM_SAFE_FREE(sspreadsheet.tables);
253 sspreadsheet.tables = new_tables;
254 sspreadsheet.num_tables++;
255}
256
258{
259 uint32_t min_last_used = 0;
260 const int max_tables = 50;
261 if (sspreadsheet.num_tables > max_tables) {
262 Vector<uint32_t> last_used_times;
263 for (const SpreadsheetTable *table : Span(sspreadsheet.tables, sspreadsheet.num_tables)) {
264 last_used_times.append(table->last_used);
265 }
266 std::sort(last_used_times.begin(), last_used_times.end());
267 min_last_used = last_used_times[sspreadsheet.num_tables - max_tables];
268 }
269
271 &sspreadsheet.tables,
272 &sspreadsheet.num_tables,
273 [&](const SpreadsheetTable *table) {
274 if (!(table->flag & SPREADSHEET_TABLE_FLAG_MANUALLY_EDITED)) {
275 /* Remove tables that have never been modified manually. Those can be rebuilt from
276 * scratch if necessary. */
277 return true;
278 }
279 if (table->last_used < min_last_used) {
280 /* The table has not been used for a while and there are too many unused tables. So
281 * garbage collect this table. This does remove user-edited column widths and orders, but
282 * doesn't remove any actual data. */
283 return true;
284 }
286 case SPREADSHEET_TABLE_ID_TYPE_GEOMETRY: {
287 const SpreadsheetTableIDGeometry &table_id =
288 *reinterpret_cast<const SpreadsheetTableIDGeometry *>(table->id);
289 LISTBASE_FOREACH (ViewerPathElem *, elem, &table_id.viewer_path.path) {
290 if (elem->type == VIEWER_PATH_ELEM_TYPE_ID) {
291 const IDViewerPathElem &id_elem = reinterpret_cast<const IDViewerPathElem &>(
292 *elem);
293 if (!id_elem.id) {
294 /* Remove tables which reference an ID that does not exist anymore. */
295 return true;
296 }
297 }
298 }
299 break;
300 }
301 }
302 return false;
303 },
304 [](SpreadsheetTable **table) { spreadsheet_table_free(*table); });
305}
306
308{
309 /* Might not be reached exactly if there are many columns with the same last used time. */
310 const int max_unavailable_columns_target = 50;
311 int num_unavailable_columns = 0;
312 for (SpreadsheetColumn *column : Span(table.columns, table.num_columns)) {
313 if (!column->is_available()) {
314 num_unavailable_columns++;
315 }
316 }
317 if (num_unavailable_columns <= max_unavailable_columns_target) {
318 /* No need to remove columns. */
319 return;
320 }
321
322 /* Find the threshold time for unavailable columns to remove. */
323 Vector<uint32_t> last_used_times;
324 for (SpreadsheetColumn *column : Span(table.columns, table.num_columns)) {
325 if (!column->is_available()) {
326 last_used_times.append(column->last_used);
327 }
328 }
329 std::sort(last_used_times.begin(), last_used_times.end());
330 const int min_last_used = last_used_times[max_unavailable_columns_target];
331
333 &table.columns,
334 &table.num_columns,
335 [&](const SpreadsheetColumn *column) {
336 if (column->is_available()) {
337 /* Available columns should never be removed here. */
338 return false;
339 }
340 if (column->last_used > min_last_used) {
341 /* Columns that have been used recently are not removed. */
342 return false;
343 }
344 return true;
345 },
346 [](SpreadsheetColumn **column) { spreadsheet_column_free(*column); });
347}
348
350{
351 const int old_index = Span(sspreadsheet.tables, sspreadsheet.num_tables).first_index(&table);
352 dna::array::move_index(sspreadsheet.tables, sspreadsheet.num_tables, old_index, 0);
353}
354
355} // namespace blender::ed::spreadsheet
void BKE_viewer_path_copy(ViewerPath *dst, const ViewerPath *src)
void BKE_viewer_path_id_remap(ViewerPath *viewer_path, const blender::bke::id::IDRemapper &mappings)
@ VIEWER_PATH_EQUAL_FLAG_IGNORE_ITERATION
void BKE_viewer_path_foreach_id(LibraryForeachIDData *data, ViewerPath *viewer_path)
void BKE_viewer_path_clear(ViewerPath *viewer_path)
void BKE_viewer_path_blend_read_data(BlendDataReader *reader, ViewerPath *viewer_path)
bool BKE_viewer_path_equal(const ViewerPath *a, const ViewerPath *b, ViewerPathEqualFlag flag=ViewerPathEqualFlag(0))
void BKE_viewer_path_blend_write(BlendWriter *writer, const ViewerPath *viewer_path)
char * BLI_strdup_null(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_MALLOC
Definition string.cc:46
#define BLO_write_struct(writer, struct_name, data_ptr)
void BLO_read_string(BlendDataReader *reader, char **ptr_p)
Definition readfile.cc:5828
void BLO_write_string(BlendWriter *writer, const char *data_ptr)
#define BLO_write_struct_array(writer, struct_name, array_size, data_ptr)
#define BLO_read_struct_array(reader, struct_name, array_size, ptr_p)
#define BLO_read_struct(reader, struct_name, ptr_p)
void BLO_write_pointer_array(BlendWriter *writer, int64_t num, const void *data_ptr)
void BLO_read_pointer_array(BlendDataReader *reader, int64_t array_size, void **ptr_p)
Definition readfile.cc:5880
eSpreadsheetTableIDType
@ SPREADSHEET_TABLE_ID_TYPE_GEOMETRY
#define MEM_SAFE_FREE(v)
switch((BMIterType) itype)
BMesh const char void * data
constexpr int64_t first_index(const T &search_value) const
Definition BLI_span.hh:377
void append(const T &value)
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
void remove_if(T **items, int *items_num, FunctionRef< bool(const T &)> predicate, void(*destruct_item)(T *))
void move_index(T *items, const int items_num, const int from_index, const int to_index)
void spreadsheet_table_move_to_front(SpaceSpreadsheet &sspreadsheet, SpreadsheetTable &table)
void spreadsheet_table_id_free(SpreadsheetTableID *table_id)
void spreadsheet_table_blend_write(BlendWriter *writer, const SpreadsheetTable *table)
void spreadsheet_table_id_blend_write(BlendWriter *writer, const SpreadsheetTableID *table_id)
void spreadsheet_column_blend_write(BlendWriter *writer, const SpreadsheetColumn *column)
void spreadsheet_table_blend_read(BlendDataReader *reader, SpreadsheetTable *table)
void spreadsheet_table_remove_unused(SpaceSpreadsheet &sspreadsheet)
void spreadsheet_column_blend_read(BlendDataReader *reader, SpreadsheetColumn *column)
void spreadsheet_table_id_free_content(SpreadsheetTableID *table_id)
SpreadsheetTableID * spreadsheet_table_id_copy(const SpreadsheetTableID &src_table_id)
void spreadsheet_table_id_copy_content_geometry(SpreadsheetTableIDGeometry &dst, const SpreadsheetTableIDGeometry &src)
void spreadsheet_table_id_blend_write_content_geometry(BlendWriter *writer, const SpreadsheetTableIDGeometry *table_id)
void spreadsheet_table_id_blend_read(BlendDataReader *reader, SpreadsheetTableID *table_id)
void spreadsheet_table_remove_unused_columns(SpreadsheetTable &table)
SpreadsheetTableIDGeometry * spreadsheet_table_id_new_geometry()
void spreadsheet_table_id_foreach_id(SpreadsheetTableID &table_id, LibraryForeachIDData *data)
SpreadsheetTable * spreadsheet_table_new(SpreadsheetTableID *table_id)
void spreadsheet_column_free(SpreadsheetColumn *column)
void spreadsheet_table_free(SpreadsheetTable *table)
void spreadsheet_table_foreach_id(SpreadsheetTable &table, LibraryForeachIDData *data)
bool spreadsheet_table_id_match(const SpreadsheetTableID &a, const SpreadsheetTableID &b)
SpreadsheetTable * spreadsheet_table_copy(const SpreadsheetTable &src_table)
void spreadsheet_table_remap_id(SpreadsheetTable &table, const bke::id::IDRemapper &mappings)
SpreadsheetColumn * spreadsheet_column_copy(const SpreadsheetColumn *src_column)
void spreadsheet_table_id_remap_id(SpreadsheetTableID &table_id, const bke::id::IDRemapper &mappings)
SpreadsheetTable * spreadsheet_table_find(SpaceSpreadsheet &sspreadsheet, const SpreadsheetTableID &table_id)
void spreadsheet_table_add(SpaceSpreadsheet &sspreadsheet, SpreadsheetTable *table)
SpreadsheetTable ** tables
SpreadsheetBundlePathElem * bundle_path
SpreadsheetInstanceID * instance_ids
SpreadsheetColumn ** columns
SpreadsheetTableID * id
i
Definition text_draw.cc:230