aboutsummaryrefslogtreecommitdiff
path: root/core/src/Model/Template/TableTemplate.hpp
blob: 87718678b09b9cd9a52a9e11e981230ad217be91 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
#pragma once

#include "Model/Template/Template.hpp"
#include "Utils/Vector.hpp"
#include "Utils/VectorHash.hpp"
#include "cplt_fwd.hpp"

#include <tsl/array_map.h>
#include <tsl/robin_map.h>
#include <string>
#include <string_view>
#include <vector>

class TableCell
{
public:
	enum TextAlignment
	{
		/// For horizontal alignment, this means align left. For vertical alignment, this means align top.
		AlignAxisMin,
		/// Align middle of the text to the middle of the axis.
		AlignCenter,
		/// For horizontal alignment, this means align right. For vertical alignment, this means align bottom.
		AlignAxisMax,
	};

	enum CellType
	{
		ConstantCell,
		SingularParametricCell,
		ArrayParametricCell,
	};

public:
	/// Display content of this cell. This doesn't necessarily have to line up with the parameter name (if this cell is one).
	std::string Content;
	Vec2i Location;
	/// Location of the primary (top left) cell, if this cell is a part of a merged group.
	/// Otherwise, either component of this field shall be -1.
	Vec2i PrimaryCellLocation{ -1, -1 };
	int SpanX = 0;
	int SpanY = 0;
	TextAlignment HorizontalAlignment = AlignCenter;
	TextAlignment VerticalAlignment = AlignCenter;
	CellType Type = ConstantCell;
	/// The id of the group description object, if this cell isn't a constant or singular parameter cell. Otherwise, this value is -1.
	int DataId = -1;

public:
	/// Return whether this cell holds meaningful data, i.e. true when this cell is either unmerged or the primary cell of a merged range.
	bool IsDataHoldingCell() const;
	/// Return whether this cell is the primary (i.e. top left) cell of a merged range or not.
	bool IsPrimaryCell() const;
	/// Return whether this cell is a part of a merged range or not. Includes the primary cell.
	bool IsMergedCell() const;

	void ReadFromDataStream(InputDataStream& stream);
	void WriteToDataStream(OutputDataStream& stream) const;
};

// TODO support reverse (bottom to top) filling order
// TODO support horizontal filling order

/// Parameter group information for a grouped array of cells. When instantiated, an array of 0 or more
/// elements shall be provided by the user, which will replace the group of templated cells with a list
/// of rows, each instantiated with the n-th element in the provided array.
/// \code
/// [["foo", "bar", "foobar"],
///  ["a", "b", c"],
///  ["1", "2", "3"],
///  ["x", "y", "z"]]
/// // ... may be more
/// \endcode
/// This would create 4 rows of data in the place of the original parameter group.
///
/// If more than one array parameter groups are on the same row, they would share space between each other:
/// \code
///  | 2 elements was fed to it
///  |                 | 1 element was fed to it
///  V                 V
/// {~~~~~~~~~~~~~~~~}{~~~~~~~~~~~~~~}
/// +------+---------+---------------+
/// | Foo  | Example | Another group |
/// +------+---------+---------------+
/// | Cool | Example |               |
/// +------+---------+---------------+
/// \endcode
///
/// \see TableCell
/// \see TableInstantiationParameters
/// \see TableTemplate
class TableArrayGroup
{
public:
	/// Parameter name mapped to cell location (index from LeftCell).
	tsl::array_map<char, int> mName2Cell;
	int Row;
	/// Leftmost cell in this group
	int LeftCell;
	/// Rightmost cell in this group
	int RightCell;

public:
	Vec2i GetLeftCell() const;
	Vec2i GetRightCell() const;
	int GetCount() const;

	/// Find the location of the cell within this array group that has the given name.
	Vec2i FindCell(std::string_view name);
	bool UpdateCellName(std::string_view oldName, std::string_view newName);

	void ReadFromDataStream(InputDataStream& stream);
	void WriteToDataStream(OutputDataStream& stream) const;
};

// Forward declaration of libxlsxwriter structs
struct lxw_workbook;
struct lxw_worksheet;

