Ticket #8901: cutlist_undo_stack_v1.patch
File cutlist_undo_stack_v1.patch, 19.1 KB (added by , 14 years ago) |
---|
-
libs/libmythtv/deletemap.h
4 4 #include "programinfo.h" 5 5 #include "playercontext.h" 6 6 7 class DeleteMap; 8 9 typedef struct DeleteMapUndoEntry 10 { 11 frm_dir_map_t deleteMap; 12 QString message; // how we got from previous map to this map 13 DeleteMapUndoEntry(frm_dir_map_t dm, QString msg); 14 DeleteMapUndoEntry(void); 15 } DeleteMapUndoEntry; 16 17 class DeleteMapUndoStack 18 { 19 public: 20 DeleteMapUndoStack(DeleteMap *map) 21 : m_map(map), m_undoStackPointer(-1) { 22 Push(""); 23 } 24 void Push(QString message); 25 bool Undo(void); 26 bool Redo(void); 27 bool HasUndo(void); 28 bool HasRedo(void); 29 QString GetUndoMessage(void); 30 QString GetRedoMessage(void); 31 32 private: 33 DeleteMap *m_map; 34 // Invariant: m_undoStack[m_undoStackPointer].deleteMap == *m_map 35 // where m_deleteMap is from the current DeleteMap. 36 QVector<DeleteMapUndoEntry> m_undoStack; 37 int m_undoStackPointer; 38 }; 39 7 40 class DeleteMap 8 41 { 42 friend class DeleteMapUndoStack; 9 43 public: 10 44 DeleteMap(): m_editing(false), m_nextCutStart(0), m_changed(true), 11 45 m_seekamountpos(4), m_seekamount(30) { } 12 46 13 47 bool HandleAction(QString &action, uint64_t frame, uint64_t played, 14 uint64_t total, double rate); 48 uint64_t total, double rate, 49 DeleteMapUndoStack *undoStack); 15 50 int GetSeekAmount(void) { return m_seekamount; } 16 51 void UpdateSeekAmount(int change, double framerate); 17 52 void SetSeekAmount(int amount) { m_seekamount = amount; } … … 25 60 bool IsFileEditing(PlayerContext *ctx); 26 61 bool IsEmpty(void); 27 62 28 void SetMap(const frm_dir_map_t &map); 29 void LoadCommBreakMap(uint64_t total, frm_dir_map_t &map); 63 void SetMap(const frm_dir_map_t &map, DeleteMapUndoStack *undoStack); 64 void LoadCommBreakMap(uint64_t total, frm_dir_map_t &map, 65 DeleteMapUndoStack *undoStack); 30 66 void SaveMap(uint64_t total, PlayerContext *ctx); 31 void LoadMap(uint64_t total, PlayerContext *ctx); 67 void LoadMap(uint64_t total, PlayerContext *ctx, 68 DeleteMapUndoStack *undoStack); 32 69 void CleanMap(uint64_t total); 33 70 34 void Clear(void); 35 void ReverseAll(uint64_t total); 36 void Add(uint64_t frame, uint64_t total, MarkTypes type); 37 void NewCut(uint64_t frame, uint64_t total); 38 void Delete(uint64_t frame, uint64_t total); 39 void Reverse(uint64_t frame, uint64_t total); 40 void MoveRelative(uint64_t frame, uint64_t total, bool right); 41 void Move(uint64_t frame, uint64_t to, uint64_t total); 71 void Clear(DeleteMapUndoStack *undoStack); 72 void ReverseAll(uint64_t total, DeleteMapUndoStack *undoStack); 73 void Add(uint64_t frame, uint64_t total, MarkTypes type, 74 DeleteMapUndoStack *undoStack, QString message); 75 void NewCut(uint64_t frame, uint64_t total, DeleteMapUndoStack *undoStack); 76 void Delete(uint64_t frame, uint64_t total, 77 DeleteMapUndoStack *undoStack, QString message); 78 void Reverse(uint64_t frame, uint64_t total, DeleteMapUndoStack *undoStack); 79 void MoveRelative(uint64_t frame, uint64_t total, bool right, 80 DeleteMapUndoStack *undoStack); 81 void Move(uint64_t frame, uint64_t to, uint64_t total, DeleteMapUndoStack *undoStack); 42 82 43 83 bool IsInDelete(uint64_t frame); 44 84 uint64_t GetNearestMark(uint64_t frame, uint64_t total, bool right); -
libs/libmythtv/mythplayer.h
301 301 uint64_t GetNearestMark(uint64_t frame, bool right); 302 302 bool IsTemporaryMark(uint64_t frame); 303 303 bool HasTemporaryMark(void); 304 bool DeleteMapHasUndo(void) { return deleteMapUndoStack.HasUndo(); } 305 bool DeleteMapHasRedo(void) { return deleteMapUndoStack.HasRedo(); } 306 QString DeleteMapGetUndoMessage(void) { return deleteMapUndoStack.GetUndoMessage(); } 307 QString DeleteMapGetRedoMessage(void) { return deleteMapUndoStack.GetRedoMessage(); } 304 308 305 309 // Decoder stuff.. 306 310 VideoFrame *GetNextVideoFrame(bool allow_unsafe = true); … … 652 656 // Manual editing 653 657 DeleteMap deleteMap; 654 658 bool pausedBeforeEdit; 659 DeleteMapUndoStack deleteMapUndoStack; 655 660 656 661 // Playback (output) speed control 657 662 /// Lock for next_play_speed and next_normal_speed -
libs/libmythtv/tv_play.cpp
774 774 "Jump back 10x the normal amount"), ",,<"); 775 775 REG_KEY("TV Editing", "BIGJUMPFWD", QT_TRANSLATE_NOOP("MythControls", 776 776 "Jump forward 10x the normal amount"), ">,."); 777 REG_KEY("TV Editing", "UNDO", QT_TRANSLATE_NOOP("MythControls", 778 "Undo"), "Ctrl+Z"); 779 REG_KEY("TV Editing", "REDO", QT_TRANSLATE_NOOP("MythControls", 780 "Redo"), "Ctrl+Y"); 777 781 778 782 /* Teletext keys */ 779 783 REG_KEY("Teletext Menu", "NEXTPAGE", QT_TRANSLATE_NOOP("MythControls", … … 9310 9314 if ("EDIT_CUT_POINTS" == type) 9311 9315 osd->DialogAddButton(QObject::tr("Cut List Options"), 9312 9316 "DIALOG_CUTPOINT_CUTLISTOPTIONS_0", true); 9317 if (ctx->player->DeleteMapHasUndo()) 9318 osd->DialogAddButton(QObject::tr("Undo") + " - " + 9319 ctx->player->DeleteMapGetUndoMessage(), 9320 QString("DIALOG_CUTPOINT_UNDO_0")); 9321 if (ctx->player->DeleteMapHasRedo()) 9322 osd->DialogAddButton(QObject::tr("Redo") + " - " + 9323 ctx->player->DeleteMapGetRedoMessage(), 9324 QString("DIALOG_CUTPOINT_REDO_0")); 9313 9325 } 9314 9326 else if ("CUT_LIST_OPTIONS" == type) 9315 9327 { -
libs/libmythtv/deletemap.cpp
10 10 #define EDIT_CHECK if(!m_editing) \ 11 11 { VERBOSE(VB_IMPORTANT, LOC_ERR + "Cannot edit outside editmode."); return; } 12 12 13 DeleteMapUndoEntry::DeleteMapUndoEntry(frm_dir_map_t dm, QString msg) : 14 deleteMap(dm), message(msg) { } 15 16 DeleteMapUndoEntry::DeleteMapUndoEntry(void) 17 { 18 frm_dir_map_t dm; 19 deleteMap = dm; 20 message = ""; 21 } 22 23 void DeleteMapUndoStack::Push(QString message) 24 { 25 DeleteMapUndoEntry entry(m_map->m_deleteMap, message); 26 // Remove all "redo" entries 27 while (m_undoStack.size() > m_undoStackPointer + 1) 28 m_undoStack.pop_back(); 29 m_undoStack.append(entry); 30 m_undoStackPointer ++; 31 } 32 33 bool DeleteMapUndoStack::Undo(void) 34 { 35 if (!HasUndo()) 36 return false; 37 m_undoStackPointer --; 38 m_map->m_deleteMap = m_undoStack[m_undoStackPointer].deleteMap; 39 m_map->m_changed = true; 40 return true; 41 } 42 43 bool DeleteMapUndoStack::Redo(void) 44 { 45 if (!HasRedo()) 46 return false; 47 m_undoStackPointer ++; 48 m_map->m_deleteMap = m_undoStack[m_undoStackPointer].deleteMap; 49 m_map->m_changed = true; 50 return true; 51 } 52 53 bool DeleteMapUndoStack::HasUndo(void) 54 { 55 return (m_undoStackPointer > 0); 56 } 57 58 bool DeleteMapUndoStack::HasRedo(void) 59 { 60 return (m_undoStackPointer < m_undoStack.size() - 1); 61 } 62 63 QString DeleteMapUndoStack::GetUndoMessage(void) 64 { 65 QString result; 66 if (HasUndo()) 67 result = m_undoStack[m_undoStackPointer].message; 68 else 69 result = QObject::tr("(No more undo operations)"); 70 return result; 71 } 72 73 QString DeleteMapUndoStack::GetRedoMessage(void) 74 { 75 QString result; 76 if (HasRedo()) 77 result = m_undoStack[m_undoStackPointer + 1].message; 78 else 79 result = QObject::tr("(No more redo operations)"); 80 return result; 81 } 82 13 83 bool DeleteMap::HandleAction(QString &action, uint64_t frame, 14 uint64_t played, uint64_t total, double rate) 84 uint64_t played, uint64_t total, double rate, 85 DeleteMapUndoStack *undoStack) 15 86 { 16 87 bool handled = true; 17 88 if (action == "REVERSE") 18 Reverse(frame, total );89 Reverse(frame, total, undoStack); 19 90 else if (action == "UP") 20 91 UpdateSeekAmount(1, rate); 21 92 else if (action == "DOWN") 22 93 UpdateSeekAmount(-1, rate); 23 94 else if (action == "CLEARMAP") 24 Clear( );95 Clear(undoStack); 25 96 else if (action == "INVERTMAP") 26 ReverseAll(total );97 ReverseAll(total, undoStack); 27 98 else if (action == "MOVEPREV") 28 MoveRelative(frame, total, false );99 MoveRelative(frame, total, false, undoStack); 29 100 else if (action == "MOVENEXT") 30 MoveRelative(frame, total, true );101 MoveRelative(frame, total, true, undoStack); 31 102 else if (action == "CUTTOBEGINNING") 32 Add(frame, total, MARK_CUT_END); 103 Add(frame, total, MARK_CUT_END, undoStack, 104 QObject::tr("Cut to beginning")); 33 105 else if (action == "CUTTOEND") 34 Add(frame, total, MARK_CUT_START); 106 Add(frame, total, MARK_CUT_START, undoStack, 107 QObject::tr("Cut to beginning")); 35 108 else if (action == "NEWCUT") 36 NewCut(frame, total );109 NewCut(frame, total, undoStack); 37 110 else if (action == "DELETE") 38 Delete(frame, total); 111 Delete(frame, total, undoStack, 112 QObject::tr("Delete cut area")); 113 else if (action == "UNDO") 114 undoStack->Undo(); 115 else if (action == "REDO") 116 undoStack->Redo(); 39 117 else 40 118 handled = false; 41 119 return handled; … … 150 228 } 151 229 152 230 /// Clears the deleteMap. 153 void DeleteMap::Clear( void)231 void DeleteMap::Clear(DeleteMapUndoStack *undoStack) 154 232 { 155 233 m_deleteMap.clear(); 156 234 m_changed = true; 235 if (undoStack) 236 undoStack->Push(QObject::tr("Clear cut list")); 157 237 } 158 238 159 239 /// Reverses the direction of each mark in the map. 160 void DeleteMap::ReverseAll(uint64_t total )240 void DeleteMap::ReverseAll(uint64_t total, DeleteMapUndoStack *undoStack) 161 241 { 162 242 EDIT_CHECK 163 243 frm_dir_map_t::Iterator it = m_deleteMap.begin(); … … 165 245 Add(it.key(), it.value() == MARK_CUT_END ? MARK_CUT_START : 166 246 MARK_CUT_END); 167 247 CleanMap(total); 248 if (undoStack) 249 undoStack->Push(QObject::tr("Invert cut list")); 168 250 } 169 251 170 252 /** … … 172 254 * existing redundant mark of that type is removed. This simplifies 173 255 * the cleanup code. 174 256 */ 175 void DeleteMap::Add(uint64_t frame, uint64_t total, MarkTypes type) 257 void DeleteMap::Add(uint64_t frame, uint64_t total, MarkTypes type, 258 DeleteMapUndoStack *undoStack, QString message) 176 259 { 177 260 EDIT_CHECK 178 261 if ((MARK_CUT_START != type) && (MARK_CUT_END != type) && … … 186 269 { 187 270 // Delete the temporary mark before putting a real mark at its 188 271 // location 189 Delete(frame, total );272 Delete(frame, total, NULL, ""); 190 273 } 191 274 else // Don't add a mark on top of a mark 192 275 return; … … 241 324 Delete((uint64_t)remove); 242 325 Add(frame, type); 243 326 CleanMap(total); 327 if (undoStack) 328 undoStack->Push(message); 244 329 } 245 330 246 331 /// Remove the mark at the given frame. 247 void DeleteMap::Delete(uint64_t frame, uint64_t total) 332 void DeleteMap::Delete(uint64_t frame, uint64_t total, 333 DeleteMapUndoStack *undoStack, QString message) 248 334 { 249 335 EDIT_CHECK 250 336 if (m_deleteMap.isEmpty()) … … 271 357 if (prev != next) 272 358 Delete(next); 273 359 CleanMap(total); 360 if (undoStack) 361 undoStack->Push(message); 274 362 } 275 363 276 364 /// Reverse the direction of the mark at the given frame. 277 void DeleteMap::Reverse(uint64_t frame, uint64_t total) 365 void DeleteMap::Reverse(uint64_t frame, uint64_t total, 366 DeleteMapUndoStack *undoStack) 278 367 { 279 368 EDIT_CHECK 280 369 int type = Delete(frame); 281 Add(frame, total, type == MARK_CUT_END ? MARK_CUT_START : MARK_CUT_END); 370 Add(frame, total, type == MARK_CUT_END ? MARK_CUT_START : MARK_CUT_END, 371 NULL, ""); 372 if (undoStack) 373 undoStack->Push(QObject::tr("Reverse mark direction")); 282 374 } 283 375 284 376 /// Add a new cut marker (to start or end a cut region) 285 void DeleteMap::NewCut(uint64_t frame, uint64_t total) 377 void DeleteMap::NewCut(uint64_t frame, uint64_t total, 378 DeleteMapUndoStack *undoStack) 286 379 { 287 380 EDIT_CHECK 288 381 … … 376 469 Add(frame, MARK_PLACEHOLDER); 377 470 378 471 CleanMap(total); 472 if (undoStack) 473 undoStack->Push(QObject::tr("New cut")); 379 474 } 380 475 381 476 /// Move the previous (!right) or next (right) cut to frame. 382 void DeleteMap::MoveRelative(uint64_t frame, uint64_t total, bool right) 477 void DeleteMap::MoveRelative(uint64_t frame, uint64_t total, bool right, 478 DeleteMapUndoStack *undoStack) 383 479 { 384 480 frm_dir_map_t::Iterator it = m_deleteMap.find(frame); 385 481 if (it != m_deleteMap.end()) … … 397 493 { 398 494 // If on a mark, don't collapse a cut region to 0; 399 495 // instead, delete the region 400 Delete(frame, total );496 Delete(frame, total, undoStack, QObject::tr("Delete cut area")); 401 497 return; 402 498 } 403 499 else if (MARK_PLACEHOLDER == type) 404 500 { 405 501 // Delete the temporary mark before putting a real mark at its 406 502 // location 407 Delete(frame, total );503 Delete(frame, total, NULL, ""); 408 504 } 409 505 } 410 506 411 507 uint64_t from = GetNearestMark(frame, total, right); 412 Move(from, frame, total );508 Move(from, frame, total, undoStack); 413 509 } 414 510 415 511 /// Move an existing mark to a new frame. 416 void DeleteMap::Move(uint64_t frame, uint64_t to, uint64_t total) 512 void DeleteMap::Move(uint64_t frame, uint64_t to, uint64_t total, 513 DeleteMapUndoStack *undoStack) 417 514 { 418 515 EDIT_CHECK 419 516 MarkTypes type = Delete(frame); … … 424 521 else if (frame == total) 425 522 type = MARK_CUT_END; 426 523 } 427 Add(to, total, type );524 Add(to, total, type, undoStack, QObject::tr("Move mark")); 428 525 } 429 526 430 527 /// Private addition to the deleteMap. … … 577 674 } 578 675 579 676 /// Use the given map. 580 void DeleteMap::SetMap(const frm_dir_map_t &map )677 void DeleteMap::SetMap(const frm_dir_map_t &map, DeleteMapUndoStack *undoStack) 581 678 { 582 Clear( );679 Clear(NULL); 583 680 m_deleteMap = map; 584 681 m_deleteMap.detach(); 682 if (undoStack) 683 undoStack->Push(QObject::tr("Set new cut list")); 585 684 } 586 685 587 686 /// Loads the given commercial break map into the deleteMap. 588 void DeleteMap::LoadCommBreakMap(uint64_t total, frm_dir_map_t &map) 687 void DeleteMap::LoadCommBreakMap(uint64_t total, frm_dir_map_t &map, 688 DeleteMapUndoStack *undoStack) 589 689 { 590 Clear( );690 Clear(NULL); 591 691 frm_dir_map_t::Iterator it = map.begin(); 592 692 for ( ; it != map.end(); ++it) 593 693 Add(it.key(), it.value() == MARK_COMM_START ? 594 694 MARK_CUT_START : MARK_CUT_END); 595 695 CleanMap(total); 696 if (undoStack) 697 undoStack->Push(QObject::tr("Load commskip list")); 596 698 } 597 699 598 700 /// Loads the delete map from the database. 599 void DeleteMap::LoadMap(uint64_t total, PlayerContext *ctx) 701 void DeleteMap::LoadMap(uint64_t total, PlayerContext *ctx, 702 DeleteMapUndoStack *undoStack) 600 703 { 601 704 if (!ctx || !ctx->playingInfo || gCoreContext->IsDatabaseIgnored()) 602 705 return; 603 706 604 Clear( );707 Clear(NULL); 605 708 ctx->LockPlayingInfo(__FILE__, __LINE__); 606 709 ctx->playingInfo->QueryCutList(m_deleteMap); 607 710 ctx->UnlockPlayingInfo(__FILE__, __LINE__); 608 711 CleanMap(total); 712 if (undoStack) 713 undoStack->Push(QObject::tr("Load cut list")); 609 714 } 610 715 611 716 /// Saves the delete map to the database. -
libs/libmythtv/mythplayer.cpp
227 227 videoFilters(NULL), FiltMan(new FilterManager()), 228 228 229 229 forcePositionMapSync(false), pausedBeforeEdit(false), 230 deleteMapUndoStack(&deleteMap), 230 231 // Playback (output) speed control 231 232 decoder_lock(QMutex::Recursive), 232 233 next_play_speed(1.0f), next_normal_speed(true), … … 1020 1021 if (ret > 0) 1021 1022 { 1022 1023 hasFullPositionMap = true; 1023 deleteMap.LoadMap(totalFrames, player_ctx );1024 deleteMap.LoadMap(totalFrames, player_ctx, &deleteMapUndoStack); 1024 1025 deleteMap.TrackerReset(0, totalFrames); 1025 1026 } 1026 1027 … … 3393 3394 if (save) 3394 3395 deleteMap.SaveMap(totalFrames, player_ctx); 3395 3396 else 3396 deleteMap.LoadMap(totalFrames, player_ctx );3397 deleteMap.LoadMap(totalFrames, player_ctx, &deleteMapUndoStack); 3397 3398 deleteMap.TrackerReset(framesPlayed, totalFrames); 3398 3399 deleteMap.SetFileEditing(player_ctx, false); 3399 3400 if (!pausedBeforeEdit) … … 3436 3437 { 3437 3438 frm_dir_map_t map; 3438 3439 commBreakMap.GetMap(map); 3439 deleteMap.LoadCommBreakMap(totalFrames, map); 3440 deleteMap.LoadCommBreakMap(totalFrames, map, 3441 &deleteMapUndoStack); 3440 3442 } 3441 3443 } 3442 3444 else if (action == "PREVCUT") … … 3478 3480 } 3479 3481 else if (action == "SELECT") 3480 3482 { 3481 deleteMap.NewCut(frame, totalFrames );3483 deleteMap.NewCut(frame, totalFrames, &deleteMapUndoStack); 3482 3484 refresh = true; 3483 3485 } 3484 3486 else if (action == "DELETE") 3485 3487 { 3486 3488 if (IsInDelete(frame)) 3487 3489 { 3488 deleteMap.Delete(frame, totalFrames); 3490 deleteMap.Delete(frame, totalFrames, &deleteMapUndoStack, 3491 QObject::tr("Delete cut area")); 3489 3492 refresh = true; 3490 3493 } 3491 3494 } 3492 3495 else if (action == "REVERT") 3493 3496 { 3494 deleteMap.LoadMap(totalFrames, player_ctx );3497 deleteMap.LoadMap(totalFrames, player_ctx, &deleteMapUndoStack); 3495 3498 refresh = true; 3496 3499 } 3497 3500 else if (action == "REVERTEXIT") … … 3511 3514 } 3512 3515 else 3513 3516 handled = deleteMap.HandleAction(action, frame, framesPlayed, 3514 totalFrames, video_frame_rate); 3517 totalFrames, video_frame_rate, 3518 &deleteMapUndoStack); 3515 3519 } 3516 3520 3517 3521 if (handled && refresh) … … 3834 3838 else 3835 3839 { 3836 3840 uint64_t oldnumber = number; 3837 deleteMap.LoadMap(totalFrames, player_ctx );3841 deleteMap.LoadMap(totalFrames, player_ctx, &deleteMapUndoStack); 3838 3842 commBreakMap.LoadMap(player_ctx, framesPlayed); 3839 3843 3840 3844 bool started_in_break_map = false; … … 4030 4034 4031 4035 void MythPlayer::SetCutList(const frm_dir_map_t &newCutList) 4032 4036 { 4033 deleteMap.SetMap(newCutList );4037 deleteMap.SetMap(newCutList, &deleteMapUndoStack); 4034 4038 } 4035 4039 4036 4040 bool MythPlayer::WriteStoredData(RingBuffer *outRingBuffer,