Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/space_spreadsheet/space_spreadsheet.cc
| Show All 11 Lines | |||||
| * You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | ||||
| * along with this program; if not, write to the Free Software Foundation, | * along with this program; if not, write to the Free Software Foundation, | ||||
| * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
| */ | */ | ||||
| #include <cstring> | #include <cstring> | ||||
| #include "BLI_listbase.h" | #include "BLI_listbase.h" | ||||
| #include "BLI_resource_scope.hh" | |||||
| #include "BKE_screen.h" | #include "BKE_screen.h" | ||||
| #include "ED_screen.h" | #include "ED_screen.h" | ||||
| #include "ED_space_api.h" | #include "ED_space_api.h" | ||||
| #include "DNA_scene_types.h" | #include "DNA_scene_types.h" | ||||
| #include "DNA_screen_types.h" | #include "DNA_screen_types.h" | ||||
| #include "DNA_space_types.h" | #include "DNA_space_types.h" | ||||
| #include "MEM_guardedalloc.h" | #include "MEM_guardedalloc.h" | ||||
| #include "UI_interface.h" | #include "UI_interface.h" | ||||
| #include "UI_resources.h" | #include "UI_resources.h" | ||||
| #include "UI_view2d.h" | #include "UI_view2d.h" | ||||
| #include "DEG_depsgraph_query.h" | #include "DEG_depsgraph_query.h" | ||||
| #include "RNA_access.h" | #include "RNA_access.h" | ||||
| #include "WM_api.h" | #include "WM_api.h" | ||||
| #include "WM_types.h" | #include "WM_types.h" | ||||
| #include "BLF_api.h" | |||||
| #include "spreadsheet_intern.hh" | #include "spreadsheet_intern.hh" | ||||
| #include "spreadsheet_column_layout.hh" | #include "spreadsheet_data_source_geometry.hh" | ||||
| #include "spreadsheet_from_geometry.hh" | |||||
| #include "spreadsheet_intern.hh" | #include "spreadsheet_intern.hh" | ||||
| #include "spreadsheet_layout.hh" | |||||
| using namespace blender; | |||||
| using namespace blender::ed::spreadsheet; | using namespace blender::ed::spreadsheet; | ||||
| static SpaceLink *spreadsheet_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) | static SpaceLink *spreadsheet_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) | ||||
| { | { | ||||
| SpaceSpreadsheet *spreadsheet_space = (SpaceSpreadsheet *)MEM_callocN(sizeof(SpaceSpreadsheet), | SpaceSpreadsheet *spreadsheet_space = (SpaceSpreadsheet *)MEM_callocN(sizeof(SpaceSpreadsheet), | ||||
| "spreadsheet space"); | "spreadsheet space"); | ||||
| spreadsheet_space->spacetype = SPACE_SPREADSHEET; | spreadsheet_space->spacetype = SPACE_SPREADSHEET; | ||||
| Show All 22 Lines | static SpaceLink *spreadsheet_create(const ScrArea *UNUSED(area), const Scene *UNUSED(scene)) | ||||
| return (SpaceLink *)spreadsheet_space; | return (SpaceLink *)spreadsheet_space; | ||||
| } | } | ||||
| static void spreadsheet_free(SpaceLink *sl) | static void spreadsheet_free(SpaceLink *sl) | ||||
| { | { | ||||
| SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl; | SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl; | ||||
| MEM_SAFE_FREE(sspreadsheet->runtime); | MEM_SAFE_FREE(sspreadsheet->runtime); | ||||
| LISTBASE_FOREACH_MUTABLE (SpreadsheetColumn *, column, &sspreadsheet->columns) { | |||||
| spreadsheet_column_free(column); | |||||
| } | |||||
| } | } | ||||
| static void spreadsheet_init(wmWindowManager *UNUSED(wm), ScrArea *area) | static void spreadsheet_init(wmWindowManager *UNUSED(wm), ScrArea *area) | ||||
| { | { | ||||
| SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)area->spacedata.first; | SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)area->spacedata.first; | ||||
| if (sspreadsheet->runtime == nullptr) { | if (sspreadsheet->runtime == nullptr) { | ||||
| sspreadsheet->runtime = (SpaceSpreadsheet_Runtime *)MEM_callocN( | sspreadsheet->runtime = (SpaceSpreadsheet_Runtime *)MEM_callocN( | ||||
| sizeof(SpaceSpreadsheet_Runtime), __func__); | sizeof(SpaceSpreadsheet_Runtime), __func__); | ||||
| } | } | ||||
| LISTBASE_FOREACH_MUTABLE (SpreadsheetColumn *, column, &sspreadsheet->columns) { | |||||
| spreadsheet_column_free(column); | |||||
| } | |||||
| BLI_listbase_clear(&sspreadsheet->columns); | |||||
| } | } | ||||
| static SpaceLink *spreadsheet_duplicate(SpaceLink *sl) | static SpaceLink *spreadsheet_duplicate(SpaceLink *sl) | ||||
| { | { | ||||
| const SpaceSpreadsheet *sspreadsheet_old = (SpaceSpreadsheet *)sl; | const SpaceSpreadsheet *sspreadsheet_old = (SpaceSpreadsheet *)sl; | ||||
| SpaceSpreadsheet *sspreadsheet_new = (SpaceSpreadsheet *)MEM_dupallocN(sspreadsheet_old); | SpaceSpreadsheet *sspreadsheet_new = (SpaceSpreadsheet *)MEM_dupallocN(sspreadsheet_old); | ||||
| sspreadsheet_new->runtime = (SpaceSpreadsheet_Runtime *)MEM_dupallocN(sspreadsheet_old->runtime); | sspreadsheet_new->runtime = (SpaceSpreadsheet_Runtime *)MEM_dupallocN(sspreadsheet_old->runtime); | ||||
| BLI_listbase_clear(&sspreadsheet_new->columns); | |||||
| LISTBASE_FOREACH (SpreadsheetColumn *, src_column, &sspreadsheet_old->columns) { | |||||
| SpreadsheetColumn *new_column = spreadsheet_column_copy(src_column); | |||||
| BLI_addtail(&sspreadsheet_new->columns, new_column); | |||||
| } | |||||
| return (SpaceLink *)sspreadsheet_new; | return (SpaceLink *)sspreadsheet_new; | ||||
| } | } | ||||
| static void spreadsheet_keymap(wmKeyConfig *UNUSED(keyconf)) | static void spreadsheet_keymap(wmKeyConfig *UNUSED(keyconf)) | ||||
| { | { | ||||
| } | } | ||||
| static void spreadsheet_main_region_init(wmWindowManager *wm, ARegion *region) | static void spreadsheet_main_region_init(wmWindowManager *wm, ARegion *region) | ||||
| Show All 15 Lines | static ID *get_used_id(const bContext *C) | ||||
| SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); | SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); | ||||
| if (sspreadsheet->pinned_id != nullptr) { | if (sspreadsheet->pinned_id != nullptr) { | ||||
| return sspreadsheet->pinned_id; | return sspreadsheet->pinned_id; | ||||
| } | } | ||||
| Object *active_object = CTX_data_active_object(C); | Object *active_object = CTX_data_active_object(C); | ||||
| return (ID *)active_object; | return (ID *)active_object; | ||||
| } | } | ||||
| class FallbackSpreadsheetDrawer : public SpreadsheetDrawer { | static std::unique_ptr<DataSource> get_data_source(const bContext *C) | ||||
| }; | |||||
| static void gather_spreadsheet_columns(const bContext *C, | |||||
| SpreadsheetColumnLayout &column_layout, | |||||
| blender::ResourceScope &scope) | |||||
| { | { | ||||
| Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); | Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); | ||||
| ID *used_id = get_used_id(C); | ID *used_id = get_used_id(C); | ||||
| if (used_id == nullptr) { | if (used_id == nullptr) { | ||||
| return; | return {}; | ||||
| } | } | ||||
| const ID_Type id_type = GS(used_id->name); | const ID_Type id_type = GS(used_id->name); | ||||
| if (id_type != ID_OB) { | if (id_type != ID_OB) { | ||||
| return; | return {}; | ||||
| } | } | ||||
| Object *object_orig = (Object *)used_id; | Object *object_orig = (Object *)used_id; | ||||
| if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD)) { | if (!ELEM(object_orig->type, OB_MESH, OB_POINTCLOUD)) { | ||||
| return; | return {}; | ||||
| } | } | ||||
| Object *object_eval = DEG_get_evaluated_object(depsgraph, object_orig); | Object *object_eval = DEG_get_evaluated_object(depsgraph, object_orig); | ||||
| if (object_eval == nullptr) { | if (object_eval == nullptr) { | ||||
| return; | return {}; | ||||
| } | |||||
| return data_source_from_geometry(C, object_eval); | |||||
| } | |||||
| static float get_column_width(const ColumnValues &values) | |||||
| { | |||||
| if (values.default_width_factor > 0) { | |||||
| return values.default_width_factor * UI_UNIT_X; | |||||
| } | |||||
| const int fontid = UI_style_get()->widget.uifont_id; | |||||
| const int widget_points = UI_style_get_dpi()->widget.points; | |||||
| BLF_size(fontid, widget_points * U.pixelsize, U.dpi); | |||||
| const StringRefNull name = values.name(); | |||||
| const float name_width = BLF_width(fontid, name.data(), name.size()); | |||||
| return std::max<float>(name_width + UI_UNIT_X, 3 * UI_UNIT_X); | |||||
| } | |||||
| static int get_index_column_width(const int tot_rows) | |||||
| { | |||||
| const int fontid = UI_style_get()->widget.uifont_id; | |||||
| BLF_size(fontid, UI_style_get_dpi()->widget.points * U.pixelsize, U.dpi); | |||||
| return std::to_string(std::max(0, tot_rows - 1)).size() * BLF_width(fontid, "0", 1) + | |||||
| UI_UNIT_X * 0.75; | |||||
| } | |||||
| static void update_visible_columns(ListBase &columns, DataSource &data_source) | |||||
| { | |||||
| Set<SpreadsheetColumnID> used_ids; | |||||
| LISTBASE_FOREACH_MUTABLE (SpreadsheetColumn *, column, &columns) { | |||||
| /* Remove duplicate columns. */ | |||||
HooglyBoogly: A duplicate column would be an error on the part of the data source code? Or is it expected in… | |||||
Done Inline ActionsI could also just support duplicate columns here, shouldn't be an issue. JacquesLucke: I could also just support duplicate columns here, shouldn't be an issue. | |||||
| if (used_ids.contains(*column->id)) { | |||||
| BLI_remlink(&columns, column); | |||||
| spreadsheet_column_free(column); | |||||
| continue; | |||||
| } | |||||
| std::unique_ptr<ColumnValues> values = data_source.get_column_values(*column->id); | |||||
| /* Remove columns that don't exist anymore. */ | |||||
| if (!values) { | |||||
| BLI_remlink(&columns, column); | |||||
| spreadsheet_column_free(column); | |||||
| continue; | |||||
| } | } | ||||
| return spreadsheet_columns_from_geometry(C, object_eval, column_layout, scope); | used_ids.add(*column->id); | ||||
| } | |||||
| data_source.foreach_default_column_ids([&](const SpreadsheetColumnID &column_id) { | |||||
| std::unique_ptr<ColumnValues> values = data_source.get_column_values(column_id); | |||||
| if (values) { | |||||
| if (used_ids.add(column_id)) { | |||||
| SpreadsheetColumnID *new_id = spreadsheet_column_id_copy(&column_id); | |||||
| SpreadsheetColumn *new_column = spreadsheet_column_new(new_id); | |||||
| BLI_addtail(&columns, new_column); | |||||
| } | |||||
| } | |||||
| }); | |||||
| } | } | ||||
| static void spreadsheet_main_region_draw(const bContext *C, ARegion *region) | static void spreadsheet_main_region_draw(const bContext *C, ARegion *region) | ||||
| { | { | ||||
| SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); | SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); | ||||
| blender::ResourceScope scope; | std::unique_ptr<DataSource> data_source = get_data_source(C); | ||||
| SpreadsheetColumnLayout column_layout; | if (!data_source) { | ||||
| gather_spreadsheet_columns(C, column_layout, scope); | data_source = std::make_unique<DataSource>(); | ||||
| } | |||||
| sspreadsheet->runtime->visible_rows = column_layout.row_indices.size(); | |||||
| sspreadsheet->runtime->tot_columns = column_layout.columns.size(); | update_visible_columns(sspreadsheet->columns, *data_source); | ||||
| sspreadsheet->runtime->tot_rows = column_layout.tot_rows; | |||||
| SpreadsheetLayout spreadsheet_layout; | |||||
| ResourceScope scope; | |||||
| LISTBASE_FOREACH (SpreadsheetColumn *, column, &sspreadsheet->columns) { | |||||
| std::unique_ptr<ColumnValues> values_ptr = data_source->get_column_values(*column->id); | |||||
| /* Should have been removed before if it does not exist anymore. */ | |||||
| BLI_assert(values_ptr); | |||||
| const ColumnValues *values = scope.add(std::move(values_ptr), __func__); | |||||
| const int width = get_column_width(*values); | |||||
| spreadsheet_layout.columns.append({values, width}); | |||||
| } | |||||
| const int tot_rows = data_source->tot_rows(); | |||||
| spreadsheet_layout.index_column_width = get_index_column_width(tot_rows); | |||||
| spreadsheet_layout.row_indices = IndexRange(tot_rows).as_span(); | |||||
| if (const GeometryDataSource *geometry_data_source = dynamic_cast<const GeometryDataSource *>( | |||||
| data_source.get())) { | |||||
| Object *object_eval = geometry_data_source->object_eval(); | |||||
| Object *object_orig = DEG_get_original_object(object_eval); | |||||
| if (object_orig->type == OB_MESH) { | |||||
| if (object_orig->mode == OB_MODE_EDIT) { | |||||
| if (sspreadsheet->filter_flag & SPREADSHEET_FILTER_SELECTED_ONLY) { | |||||
| spreadsheet_layout.row_indices = geometry_data_source->get_selected_element_indices(); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| sspreadsheet->runtime->tot_columns = spreadsheet_layout.columns.size(); | |||||
| sspreadsheet->runtime->tot_rows = tot_rows; | |||||
| sspreadsheet->runtime->visible_rows = spreadsheet_layout.row_indices.size(); | |||||
| std::unique_ptr<SpreadsheetDrawer> drawer = spreadsheet_drawer_from_column_layout(column_layout); | std::unique_ptr<SpreadsheetDrawer> drawer = spreadsheet_drawer_from_layout(spreadsheet_layout); | ||||
| draw_spreadsheet_in_region(C, region, *drawer); | draw_spreadsheet_in_region(C, region, *drawer); | ||||
| /* Tag footer for redraw, because the main region updates data for the footer. */ | /* Tag footer for redraw, because the main region updates data for the footer. */ | ||||
| ARegion *footer = BKE_area_find_region_type(CTX_wm_area(C), RGN_TYPE_FOOTER); | ARegion *footer = BKE_area_find_region_type(CTX_wm_area(C), RGN_TYPE_FOOTER); | ||||
| ED_region_tag_redraw(footer); | ED_region_tag_redraw(footer); | ||||
| } | } | ||||
| static void spreadsheet_main_region_listener(const wmRegionListenerParams *params) | static void spreadsheet_main_region_listener(const wmRegionListenerParams *params) | ||||
| ▲ Show 20 Lines • Show All 184 Lines • Show Last 20 Lines | |||||
A duplicate column would be an error on the part of the data source code? Or is it expected in some case?
It might be nice to have a quick bit of info about that here.