Changeset View
Changeset View
Standalone View
Standalone View
src/timeline2/model/modelupdater.cpp
Context not available. | |||||
24 | #include "trackmodel.hpp" | 24 | #include "trackmodel.hpp" | ||
---|---|---|---|---|---|
25 | #include <unordered_map> | 25 | #include <unordered_map> | ||
26 | 26 | | |||
27 | namespace { | | |||
28 | // This function assumes all the updates in the list correspond to the same item | 27 | // This function assumes all the updates in the list correspond to the same item | ||
29 | // It will merge move update together. We also merge insert + move in an insert to the right position | 28 | // It will merge move update together. We also merge insert + move in an insert to the right position | ||
30 | std::vector<std::shared_ptr<AbstractUpdate>> mergeMoves(const std::vector<std::shared_ptr<AbstractUpdate>> &list) | 29 | std::vector<std::shared_ptr<AbstractUpdate>> mergeMoves(const std::vector<std::shared_ptr<AbstractUpdate>> &list) | ||
Context not available. | |||||
117 | bool isClip = true; | 116 | bool isClip = true; | ||
118 | std::weak_ptr<TimelineModel> sourceTimeline; | 117 | std::weak_ptr<TimelineModel> sourceTimeline; | ||
119 | 118 | | |||
119 | qDebug()<<"******** MERGIN UPDATES FOR ITEM : "<<itemId<<"***********"; | ||||
120 | for (const auto &update : list) { | 120 | for (const auto &update : list) { | ||
121 | Q_ASSERT(update->getItemId() == itemId); | 121 | Q_ASSERT(update->getItemId() == itemId); | ||
122 | Q_ASSERT(!update->isMove()); | 122 | Q_ASSERT(!update->isMove()); | ||
Context not available. | |||||
126 | sourcePos = del->getPos(); | 126 | sourcePos = del->getPos(); | ||
127 | sourceTimeline = del->getTimeline(); | 127 | sourceTimeline = del->getTimeline(); | ||
128 | isClip = del->isClip(); | 128 | isClip = del->isClip(); | ||
129 | qDebug()<<"******** FOUND DELETE: "<<itemId<<", CLIP: "<<isClip<<", SOURCEPOS: "<<sourcePos; | ||||
129 | Q_ASSERT(sourceTrackId != -1); | 130 | Q_ASSERT(sourceTrackId != -1); | ||
130 | } else if (sourceTrackId != -1 && update->isInsert()) { | 131 | } else if (sourceTrackId != -1 && update->isInsert()) { | ||
132 | auto tl = sourceTimeline.lock(); | ||||
131 | auto insert = std::static_pointer_cast<InsertUpdate>(update); | 133 | auto insert = std::static_pointer_cast<InsertUpdate>(update); | ||
132 | result.emplace_back( | 134 | result.emplace_back( | ||
133 | new MoveUpdate(itemId, sourceTimeline, sourceTrackId, sourcePos, insert->getTimeline(), insert->getTrackId(), insert->getPos())); | 135 | new MoveUpdate(itemId, sourceTimeline, sourceTrackId, sourcePos, insert->getTimeline(), insert->getTrackId(), insert->getPos())); | ||
Context not available. | |||||
141 | result.push_back(update); | 143 | result.push_back(update); | ||
142 | } | 144 | } | ||
143 | } | 145 | } | ||
146 | qDebug()<<"******** MERGIN DONE FOR ITEM : "<<itemId<<"***********"; | ||||
144 | 147 | | |||
145 | if (sourcePos != -1) { | 148 | if (sourcePos != -1) { | ||
146 | // If we reach this, it means that the last operation was a delete, but we didn't find any subsequent insert. | 149 | // If we reach this, it means that the last operation was a delete, but we didn't find any subsequent insert. | ||
Context not available. | |||||
158 | } | 161 | } | ||
159 | if (!isFirstOperationInsert) { | 162 | if (!isFirstOperationInsert) { | ||
160 | result_clean.emplace_back(new DeleteUpdate(itemId, sourceTimeline, sourceTrackId, sourcePos, isClip)); | 163 | result_clean.emplace_back(new DeleteUpdate(itemId, sourceTimeline, sourceTrackId, sourcePos, isClip)); | ||
164 | return result_clean; | ||||
165 | } else { | ||||
166 | // We insert clip then delete it, so do nothing | ||||
167 | return result_clean; | ||||
161 | } | 168 | } | ||
162 | } | 169 | } | ||
163 | return result; | 170 | return result; | ||
164 | } | 171 | } | ||
165 | 172 | | |||
173 | namespace { | ||||
166 | // This function assumes all the updates in the list correspond to the same item | 174 | // This function assumes all the updates in the list correspond to the same item | ||
167 | // It will merge change updates together. If changes are constructed with different timeline pointers, this is a bit problematic, it means that the item was | 175 | // It will merge change updates together. If changes are constructed with different timeline pointers, this is a bit problematic, it means that the item was | ||
168 | // moved to a different timeline along the way. We track the insert/moves to check what is the last timeline it was seen in. This function must be called after | 176 | // moved to a different timeline along the way. We track the insert/moves to check what is the last timeline it was seen in. This function must be called after | ||
Context not available. | |||||
175 | } | 183 | } | ||
176 | std::vector<std::shared_ptr<AbstractUpdate>> result; | 184 | std::vector<std::shared_ptr<AbstractUpdate>> result; | ||
177 | int itemId = list[0]->getItemId(); | 185 | int itemId = list[0]->getItemId(); | ||
186 | qDebug() << "merging changes for item: "<<itemId; | ||||
178 | std::unordered_set<int> roles; | 187 | std::unordered_set<int> roles; | ||
179 | bool seenMove = false; | 188 | bool seenMove = false; | ||
180 | std::weak_ptr<TimelineModel> timeline; | 189 | std::weak_ptr<TimelineModel> timeline; | ||
Context not available. | |||||
193 | auto insert = std::static_pointer_cast<InsertUpdate>(update); | 202 | auto insert = std::static_pointer_cast<InsertUpdate>(update); | ||
194 | timeline = insert->getTimeline(); | 203 | timeline = insert->getTimeline(); | ||
195 | seenMove = true; | 204 | seenMove = true; | ||
205 | qDebug() << "merging INSERT change FOR: "<<itemId; | ||||
206 | //roles.insert({TimelineModel::TrackIdRole}); | ||||
196 | result.push_back(update); | 207 | result.push_back(update); | ||
197 | } else if (update->isMove()) { | 208 | } else if (update->isMove()) { | ||
198 | Q_ASSERT(!seenMove); | 209 | Q_ASSERT(!seenMove); | ||
Context not available. | |||||
233 | 244 | | |||
234 | for (const auto &u : list) { | 245 | for (const auto &u : list) { | ||
235 | updatesByItem[u->getItemId()].push_back(u); | 246 | updatesByItem[u->getItemId()].push_back(u); | ||
247 | qDebug()<<"***FOUND UPDATE FOR ITEM: "<<u->getItemId(); | ||||
236 | } | 248 | } | ||
237 | 249 | | |||
238 | // Then, we simplify updates element by element | 250 | // Then, we simplify updates element by element | ||
239 | 251 | qDebug()<<"+ + + + + ++ "; | |||
240 | std::vector<std::shared_ptr<AbstractUpdate>> res; | 252 | std::vector<std::shared_ptr<AbstractUpdate>> res; | ||
241 | for (const auto &u : updatesByItem) { | 253 | for (const auto &u : updatesByItem) { | ||
242 | auto curated = mergeChanges(mergeMoves(mergeDeleteInsert(u.second))); | 254 | auto curated = mergeChanges(mergeMoves(mergeDeleteInsert(u.second))); | ||
Context not available. | |||||
309 | }; | 321 | }; | ||
310 | return [ list, getCurrentRow = std::move(getCurrentRow), getTentativeRow = std::move(getTentativeRow) ]() | 322 | return [ list, getCurrentRow = std::move(getCurrentRow), getTentativeRow = std::move(getTentativeRow) ]() | ||
311 | { | 323 | { | ||
324 | QMap <int,int> trackRowOffset; | ||||
325 | for (const auto &u : list) { | ||||
326 | if (u->isInsert()) { | ||||
327 | auto ins = std::static_pointer_cast<InsertUpdate>(u); | ||||
328 | int trackId = ins->getTrackId(); | ||||
329 | if (trackRowOffset.contains(trackId)) { | ||||
330 | int offset = trackRowOffset.value(trackId); | ||||
331 | trackRowOffset[trackId] = offset + 1; | ||||
332 | } else { | ||||
333 | trackRowOffset[trackId] = 0; | ||||
334 | } | ||||
335 | } | ||||
336 | } | ||||
312 | for (const auto &u : list) { | 337 | for (const auto &u : list) { | ||
313 | if (u->isDelete()) { | 338 | if (u->isDelete()) { | ||
314 | auto del = std::static_pointer_cast<DeleteUpdate>(u); | 339 | auto del = std::static_pointer_cast<DeleteUpdate>(u); | ||
Context not available. | |||||
325 | if (auto timeline = ins->getTimeline().lock()) { | 350 | if (auto timeline = ins->getTimeline().lock()) { | ||
326 | int trackId = ins->getTrackId(), itemId = ins->getItemId(); | 351 | int trackId = ins->getTrackId(), itemId = ins->getItemId(); | ||
327 | int row = getTentativeRow(timeline, trackId, itemId, ins->isClip()); | 352 | int row = getTentativeRow(timeline, trackId, itemId, ins->isClip()); | ||
328 | timeline->_beginInsertRows(timeline->makeTrackIndexFromID(trackId), row, row); | 353 | //HACK: dirty workaround for the getTentativeRow that does not work in case of multiple insert in one track | ||
354 | int offset = trackRowOffset.value(trackId); | ||||
355 | /*if (trackRowOffset.contains(trackId)) { | ||||
356 | int offset = trackRowOffset.value(trackId); | ||||
357 | row += offset; | ||||
358 | trackRowOffset[trackId] = offset + 1; | ||||
359 | } else { | ||||
360 | trackRowOffset[trackId] = 1; | ||||
361 | }*/ | ||||
362 | if (offset >= 0) { | ||||
363 | qDebug()<<"===========\n==========\n===========\nINSERTING ROWS: "<<row<<" TO "<<(row + offset); | ||||
364 | timeline->_beginInsertRows(timeline->makeTrackIndexFromID(trackId), row, row + offset); | ||||
365 | trackRowOffset[trackId] = -1; | ||||
366 | } | ||||
329 | } else { | 367 | } else { | ||
330 | qDebug() << "ERROR: impossible to lock timeline"; | 368 | qDebug() << "ERROR: impossible to lock timeline"; | ||
331 | Q_ASSERT(false); | 369 | Q_ASSERT(false); | ||
Context not available. | |||||
334 | auto move = std::static_pointer_cast<MoveUpdate>(u); | 372 | auto move = std::static_pointer_cast<MoveUpdate>(u); | ||
335 | if (auto sTimeline = move->getSourceTimeline().lock()) { | 373 | if (auto sTimeline = move->getSourceTimeline().lock()) { | ||
336 | if (auto tTimeline = move->getTargetTimeline().lock()) { | 374 | if (auto tTimeline = move->getTargetTimeline().lock()) { | ||
337 | int sTrackId = move->getSourceTrackId(), itemId = move->getItemId(); | 375 | int sTrackId = move->getSourceTrackId(); | ||
376 | int itemId = move->getItemId(); | ||||
338 | int tTrackId = move->getTargetTrackId(); | 377 | int tTrackId = move->getTargetTrackId(); | ||
339 | int sRow = getCurrentRow(sTimeline, sTrackId, itemId); | 378 | int sRow = getCurrentRow(sTimeline, sTrackId, itemId); | ||
340 | int tRow = getTentativeRow(tTimeline, tTrackId, itemId, sTimeline->isClip(itemId)); | 379 | int tRow = getTentativeRow(tTimeline, tTrackId, itemId, sTimeline->isClip(itemId)); | ||
Context not available. | |||||
369 | Fun ModelUpdater::postApply_lambda(const std::vector<std::shared_ptr<AbstractUpdate>> &list) | 408 | Fun ModelUpdater::postApply_lambda(const std::vector<std::shared_ptr<AbstractUpdate>> &list) | ||
370 | { | 409 | { | ||
371 | return [list]() { | 410 | return [list]() { | ||
411 | bool insertSent = false; | ||||
372 | for (const auto &u : list) { | 412 | for (const auto &u : list) { | ||
373 | if (u->isDelete()) { | 413 | if (u->isDelete()) { | ||
414 | qDebug()<<"+ + + + ++ PREPARING AFTER DELETE UPDATE"; | ||||
374 | auto del = std::static_pointer_cast<DeleteUpdate>(u); | 415 | auto del = std::static_pointer_cast<DeleteUpdate>(u); | ||
375 | if (auto timeline = del->getTimeline().lock()) { | 416 | if (auto timeline = del->getTimeline().lock()) { | ||
376 | timeline->_endRemoveRows(); | 417 | timeline->_endRemoveRows(); | ||
Context not available. | |||||
379 | Q_ASSERT(false); | 420 | Q_ASSERT(false); | ||
380 | } | 421 | } | ||
381 | } else if (u->isInsert()) { | 422 | } else if (u->isInsert()) { | ||
423 | if (insertSent) { | ||||
424 | continue; | ||||
425 | } | ||||
426 | qDebug()<<"+ + + + ++ PREPARING AFTER INSERT UPDATE"; | ||||
382 | auto ins = std::static_pointer_cast<InsertUpdate>(u); | 427 | auto ins = std::static_pointer_cast<InsertUpdate>(u); | ||
383 | if (auto timeline = ins->getTimeline().lock()) { | 428 | if (auto timeline = ins->getTimeline().lock()) { | ||
429 | qDebug()<<"*** DONE INSERT NEW ROW: "<<ins->getItemId(); | ||||
384 | timeline->_endInsertRows(); | 430 | timeline->_endInsertRows(); | ||
431 | insertSent = true; | ||||
385 | } else { | 432 | } else { | ||
386 | qDebug() << "ERROR: impossible to lock timeline"; | 433 | qDebug() << "ERROR: impossible to lock timeline"; | ||
387 | Q_ASSERT(false); | 434 | Q_ASSERT(false); | ||
388 | } | 435 | } | ||
389 | } else if (u->isMove()) { | 436 | } else if (u->isMove()) { | ||
437 | qDebug()<<"+ + + + ++ PREPARING AFTER MOVE UPDATE"; | ||||
390 | auto move = std::static_pointer_cast<MoveUpdate>(u); | 438 | auto move = std::static_pointer_cast<MoveUpdate>(u); | ||
391 | if (auto sTimeline = move->getSourceTimeline().lock()) { | 439 | if (auto sTimeline = move->getSourceTimeline().lock()) { | ||
392 | if (auto tTimeline = move->getTargetTimeline().lock()) { | 440 | if (auto tTimeline = move->getTargetTimeline().lock()) { | ||
Context not available. | |||||
405 | Q_ASSERT(false); | 453 | Q_ASSERT(false); | ||
406 | } | 454 | } | ||
407 | } else if (u->isChange()) { | 455 | } else if (u->isChange()) { | ||
456 | qDebug()<<"+ + + + ++ PREPARING AFTER CHANGE UPDATE"; | ||||
408 | auto change = std::static_pointer_cast<ChangeUpdate>(u); | 457 | auto change = std::static_pointer_cast<ChangeUpdate>(u); | ||
409 | if (auto timeline = change->getTimeline().lock()) { | 458 | if (auto timeline = change->getTimeline().lock()) { | ||
410 | int itemId = change->getItemId(); | 459 | int itemId = change->getItemId(); | ||
Context not available. | |||||
415 | Q_ASSERT(timeline->isComposition(itemId)); | 464 | Q_ASSERT(timeline->isComposition(itemId)); | ||
416 | idx = timeline->makeCompositionIndexFromID(itemId); | 465 | idx = timeline->makeCompositionIndexFromID(itemId); | ||
417 | } | 466 | } | ||
467 | qDebug()<<"// MOVE UPDATE INDEX: "<<idx; | ||||
418 | timeline->notifyChange(idx, idx, change->getRoles()); | 468 | timeline->notifyChange(idx, idx, change->getRoles()); | ||
419 | } | 469 | } | ||
420 | } else { | 470 | } else { | ||
Context not available. | |||||
434 | } | 484 | } | ||
435 | auto updates = simplify(list); | 485 | auto updates = simplify(list); | ||
436 | auto rev_updates = reverse(updates); | 486 | auto rev_updates = reverse(updates); | ||
437 | | ||||
438 | // firstly, we need to undo the action | 487 | // firstly, we need to undo the action | ||
439 | bool undone = undo(); | 488 | bool undone = undo(); | ||
440 | Q_ASSERT(undone); | 489 | Q_ASSERT(undone); | ||
Context not available. | |||||
588 | 637 | | |||
589 | std::shared_ptr<AbstractUpdate> MoveUpdate::reverse() | 638 | std::shared_ptr<AbstractUpdate> MoveUpdate::reverse() | ||
590 | { | 639 | { | ||
591 | return std::make_shared<MoveUpdate>(m_itemId, m_targetTimeline, m_targetTrackId, m_targetPos, m_targetTimeline, m_targetTrackId, m_targetPos); | 640 | return std::make_shared<MoveUpdate>(m_itemId, m_targetTimeline, m_targetTrackId, m_targetPos, m_sourceTimeline, m_sourceTrackId, m_sourcePos); | ||
592 | } | 641 | } | ||
593 | 642 | | |||
594 | void MoveUpdate::print() const | 643 | void MoveUpdate::print() const | ||
Context not available. | |||||
633 | case TimelineModel::NameRole: | 682 | case TimelineModel::NameRole: | ||
634 | qDebug() << "NameRole"; | 683 | qDebug() << "NameRole"; | ||
635 | break; | 684 | break; | ||
685 | case TimelineModel::TrackIdRole: | ||||
686 | qDebug() << "TrackIdRole"; | ||||
687 | break; | ||||
636 | case TimelineModel::ResourceRole: | 688 | case TimelineModel::ResourceRole: | ||
637 | qDebug() << "ResourceRole"; | 689 | qDebug() << "ResourceRole"; | ||
638 | break; | 690 | break; | ||
Context not available. |