MythTV  0.26-pre
deletemap.cpp
Go to the documentation of this file.
00001 #include <cmath>
00002 #include <stdint.h>
00003 
00004 #include "mythlogging.h"
00005 #include "mythcontext.h"
00006 #include "osd.h"
00007 #include "deletemap.h"
00008 #include "mythplayer.h"
00009 
00010 #define LOC     QString("DelMap: ")
00011 #define EDIT_CHECK do { \
00012     if(!m_editing) { \
00013         LOG(VB_GENERAL, LOG_ERR, LOC + "Cannot edit outside edit mode."); \
00014         return; \
00015     } \
00016 } while(0)
00017 
00018 DeleteMapUndoEntry::DeleteMapUndoEntry(frm_dir_map_t dm, QString msg) :
00019     deleteMap(dm), message(msg) { }
00020 
00021 DeleteMapUndoEntry::DeleteMapUndoEntry(void)
00022 {
00023     frm_dir_map_t dm;
00024     deleteMap = dm;
00025     message = "";
00026 }
00027 
00028 void DeleteMap::Push(QString undoMessage)
00029 {
00030     DeleteMapUndoEntry entry(m_deleteMap, undoMessage);
00031     // Remove all "redo" entries
00032     while (m_undoStack.size() > m_undoStackPointer + 1)
00033         m_undoStack.pop_back();
00034     m_undoStack.append(entry);
00035     m_undoStackPointer ++;
00036     SaveMap(0, true);
00037 }
00038 
00039 bool DeleteMap::Undo(void)
00040 {
00041     if (!HasUndo())
00042         return false;
00043     m_undoStackPointer --;
00044     m_deleteMap = m_undoStack[m_undoStackPointer].deleteMap;
00045     m_changed = true;
00046     SaveMap(0, true);
00047     return true;
00048 }
00049 
00050 bool DeleteMap::Redo(void)
00051 {
00052     if (!HasRedo())
00053         return false;
00054     m_undoStackPointer ++;
00055     m_deleteMap = m_undoStack[m_undoStackPointer].deleteMap;
00056     m_changed = true;
00057     SaveMap(0, true);
00058     return true;
00059 }
00060 
00061 QString DeleteMap::GetUndoMessage(void) const
00062 {
00063     return (HasUndo() ? m_undoStack[m_undoStackPointer].message :
00064             QObject::tr("(Nothing to undo)"));
00065 }
00066 
00067 QString DeleteMap::GetRedoMessage(void) const
00068 {
00069     return (HasRedo() ? m_undoStack[m_undoStackPointer + 1].message :
00070             QObject::tr("(Nothing to redo)"));
00071 }
00072 
00073 bool DeleteMap::HandleAction(QString &action, uint64_t frame,
00074                              uint64_t played, uint64_t total, double rate)
00075 {
00076     bool handled = true;
00077     if (action == ACTION_UP)
00078         UpdateSeekAmount(1, rate);
00079     else if (action == ACTION_DOWN)
00080         UpdateSeekAmount(-1, rate);
00081     else if (action == ACTION_CLEARMAP)
00082         Clear(QObject::tr("Clear Cuts"));
00083     else if (action == ACTION_INVERTMAP)
00084         ReverseAll(total);
00085     else if (action == "MOVEPREV")
00086         MoveRelative(frame, total, false);
00087     else if (action == "MOVENEXT")
00088         MoveRelative(frame, total, true);
00089     else if (action == "CUTTOBEGINNING")
00090         Add(frame, total, MARK_CUT_END, QObject::tr("Cut to Beginning"));
00091     else if (action == "CUTTOEND")
00092     {
00093         Add(frame, total, MARK_CUT_START, QObject::tr("Cut to End"));
00094         // If the recording is still in progress, add an explicit end
00095         // mark at the end.
00096         if (m_ctx->player && m_ctx->player->IsWatchingInprogress())
00097             Add(total - 1, total, MARK_CUT_END, "");
00098     }
00099     else if (action == "NEWCUT")
00100         NewCut(frame, total);
00101     else if (action == "DELETE")
00102         Delete(frame, total, QObject::tr("Delete"));
00103     else if (action == "UNDO")
00104         Undo();
00105     else if (action == "REDO")
00106         Redo();
00107     else
00108         handled = false;
00109     return handled;
00110 }
00111 
00112 void DeleteMap::UpdateSeekAmount(int change, double framerate)
00113 {
00114     m_seekamountpos += change;
00115     if (m_seekamountpos > 9)
00116         m_seekamountpos = 9;
00117     if (m_seekamountpos < 0)
00118         m_seekamountpos = 0;
00119 
00120     m_seekText = "";
00121     switch (m_seekamountpos)
00122     {
00123         case 0: m_seekText = QObject::tr("cut point"); m_seekamount = -2; break;
00124         case 1: m_seekText = QObject::tr("keyframe"); m_seekamount = -1; break;
00125         case 2: m_seekText = QObject::tr("1 frame"); m_seekamount = 1; break;
00126         case 3: m_seekText = QObject::tr("0.5 seconds"); m_seekamount = (int)roundf(framerate / 2); break;
00127         case 4: m_seekText = QObject::tr("%n second(s)", "", 1); m_seekamount = (int)roundf(framerate); break;
00128         case 5: m_seekText = QObject::tr("%n second(s)", "", 5); m_seekamount = (int)roundf(framerate * 5); break;
00129         case 6: m_seekText = QObject::tr("%n second(s)", "", 20); m_seekamount = (int)roundf(framerate * 20); break;
00130         case 7: m_seekText = QObject::tr("%n minute(s)", "", 1); m_seekamount = (int)roundf(framerate * 60); break;
00131         case 8: m_seekText = QObject::tr("%n minute(s)", "", 5); m_seekamount = (int)roundf(framerate * 300); break;
00132         case 9: m_seekText = QObject::tr("%n minute(s)", "", 10); m_seekamount = (int)roundf(framerate * 600); break;
00133         default: m_seekText = QObject::tr("error"); m_seekamount = (int)roundf(framerate); break;
00134     }
00135 }
00136 
00137 static QString createTimeString(uint64_t frame, uint64_t total,
00138                                 double frame_rate, bool full_resolution)
00139 {
00140     int secs   = (int)(frame / frame_rate);
00141     int frames = frame - (int)(secs * frame_rate);
00142     int totalSecs = (int)(total / frame_rate);
00143     QString timestr;
00144     if (totalSecs >= 3600)
00145         timestr = QString::number(secs / 3600) + ":";
00146     timestr += QString("%1").arg((secs / 60) % 60, 2, 10, QChar(48)) +
00147         QString(":%1").arg(secs % 60, 2, 10, QChar(48));
00148     if (full_resolution)
00149         timestr += QString(".%1").arg(frames, 2, 10, QChar(48));
00150     return timestr;
00151 }
00152 
00157 void DeleteMap::UpdateOSD(uint64_t frame, uint64_t total, double frame_rate,
00158                           OSD *osd)
00159 {
00160     if (!osd || !m_ctx)
00161         return;
00162     CleanMap(total);
00163 
00164     InfoMap infoMap;
00165     m_ctx->LockPlayingInfo(__FILE__, __LINE__);
00166     if (m_ctx->playingInfo)
00167         m_ctx->playingInfo->ToMap(infoMap);
00168     infoMap.detach();
00169     m_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
00170 
00171     QString cutmarker = " ";
00172     if (IsInDelete(frame))
00173         cutmarker = QObject::tr("cut");
00174 
00175     QString timestr = createTimeString(frame, total, frame_rate, true);
00176     uint64_t relTotal = TranslatePositionAbsToRel(total);
00177     QString relTimeDisplay = createTimeString(TranslatePositionAbsToRel(frame),
00178                                               relTotal, frame_rate, false);
00179     QString relLengthDisplay = createTimeString(relTotal,
00180                                                 relTotal, frame_rate, false);
00181     infoMap["timedisplay"]  = timestr;
00182     infoMap["framedisplay"] = QString::number(frame);
00183     infoMap["cutindicator"] = cutmarker;
00184     infoMap["title"]        = QObject::tr("Edit");
00185     infoMap["seekamount"]   = m_seekText;;
00186     infoMap["reltimedisplay"] = relTimeDisplay;
00187     infoMap["rellengthdisplay"] = relLengthDisplay;
00188     infoMap["fulltimedisplay"] = timestr + " (" +
00189         QObject::tr("%1 of %2").arg(relTimeDisplay).arg(relLengthDisplay) + ")";
00190 
00191     QHash<QString,float> posMap;
00192     posMap.insert("position", (float)((double)frame/(double)total));
00193     osd->SetValues("osd_program_editor", posMap, kOSDTimeout_None);
00194     osd->SetText("osd_program_editor", infoMap,  kOSDTimeout_None);
00195     if (m_changed || total != m_cachedTotalForOSD)
00196         osd->SetRegions("osd_program_editor", m_deleteMap, total);
00197     m_changed = false;
00198     m_cachedTotalForOSD = total;
00199 }
00200 
00202 void DeleteMap::SetEditing(bool edit, OSD *osd)
00203 {
00204     if (osd && !edit)
00205         osd->HideWindow("osd_program_editor");
00206     m_editing = edit;
00207 }
00208 
00210 void DeleteMap::SetFileEditing(bool edit)
00211 {
00212     if (m_ctx)
00213     {
00214         m_ctx->LockPlayingInfo(__FILE__, __LINE__);
00215         if (m_ctx->playingInfo)
00216             m_ctx->playingInfo->SetEditing(edit);
00217         m_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
00218     }
00219 }
00220 
00222 bool DeleteMap::IsFileEditing(void)
00223 {
00224     bool result = false;
00225     if (m_ctx)
00226     {
00227         m_ctx->LockPlayingInfo(__FILE__, __LINE__);
00228         if (m_ctx->playingInfo)
00229             result = m_ctx->playingInfo->QueryIsEditing();
00230         m_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
00231     }
00232     return result;
00233 }
00234 
00235 bool DeleteMap::IsEmpty(void) const
00236 {
00237     return m_deleteMap.empty();
00238 }
00239 
00241 void DeleteMap::Clear(QString undoMessage)
00242 {
00243     m_deleteMap.clear();
00244     m_changed = true;
00245     if (!undoMessage.isEmpty())
00246         Push(undoMessage);
00247 }
00248 
00250 void DeleteMap::ReverseAll(uint64_t total)
00251 {
00252     EDIT_CHECK;
00253     frm_dir_map_t::Iterator it = m_deleteMap.begin();
00254     for ( ; it != m_deleteMap.end(); ++it)
00255         Add(it.key(), it.value() == MARK_CUT_END ? MARK_CUT_START :
00256                                                    MARK_CUT_END);
00257     CleanMap(total);
00258     Push(QObject::tr("Reverse Cuts"));
00259 }
00260 
00266 void DeleteMap::Add(uint64_t frame, uint64_t total, MarkTypes type,
00267                     QString undoMessage)
00268 {
00269     EDIT_CHECK;
00270     if ((MARK_CUT_START != type) && (MARK_CUT_END != type) &&
00271         (MARK_PLACEHOLDER != type))
00272         return;
00273 
00274     frm_dir_map_t::Iterator find_temporary = m_deleteMap.find(frame);
00275     if (find_temporary != m_deleteMap.end())
00276     {
00277         if (MARK_PLACEHOLDER == find_temporary.value())
00278         {
00279             // Delete the temporary mark before putting a real mark at its
00280             // location
00281             Delete(frame, total);
00282         }
00283         else // Don't add a mark on top of a mark
00284             return;
00285     }
00286 
00287     int       lasttype  = MARK_UNSET;
00288     long long lastframe = -1;
00289     long long remove    = -1;
00290     QMutableMapIterator<uint64_t, MarkTypes> it(m_deleteMap);
00291 
00292     if (type == MARK_CUT_END)
00293     {
00294         // remove curent end marker if it exists
00295         while (it.hasNext())
00296         {
00297             it.next();
00298             if (it.key() > frame)
00299             {
00300                 if ((lasttype == MARK_CUT_END) && (lastframe > -1))
00301                     remove = lastframe;
00302                 break;
00303             }
00304             lasttype  = it.value();
00305             lastframe = it.key();
00306         }
00307         if ((remove < 0) && (lasttype == MARK_CUT_END) &&
00308             (lastframe > -1) && (lastframe < (int64_t)frame))
00309             remove = lastframe;
00310     }
00311     else if (type == MARK_CUT_START)
00312     {
00313         // remove curent start marker if it exists
00314         it.toBack();
00315         while (it.hasPrevious())
00316         {
00317             it.previous();
00318             if (it.key() <= frame)
00319             {
00320                 if (lasttype == MARK_CUT_START && (lastframe > -1))
00321                     remove = lastframe;
00322                 break;
00323             }
00324             lasttype  = it.value();
00325             lastframe = it.key();
00326         }
00327         if ((remove < 0) && (lasttype == MARK_CUT_START) &&
00328             (lastframe > -1) && (lastframe > (int64_t)frame))
00329             remove = lastframe;
00330     }
00331 
00332     if (remove > -1)
00333         Delete((uint64_t)remove);
00334     Add(frame, type);
00335     CleanMap(total);
00336     if (!undoMessage.isEmpty())
00337         Push(undoMessage);
00338 }
00339 
00341 void DeleteMap::Delete(uint64_t frame, uint64_t total, QString undoMessage)
00342 {
00343     EDIT_CHECK;
00344     if (m_deleteMap.isEmpty())
00345         return;
00346 
00347     uint64_t prev = GetNearestMark(frame, total, false);
00348     uint64_t next = GetNearestMark(frame, total, true);
00349 
00350     // If frame is a cut point, GetNearestMark() would return the previous/next
00351     // mark (not this frame), so check to see if we need to use frame, instead
00352     frm_dir_map_t::Iterator it = m_deleteMap.find(frame);
00353     if (it != m_deleteMap.end())
00354     {
00355         int type = it.value();
00356         if (MARK_PLACEHOLDER == type)
00357             next = prev = frame;
00358         else if (MARK_CUT_END == type)
00359             next  = frame;
00360         else if (MARK_CUT_START == type)
00361             prev = frame;
00362     }
00363 
00364     Delete(prev);
00365     if (prev != next)
00366         Delete(next);
00367     CleanMap(total);
00368     if (!undoMessage.isEmpty())
00369         Push(undoMessage);
00370 }
00371 
00373 void DeleteMap::NewCut(uint64_t frame, uint64_t total)
00374 {
00375     EDIT_CHECK;
00376 
00377     // find any existing temporary marker to determine cut range
00378     int64_t existing = -1;
00379     frm_dir_map_t::Iterator it;
00380     for (it = m_deleteMap.begin() ; it != m_deleteMap.end(); ++it)
00381     {
00382         if (MARK_PLACEHOLDER == it.value())
00383         {
00384             existing = it.key();
00385             break;
00386         }
00387     }
00388 
00389     if (existing > -1)
00390     {
00391         uint64_t otherframe = static_cast<uint64_t>(existing);
00392         if (otherframe == frame)
00393             Delete(otherframe);
00394         else
00395         {
00396             uint64_t startframe;
00397             uint64_t endframe;
00398             int64_t cut_start = -1;
00399             int64_t cut_end = -1;
00400             if (IsInDelete(frame))
00401             {
00402                 MarkTypes type = MARK_UNSET;
00403                 cut_start = GetNearestMark(frame, total, false);
00404                 cut_end = GetNearestMark(frame, total, true);
00405                 frm_dir_map_t::Iterator it = m_deleteMap.find(frame);
00406                 if (it != m_deleteMap.end())
00407                     type = it.value();
00408                 if (MARK_CUT_START == type)
00409                 {
00410                     cut_start = frame;
00411                 }
00412                 else if (MARK_CUT_END == type)
00413                 {
00414                     cut_end = frame;
00415                 }
00416             }
00417 
00418             if (otherframe < frame)
00419             {
00420                 startframe = otherframe;
00421                 endframe = cut_end != -1 ? static_cast<uint64_t>(cut_end)
00422                                          : frame;
00423             }
00424             else
00425             {
00426                 startframe = cut_start != -1 ? static_cast<uint64_t>(cut_start)
00427                                              : frame;
00428                 endframe = otherframe;
00429             }
00430 
00431             // Don't place a cut marker on first or last frame; instead cut
00432             // to beginning or end
00433             if (startframe == 1)
00434                 startframe = 0;
00435             if (endframe >= total - 1)
00436                 endframe = total;
00437 
00438             // Don't cut the entire recording
00439             if ((startframe == 0) && (endframe == total))
00440             {
00441                 LOG(VB_GENERAL, LOG_CRIT, LOC +
00442                     "Refusing to cut entire recording.");
00443                 return;
00444             }
00445 
00446             Delete(otherframe);
00447             Add(startframe, MARK_CUT_START);
00448             Add(endframe, MARK_CUT_END);
00449             // Clear out any markers between the start and end frames
00450             otherframe = 0;
00451             frm_dir_map_t::Iterator it = m_deleteMap.find(startframe);
00452             for ( ; it != m_deleteMap.end() && otherframe < endframe; ++it)
00453             {
00454                 otherframe = it.key();
00455                 if ((startframe < otherframe) && (endframe > otherframe))
00456                 {
00457                     LOG(VB_PLAYBACK, LOG_INFO, LOC +
00458                         QString("Deleting bounded marker: %1").arg(otherframe));
00459                     Delete(otherframe);
00460                 }
00461             }
00462         }
00463     }
00464     else
00465         Add(frame, MARK_PLACEHOLDER);
00466 
00467     CleanMap(total);
00468     Push(QObject::tr("New Cut"));
00469 }
00470 
00472 void DeleteMap::MoveRelative(uint64_t frame, uint64_t total, bool right)
00473 {
00474     frm_dir_map_t::Iterator it = m_deleteMap.find(frame);
00475     if (it != m_deleteMap.end())
00476     {
00477         int type = it.value();
00478         if (((MARK_CUT_START == type) && !right) ||
00479             ((MARK_CUT_END == type) && right))
00480         {
00481             // If on a mark, don't move a mark from a different cut region;
00482             // instead, "move" this mark onto itself
00483             return;
00484         }
00485         else if (((MARK_CUT_START == type) && right) ||
00486                  ((MARK_CUT_END == type) && !right))
00487         {
00488             // If on a mark, don't collapse a cut region to 0;
00489             // instead, delete the region
00490             Delete(frame, total, QObject::tr("Delete"));
00491             return;
00492         }
00493         else if (MARK_PLACEHOLDER == type)
00494         {
00495             // Delete the temporary mark before putting a real mark at its
00496             // location
00497             Delete(frame, total);
00498         }
00499     }
00500 
00501     uint64_t from = GetNearestMark(frame, total, right);
00502     Move(from, frame, total);
00503 }
00504 
00506 void DeleteMap::Move(uint64_t frame, uint64_t to, uint64_t total)
00507 {
00508     EDIT_CHECK;
00509     MarkTypes type = Delete(frame);
00510     if (MARK_UNSET == type)
00511     {
00512         if (frame == 0)
00513             type = MARK_CUT_START;
00514         else if (frame == total)
00515             type = MARK_CUT_END;
00516     }
00517     Add(to, total, type, QObject::tr("Move Mark"));
00518 }
00519 
00521 void DeleteMap::Add(uint64_t frame, MarkTypes type)
00522 {
00523     m_deleteMap[frame] = type;
00524     m_changed = true;
00525 }
00526 
00528 MarkTypes DeleteMap::Delete(uint64_t frame)
00529 {
00530     if (m_deleteMap.contains(frame))
00531     {
00532         m_changed = true;
00533         return m_deleteMap.take(frame);
00534     }
00535     return MARK_UNSET;
00536 }
00537 
00542 bool DeleteMap::IsInDelete(uint64_t frame) const
00543 {
00544     if (m_deleteMap.isEmpty())
00545         return false;
00546 
00547     frm_dir_map_t::const_iterator it = m_deleteMap.find(frame);
00548     if (it != m_deleteMap.end())
00549         return true;
00550 
00551     int      lasttype  = MARK_UNSET;
00552     uint64_t lastframe = -1;
00553     for (it = m_deleteMap.begin() ; it != m_deleteMap.end(); ++it)
00554     {
00555         if (it.key() > frame)
00556             return MARK_CUT_END == it.value();
00557         lasttype  = it.value();
00558         lastframe = it.key();
00559     }
00560 
00561     if (lasttype == MARK_CUT_START && lastframe <= frame)
00562         return true;
00563     return false;
00564 }
00565 
00569 bool DeleteMap::IsTemporaryMark(uint64_t frame) const
00570 {
00571     if (m_deleteMap.isEmpty())
00572         return false;
00573 
00574     frm_dir_map_t::const_iterator it = m_deleteMap.find(frame);
00575     return (it != m_deleteMap.end()) && (MARK_PLACEHOLDER == it.value());
00576 }
00577 
00584 uint64_t DeleteMap::GetNearestMark(
00585     uint64_t frame, uint64_t total, bool right,
00586     bool *hasMark) const
00587 {
00588     uint64_t result;
00589     if (hasMark)
00590         *hasMark = true;
00591     frm_dir_map_t::const_iterator it = m_deleteMap.begin();
00592     if (right)
00593     {
00594         result = total;
00595         for (; it != m_deleteMap.end(); ++it)
00596             if (it.key() > frame)
00597                 return it.key();
00598         if (hasMark)
00599             *hasMark = false;
00600     }
00601     else
00602     {
00603         result = 0;
00604         for (; it != m_deleteMap.end(); ++it)
00605         {
00606             if (it.key() >= frame)
00607                 return result;
00608             result = it.key();
00609         }
00610     }
00611     return result;
00612 }
00613 
00617 bool DeleteMap::HasTemporaryMark(void) const
00618 {
00619     if (!m_deleteMap.isEmpty())
00620     {
00621         frm_dir_map_t::const_iterator it = m_deleteMap.begin();
00622         for ( ; it != m_deleteMap.end(); ++it)
00623             if (MARK_PLACEHOLDER == it.value())
00624                 return true;
00625     }
00626 
00627     return false;
00628 }
00629 
00635 void DeleteMap::CleanMap(uint64_t total)
00636 {
00637     if (IsEmpty())
00638         return;
00639 
00640     Delete(0);
00641     Delete(total);
00642 
00643     bool clear = false;
00644     while (!IsEmpty() && !clear)
00645     {
00646         clear = true;
00647         int     lasttype  = MARK_UNSET;
00648         int64_t lastframe = -1;
00649         int64_t tempframe = -1;
00650         frm_dir_map_t::iterator it = m_deleteMap.begin();
00651         for ( ; it != m_deleteMap.end(); ++it)
00652         {
00653             int      thistype  = it.value();
00654             uint64_t thisframe = it.key();
00655             if (thisframe >= total)
00656             {
00657                 Delete(thisframe);
00658             }
00659             else if (lasttype == thistype)
00660             {
00661                 Delete(thistype == MARK_CUT_END ? thisframe :
00662                                                   (uint64_t)lastframe);
00663                 clear = false;
00664                 break;
00665             }
00666             if (MARK_PLACEHOLDER == thistype)
00667             {
00668                 if (tempframe > 0)
00669                     Delete(tempframe);
00670                 tempframe = thisframe;
00671             }
00672             else
00673             {
00674                 lasttype  = thistype;
00675                 lastframe = thisframe;
00676             }
00677         }
00678     }
00679 }
00680 
00682 void DeleteMap::SetMap(const frm_dir_map_t &map)
00683 {
00684     Clear();
00685     m_deleteMap = map;
00686     m_deleteMap.detach();
00687     // Can't save an undo point for SetMap() or transcodes fail.
00688     // Leaving as a marker for refactor.
00689     //Push(QObject::tr("Set New Cut List"));
00690 }
00691 
00693 void DeleteMap::LoadCommBreakMap(uint64_t total, frm_dir_map_t &map)
00694 {
00695     Clear();
00696     frm_dir_map_t::Iterator it = map.begin();
00697     for ( ; it != map.end(); ++it)
00698         Add(it.key(), it.value() == MARK_COMM_START ?
00699                 MARK_CUT_START : MARK_CUT_END);
00700     CleanMap(total);
00701     Push(QObject::tr("Load Detected Commercials"));
00702 }
00703 
00705 void DeleteMap::LoadMap(uint64_t total, QString undoMessage)
00706 {
00707     if (!m_ctx || !m_ctx->playingInfo || gCoreContext->IsDatabaseIgnored())
00708         return;
00709 
00710     Clear();
00711     m_ctx->LockPlayingInfo(__FILE__, __LINE__);
00712     m_ctx->playingInfo->QueryCutList(m_deleteMap);
00713     m_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
00714     CleanMap(total);
00715     if (!undoMessage.isEmpty())
00716         Push(undoMessage);
00717 }
00718 
00721 bool DeleteMap::LoadAutoSaveMap(uint64_t total)
00722 {
00723     if (!m_ctx || !m_ctx->playingInfo || gCoreContext->IsDatabaseIgnored())
00724         return false;
00725 
00726     frm_dir_map_t tmpDeleteMap = m_deleteMap;
00727     Clear();
00728     m_ctx->LockPlayingInfo(__FILE__, __LINE__);
00729     bool result = m_ctx->playingInfo->QueryCutList(m_deleteMap, true);
00730     m_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
00731     CleanMap(total);
00732     if (result)
00733         Push(QObject::tr("Load Auto-saved Cuts"));
00734     else
00735         m_deleteMap = tmpDeleteMap;
00736 
00737     return result;
00738 }
00739 
00741 void DeleteMap::SaveMap(uint64_t total, bool isAutoSave)
00742 {
00743     if (!m_ctx || !m_ctx->playingInfo || gCoreContext->IsDatabaseIgnored())
00744         return;
00745 
00746     if (!isAutoSave)
00747     {
00748         // Remove temporary placeholder marks
00749         QMutableMapIterator<uint64_t, MarkTypes> it(m_deleteMap);
00750         while (it.hasNext())
00751         {
00752             it.next();
00753             if (MARK_PLACEHOLDER == it.value())
00754             {
00755                 it.remove();
00756                 m_changed = true;
00757             }
00758         }
00759 
00760         CleanMap(total);
00761     }
00762     m_ctx->LockPlayingInfo(__FILE__, __LINE__);
00763     m_ctx->playingInfo->SaveMarkupFlag(MARK_UPDATED_CUT);
00764     m_ctx->playingInfo->SaveCutList(m_deleteMap, isAutoSave);
00765     m_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
00766 }
00767 
00774 void DeleteMap::TrackerReset(uint64_t frame, uint64_t total)
00775 {
00776     m_nextCutStart = 0;
00777     m_nextCutStartIsValid = false;
00778     if (IsEmpty())
00779         return;
00780 
00781     frm_dir_map_t::iterator cutpoint = m_deleteMap.find(frame);
00782     if (cutpoint != m_deleteMap.end())
00783     {
00784         if (cutpoint.value() == MARK_CUT_START)
00785         {
00786             m_nextCutStartIsValid = true;
00787             m_nextCutStart = cutpoint.key();
00788         }
00789         else
00790         {
00791             ++cutpoint;
00792             m_nextCutStartIsValid = (cutpoint != m_deleteMap.end());
00793             m_nextCutStart = m_nextCutStartIsValid ? cutpoint.key() : total;
00794         }
00795     }
00796     else
00797         m_nextCutStart = GetNearestMark(frame, total, !IsInDelete(frame),
00798                                         &m_nextCutStartIsValid);
00799     LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("Tracker next CUT_START: %1")
00800                                    .arg(m_nextCutStart));
00801 }
00802 
00807 bool DeleteMap::TrackerWantsToJump(uint64_t frame, uint64_t total, uint64_t &to)
00808 {
00809     if (IsEmpty() || !m_nextCutStartIsValid || frame < m_nextCutStart)
00810         return false;
00811 
00812     to = GetNearestMark(m_nextCutStart, total, true);
00813     LOG(VB_PLAYBACK, LOG_INFO, LOC +
00814         QString("Tracker wants to jump to: %1").arg(to));
00815     return true;
00816 }
00817 
00822 uint64_t DeleteMap::GetLastFrame(uint64_t total) const
00823 {
00824     uint64_t result = total;
00825     if (IsEmpty())
00826         return result;
00827 
00828     frm_dir_map_t::const_iterator it = m_deleteMap.end();
00829     --it;
00830 
00831     if (it.value() == MARK_CUT_START)
00832         result = it.key();
00833     return result;
00834 }
00835 
00839 bool DeleteMap::IsSaved(void) const
00840 {
00841     if (!m_ctx || !m_ctx->playingInfo || gCoreContext->IsDatabaseIgnored())
00842         return true;
00843 
00844     frm_dir_map_t currentMap(m_deleteMap);
00845     frm_dir_map_t savedMap;
00846     m_ctx->LockPlayingInfo(__FILE__, __LINE__);
00847     m_ctx->playingInfo->QueryCutList(savedMap);
00848     m_ctx->UnlockPlayingInfo(__FILE__, __LINE__);
00849 
00850     // Remove temporary placeholder marks from currentMap
00851     QMutableMapIterator<uint64_t, MarkTypes> it(currentMap);
00852     while (it.hasNext())
00853     {
00854         it.next();
00855         if (MARK_PLACEHOLDER == it.value())
00856             it.remove();
00857     }
00858 
00859     return currentMap == savedMap;
00860 }
00861 
00862 uint64_t DeleteMap::TranslatePositionAbsToRel(const frm_dir_map_t &deleteMap,
00863                                               uint64_t absPosition)
00864 {
00865     uint64_t subtraction = 0;
00866     uint64_t startOfCutRegion = 0;
00867     frm_dir_map_t::const_iterator i;
00868     bool withinCut = false;
00869     bool first = true;
00870     for (i = deleteMap.constBegin(); i != deleteMap.constEnd(); ++i)
00871     {
00872         if (first)
00873             withinCut = (i.value() == MARK_CUT_END);
00874         first = false;
00875         if (i.key() > absPosition)
00876             break;
00877         if (i.value() == MARK_CUT_START && !withinCut)
00878         {
00879             withinCut = true;
00880             startOfCutRegion = i.key();
00881         }
00882         else if (i.value() == MARK_CUT_END && withinCut)
00883         {
00884             withinCut = false;
00885             subtraction += (i.key() - startOfCutRegion);
00886         }
00887     }
00888     if (withinCut)
00889         subtraction += (absPosition - startOfCutRegion);
00890     return absPosition - subtraction;
00891 }
00892 
00893 uint64_t DeleteMap::TranslatePositionRelToAbs(const frm_dir_map_t &deleteMap,
00894                                               uint64_t relPosition)
00895 {
00896     uint64_t addition = 0;
00897     uint64_t startOfCutRegion = 0;
00898     frm_dir_map_t::const_iterator i;
00899     bool withinCut = false;
00900     bool first = true;
00901     for (i = deleteMap.constBegin(); i != deleteMap.constEnd(); ++i)
00902     {
00903         if (first)
00904             withinCut = (i.value() == MARK_CUT_END);
00905         first = false;
00906         if (i.value() == MARK_CUT_START && !withinCut)
00907         {
00908             withinCut = true;
00909             startOfCutRegion = i.key();
00910             if (relPosition + addition <= startOfCutRegion)
00911                 break;
00912         }
00913         else if (i.value() == MARK_CUT_END && withinCut)
00914         {
00915             withinCut = false;
00916             addition += (i.key() - startOfCutRegion);
00917         }
00918     }
00919     return relPosition + addition;
00920 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends