src/model.cpp

changeset 200
ca23936b455b
parent 173
8a3047468994
child 204
52e10e8d88cc
equal deleted inserted replaced
199:6988973515d2 200:ca23936b455b
14 * 14 *
15 * You should have received a copy of the GNU General Public License 15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */ 17 */
18 18
19 #include <QBrush>
20 #include <QFile>
21 #include <QFileInfo>
22 #include <QFont>
23 #include <QSaveFile>
24 #include "model.h" 19 #include "model.h"
25 #include "modeleditor.h"
26 #include "documentmanager.h"
27 20
28 /** 21 QString modelElementToString(const ModelElement &element)
29 * @brief Constructs a model 22 {
30 * @param parent QObject parent to pass forward 23 return std::visit(overloaded{
31 */ 24 [](const Colored<SubfileReference>& ref) {
25 return QStringLiteral("1 %1 %2 %3")
26 .arg(ref.color.index)
27 .arg(transformToString(ref.transformation))
28 .arg(ref.name);
29 },
30 [](const Colored<LineSegment>& seg) {
31 return QStringLiteral("2 %1 %2 %3")
32 .arg(seg.color.index)
33 .arg(vertexToString(seg.p1))
34 .arg(vertexToString(seg.p2));
35 },
36 [](const Colored<Triangle>& triangle) {
37 return QStringLiteral("3 %1 %2 %3 %4")
38 .arg(triangle.color.index)
39 .arg(vertexToString(triangle.p1))
40 .arg(vertexToString(triangle.p2))
41 .arg(vertexToString(triangle.p3));
42 },
43 [](const Colored<Quadrilateral>& quad) {
44 return QStringLiteral("4 %1 %2 %3 %4 %5")
45 .arg(quad.color.index)
46 .arg(vertexToString(quad.p1))
47 .arg(vertexToString(quad.p2))
48 .arg(vertexToString(quad.p3))
49 .arg(vertexToString(quad.p4));
50 },
51 [](const Colored<ConditionalEdge>& cedge) {
52 return QStringLiteral("5 %1 %2 %3 %4 %5")
53 .arg(cedge.color.index)
54 .arg(vertexToString(cedge.p1))
55 .arg(vertexToString(cedge.p2))
56 .arg(vertexToString(cedge.c1))
57 .arg(vertexToString(cedge.c2));
58 },
59 [](const Comment& comment) {
60 return "0 " + comment.text;
61 },
62 [](const Empty&) {
63 return QStringLiteral("");
64 },
65 [](const ParseError& parseError) {
66 return parseError.code;
67 },
68 }, element);
69 }
70
32 Model::Model(QObject *parent) : 71 Model::Model(QObject *parent) :
33 QAbstractListModel{parent} 72 QAbstractListModel{parent}
34 { 73 {
35 } 74 }
36 75
37 /** 76 Model::~Model()
38 * @returns the amount of elements in the model
39 */
40 int Model::size() const
41 { 77 {
42 return static_cast<int>(this->body.size());
43 } 78 }
44 79
45 /** 80 ModelId Model::append(const ModelElement &value)
46 * @brief Looks up the object ID at the specified index. If out of bounds, returns NULL_ID.
47 * @param index Index of object to look up
48 * @return object ID
49 */
50 ldraw::id_t Model::at(int index) const
51 { 81 {
52 if (index >= 0 and index < this->size()) 82 const int position = static_cast<int>(this->body.size());
53 { 83 const ModelId id = this->runningId;
54 return this->body[index]->id; 84 this->runningId.value += 1;
55 } 85 Q_EMIT this->beginInsertRows({}, position, position);
56 else 86 this->body.push_back({value, id});
57 { 87 this->positions[id] = position;
58 return ldraw::NULL_ID; 88 Q_EMIT this->endInsertRows();
89 return id;
90 }
91
92 const ModelElement &Model::at(int position) const
93 {
94 return this->body[position].data;
95 }
96
97 ModelId Model::idAt(int position) const
98 {
99 return this->body[position].id;
100 }
101
102 void Model::assignAt(int position, const ModelElement &element)
103 {
104 this->body[position].data = element;
105 const QModelIndex index = this->index(position);
106 Q_EMIT this->dataChanged(index, index);
107 }
108
109 std::optional<int> Model::find(ModelId id) const
110 {
111 return pointerToOptional(findInMap(this->positions, id));
112 }
113
114 void Model::remove(int index)
115 {
116 if (index >= 0 and index < this->size()) {
117 Q_EMIT this->beginRemoveRows({}, index, index);
118 this->body.erase(this->body.begin() + index);
119 Q_EMIT this->endRemoveRows();
59 } 120 }
60 } 121 }
61 122
62 /** 123 int Model::rowCount(const QModelIndex &) const
63 * @brief @overload QAbstractListModel::rowCount
64 * @return size
65 */
66 int Model::rowCount(const QModelIndex&) const
67 { 124 {
68 return this->size(); 125 return this->size();
69 } 126 }
70 127
71 /** 128 QVariant Model::data(const QModelIndex &index, int role) const
72 * @brief @overload QAbstractListModel::data
73 * @param index
74 * @param role
75 * @return QVariant
76 */
77 QVariant Model::data(const QModelIndex& index, int role) const
78 { 129 {
79 const ldraw::Object* object = (*this)[index.row()]; 130 const int i = index.row();
80 switch(role) 131 switch(role)
81 { 132 {
133 /*
82 case Qt::DecorationRole: 134 case Qt::DecorationRole:
83 return QPixmap{object->iconName()}.scaledToHeight(24); 135 return QPixmap{object->iconName()}.scaledToHeight(24);
136 */
84 case Qt::DisplayRole: 137 case Qt::DisplayRole:
85 return object->textRepresentation(); 138 return modelElementToString(this->body[i].data);
139 /*
86 case Qt::ForegroundRole: 140 case Qt::ForegroundRole:
87 return object->textRepresentationForeground(); 141 return object->textRepresentationForeground();
88 case Qt::BackgroundRole: 142 case Qt::BackgroundRole:
89 return object->textRepresentationBackground(); 143 return object->textRepresentationBackground();
90 case Qt::FontRole: 144 case Qt::FontRole:
91 return object->textRepresentationFont(); 145 return object->textRepresentationFont();
146 */
92 default: 147 default:
93 return {}; 148 return {};
94 } 149 }
95 } 150 }
96 151
97 /** 152 const ModelElement &Model::operator[](int index) const
98 * @brief Finds the position of the specified object in the model
99 * @param id Object id to look for
100 * @return model index
101 */
102 QModelIndex Model::find(ldraw::id_t id) const
103 { 153 {
104 if (this->needObjectsByIdRebuild) 154 return this->body[index].data;
105 { 155 }
106 this->objectsById.clear(); 156
107 for (std::size_t i = 0; i < this->body.size(); ++i) 157 int Model::size() const
108 { 158 {
109 this->objectsById[this->body[i]->id] = i; 159 return this->body.size();
110 } 160 }
111 this->needObjectsByIdRebuild = false; 161
112 } 162 void save(const Model &model, QIODevice *device)
113 const auto it = this->objectsById.find(id); 163 {
114 if (it != this->objectsById.end()) 164 QTextStream out{device};
115 { 165 for (int i = 0; i < model.size(); ++i) {
116 return this->index(it->second); 166 out << modelElementToString(model[i]) << "\r\n";
117 }
118 else
119 {
120 return {};
121 } 167 }
122 } 168 }
123 169
124 /** 170 /**
125 * @brief Gets an object id by position in the model
126 * @param index Position of the object in the model
127 * @return id
128 */
129 ldraw::id_t Model::idAt(const QModelIndex& index) const
130 {
131 return (*this)[index.row()]->id;
132 }
133
134 #if 0
135 /**
136 * @brief Sets the path to the model 171 * @brief Sets the path to the model
137 * @param path New path to use 172 * @param path New path to use
138 */ 173 */
139 void Model::setPath(const QString &path) 174 void updateHeaderNameField(Model& model, const QString &name)
140 { 175 {
141 this->storedPath = path;
142 this->header.name = QFileInfo{path}.fileName();
143 // Update the "Name: 1234.dat" comment 176 // Update the "Name: 1234.dat" comment
144 if (this->body.size() >= 2) 177 if (model.size() >= 2) {
145 { 178 if (const Comment* nameObject = std::get_if<Comment>(&model[1])) {
146 const ldraw::id_t id = this->body[1]->id; 179 if (nameObject->text.startsWith("Name: ")) {
147 if (this->isA<ldraw::MetaCommand>(id)) 180 model[1] = Comment{"Name: " + name};
148 {
149 const QString& textBody = this->body[1]->getProperty<ldraw::Property::Text>();
150 if (textBody.startsWith("Name: "))
151 {
152 auto editor = this->edit();
153 editor.setObjectProperty<ldraw::Property::Text>(id, "Name: " + this->header.name);
154 } 181 }
155 } 182 }
156 } 183 }
157 } 184 }
158 #endif
159
160 /**
161 * @brief Adds the given object into the model.
162 * @param object r-value reference to the object
163 */
164 ldraw::id_t Model::append(ModelObjectPointer&& object)
165 {
166 const int position = static_cast<int>(this->body.size());
167 Q_EMIT this->beginInsertRows({}, position, position);
168 this->body.push_back(std::move(object));
169 Q_EMIT this->endInsertRows();
170 const ldraw::id_t id = this->body.back()->id;
171 this->objectsById[id] = this->body.size() - 1;
172 return id;
173 }
174
175 /**
176 * @brief Removes the object at the specified position
177 * @param position
178 */
179 void Model::remove(int position)
180 {
181 if (position >= 0 and position < signed_cast(this->body.size()))
182 {
183 Q_EMIT this->beginRemoveRows({}, position, position);
184 this->body.erase(std::begin(this->body) + position);
185 this->needObjectsByIdRebuild = true;
186 Q_EMIT this->endRemoveRows();
187 }
188 }
189
190 void Model::emitDataChangedSignal(int position)
191 {
192 Q_EMIT this->dataChanged(this->index(position), this->index(position));
193 }
194
195 /**
196 * @brief Gets the object pointer at the specified position
197 * @param index Position of the object
198 * @returns object pointer
199 */
200 ldraw::Object* Model::operator[](int index)
201 {
202 if (index >= 0 and index < this->size())
203 {
204 return this->body[index].get();
205 }
206 else
207 {
208 throw std::out_of_range{"index out of range"};
209 }
210 }
211
212 /**
213 * @brief Gets the object pointer at the specified position
214 * @param index Position of the object
215 * @returns object pointer
216 */
217 const ldraw::Object* Model::operator[](int index) const
218 {
219 if (index >= 0 and index < this->size())
220 {
221 return this->body[index].get();
222 }
223 else
224 {
225 throw std::out_of_range{"index out of range"};
226 }
227 }
228
229 /**
230 * @brief Gets an object pointer by id. Used by the editing context to actually modify objects.
231 * @param id
232 * @return object pointer
233 */
234 ldraw::Object* Model::findObjectById(const ldraw::id_t id)
235 {
236 const QModelIndex index = this->find(id);
237 if (index.isValid())
238 {
239 return (*this)[index.row()];
240 }
241 else
242 {
243 return nullptr;
244 }
245 }
246
247 const ldraw::Object* Model::findObjectById(const ldraw::id_t id) const
248 {
249 const QModelIndex index = this->find(id);
250 if (index.isValid())
251 {
252 return (*this)[index.row()];
253 }
254 else
255 {
256 return nullptr;
257 }
258 }
259
260 /**
261 * @brief Attempts the save the model
262 */
263 void save(const Model &model, QIODevice *device)
264 {
265 QTextStream out{device};
266 applyToModel<ldraw::Object>(model, [&](const ldraw::Object* object) {
267 out << object->toLDrawCode() << "\r\n";
268 });
269 }

mercurial