/// An object containing the necessary information to instantiate a table template.
/// \see TableTemplate
class TableInstantiationParameters
{
private:
	const TableTemplate* mTable;

public:
	tsl::robin_map<Vec2i, std::string> SingularCells;

	using ArrayGroupRow = std::vector<std::string>;
	using ArrayGroupData = std::vector<ArrayGroupRow>;
	std::vector<ArrayGroupData> ArrayGroups;

public:
	TableInstantiationParameters(const TableTemplate& table);

	TableInstantiationParameters& ResetTable(const TableTemplate& newTable);
	TableInstantiationParameters RebindTable(const TableTemplate& newTable) const;

	const TableTemplate& GetTable() const;
};

/// A table template, where individual cells can be filled by workflows instantiating this template. Merged cells,
/// parametric rows/columns, and grids are also supported.
///
/// This current supports exporting to xlsx files.
class TableTemplate : public Template
{
	friend class TableSingleParamsIter;
	friend class TableArrayGroupsIter;
	class Private;

private:
	/// Map from parameter name to index of the parameter cell (stored in mCells).
	tsl::array_map<char, int> mName2Parameters;
	/// Map from array group name to the index of the array group (stored in mArrayGroups).
	tsl::array_map<char, int> mName2ArrayGroups;
	std::vector<TableCell> mCells;
	std::vector<TableArrayGroup> mArrayGroups;
	std::vector<int> mRowHeights;
	std::vector<int> mColumnWidths;

public:
	static bool IsInstance(const Template* tmpl);
	TableTemplate();

	int GetTableWidth() const;
	int GetTableHeight() const;
	void Resize(int newWidth, int newHeight);

	int GetRowHeight(int row) const;
	void SetRowHeight(int row, int height);
	int GetColumnWidth(int column) const;
	void SetColumnWidth(int column, int width);

	const TableCell& GetCell(Vec2i pos) const;
	TableCell& GetCell(Vec2i pos);
	/// <ul>
	/// <li> In case of becoming a SingularParametricCell: the parameter name is filled with TableCell::Content.
	/// <li> In case of becoming a ArrayGroupParametricCell: the array group name is automatically generated as the nth group it would be come.
	/// i.e., if there aRe currently 3 groups, the newly created group would be named "4".
	/// If this name collides with an existing group, hyphens \c - will be append to the name until no collision happens.
	/// </ul>
	void SetCellType(Vec2i pos, TableCell::CellType type);

	/// Updates the parameter cell to a new name. Returns true on success and false on failure (param not found or name duplicates).
	bool UpdateParameterName(std::string_view oldName, std::string_view newName);

	int GetArrayGroupCount() const;
	const TableArrayGroup& GetArrayGroup(int id) const;
	TableArrayGroup& GetArrayGroup(int id);
	TableArrayGroup* AddArrayGroup(int row, int left, int right);
	TableArrayGroup* AddArrayGroup(std::string_view name, int row, int left, int right);
	bool UpdateArrayGroupName(std::string_view oldName, std::string_view newName);
	bool ExtendArrayGroupLeft(int id, int n);
	bool ExtendArrayGroupRight(int id, int n);

	/// Find a singular parameter cell by its name. This does not include cells within an array group.
	TableCell* FindCell(std::string_view name);

	/// Find an array group by its name.
	TableArrayGroup* FindArrayGroup(std::string_view name);

	enum MergeCellsResult
	{
		MCR_CellAlreadyMerged,
		MCR_Success,
	};
	MergeCellsResult MergeCells(Vec2i topLeft, Vec2i bottomRight);

	enum BreakCellsResult
	{
		BCR_CellNotMerged,
		BCR_Success,
	};
	BreakCellsResult BreakCells(Vec2i topLeft);

	lxw_workbook* InstantiateToExcelWorkbook(const TableInstantiationParameters& params) const;
	lxw_worksheet* InstantiateToExcelWorksheet(lxw_workbook* workbook, const TableInstantiationParameters& params) const;

	void ReadFromDataStream(InputDataStream& stream) override;
	void WriteToDataStream(OutputDataStream& stream) const override;
};