441 lines
15 KiB
C++
441 lines
15 KiB
C++
/* ---------------------------------------------------------------------------------
|
|
Implementation file of Markers_manager class
|
|
Copyright (c) 2011-2013 AnS
|
|
|
|
(The MIT License)
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
------------------------------------------------------------------------------------
|
|
Markers_manager - Manager of Markers
|
|
[Single instance]
|
|
|
|
* stores one snapshot of Markers, representing current state of Markers in the project
|
|
* saves and loads the data from a project file. On error: clears the data
|
|
* regularly ensures that the size of current Markers array is not less than the number of frames in current Input
|
|
* implements all operations with Markers: setting Marker to a frame, removing Marker, inserting/deleting frames between Markers, truncating Markers array, changing Notes, finding frame for any given Marker, access to the data of Snapshot of Markers state
|
|
* implements full/partial copying of data between two Snapshots of Markers state, and searching for first difference between two Snapshots of Markers state
|
|
* also here's the code of searching for "similar" Notes
|
|
* also here's the code of editing Marker Notes
|
|
* also here's the code of Find Note dialog
|
|
* stores resources: save id, properties of searching for similar Notes
|
|
------------------------------------------------------------------------------------ */
|
|
|
|
#include "fceu.h"
|
|
#include "Qt/TasEditor/taseditor_project.h"
|
|
#include "Qt/TasEditor/TasEditorWindow.h"
|
|
|
|
// resources
|
|
static char markers_save_id[MARKERS_ID_LEN] = "MARKERS";
|
|
static char markers_skipsave_id[MARKERS_ID_LEN] = "MARKERX";
|
|
//static char keywordDelimiters[] = " !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
|
|
|
|
MARKERS_MANAGER::MARKERS_MANAGER()
|
|
{
|
|
memset(findNoteString, 0, MAX_NOTE_LEN);
|
|
}
|
|
|
|
void MARKERS_MANAGER::init()
|
|
{
|
|
reset();
|
|
}
|
|
void MARKERS_MANAGER::free()
|
|
{
|
|
markers.markersArray.resize(0);
|
|
markers.notes.resize(0);
|
|
}
|
|
void MARKERS_MANAGER::reset()
|
|
{
|
|
free();
|
|
markerNoteEditMode = MARKER_NOTE_EDIT_NONE;
|
|
currentIterationOfFindSimilar = 0;
|
|
markers.notes.resize(1);
|
|
markers.notes[0] = "Power on";
|
|
update();
|
|
}
|
|
void MARKERS_MANAGER::update()
|
|
{
|
|
// the size of current markers_array must be no less then the size of Input
|
|
if ((int)markers.markersArray.size() < currMovieData.getNumRecords())
|
|
markers.markersArray.resize(currMovieData.getNumRecords());
|
|
}
|
|
|
|
void MARKERS_MANAGER::save(EMUFILE *os, bool really_save)
|
|
{
|
|
if (really_save)
|
|
{
|
|
setTasProjectProgressBarText("Saving Markers...");
|
|
setTasProjectProgressBar( 0, 100 );
|
|
|
|
// write "MARKERS" string
|
|
os->fwrite(markers_save_id, MARKERS_ID_LEN);
|
|
markers.resetCompressedStatus(); // must recompress data, because most likely it has changed since last compression
|
|
markers.save(os);
|
|
setTasProjectProgressBar( 100, 100 );
|
|
}
|
|
else
|
|
{
|
|
// write "MARKERX" string, meaning that Markers are not saved
|
|
os->fwrite(markers_skipsave_id, MARKERS_ID_LEN);
|
|
}
|
|
}
|
|
// returns true if couldn't load
|
|
bool MARKERS_MANAGER::load(EMUFILE *is, unsigned int offset)
|
|
{
|
|
if (offset)
|
|
{
|
|
if (is->fseek(offset, SEEK_SET)) goto error;
|
|
} else
|
|
{
|
|
reset();
|
|
return false;
|
|
}
|
|
// read "MARKERS" string
|
|
char save_id[MARKERS_ID_LEN];
|
|
if ((int)is->fread(save_id, MARKERS_ID_LEN) < MARKERS_ID_LEN) goto error;
|
|
if (!strcmp(markers_skipsave_id, save_id))
|
|
{
|
|
// string says to skip loading Markers
|
|
FCEU_printf("No Markers in the file\n");
|
|
reset();
|
|
return false;
|
|
}
|
|
if (strcmp(markers_save_id, save_id)) goto error; // string is not valid
|
|
if (markers.load(is)) goto error;
|
|
// all ok
|
|
return false;
|
|
error:
|
|
FCEU_printf("Error loading Markers\n");
|
|
reset();
|
|
return true;
|
|
}
|
|
// -----------------------------------------------------------------------------------------
|
|
int MARKERS_MANAGER::getMarkersArraySize()
|
|
{
|
|
return markers.markersArray.size();
|
|
}
|
|
bool MARKERS_MANAGER::setMarkersArraySize(int newSize)
|
|
{
|
|
// if we are truncating, clear Markers that are gonna be erased (so that obsolete notes will be erased too)
|
|
bool markers_changed = false;
|
|
for (int i = markers.markersArray.size() - 1; i >= newSize; i--)
|
|
{
|
|
if (markers.markersArray[i])
|
|
{
|
|
removeMarkerFromFrame(i);
|
|
markers_changed = true;
|
|
}
|
|
}
|
|
markers.markersArray.resize(newSize);
|
|
return markers_changed;
|
|
}
|
|
|
|
int MARKERS_MANAGER::getMarkerAtFrame(int frame)
|
|
{
|
|
if (frame >= 0 && frame < (int)markers.markersArray.size())
|
|
return markers.markersArray[frame];
|
|
else
|
|
return 0;
|
|
}
|
|
// finds and returns # of Marker starting from start_frame and searching up
|
|
int MARKERS_MANAGER::getMarkerAboveFrame(int startFrame)
|
|
{
|
|
if (startFrame >= (int)markers.markersArray.size())
|
|
startFrame = markers.markersArray.size() - 1;
|
|
for (; startFrame >= 0; startFrame--)
|
|
if (markers.markersArray[startFrame]) return markers.markersArray[startFrame];
|
|
return 0;
|
|
}
|
|
// special version of the function
|
|
int MARKERS_MANAGER::getMarkerAboveFrame(MARKERS& targetMarkers, int startFrame)
|
|
{
|
|
if (startFrame >= (int)targetMarkers.markersArray.size())
|
|
startFrame = targetMarkers.markersArray.size() - 1;
|
|
for (; startFrame >= 0; startFrame--)
|
|
if (targetMarkers.markersArray[startFrame]) return targetMarkers.markersArray[startFrame];
|
|
return 0;
|
|
}
|
|
// finds frame where the Marker is set
|
|
int MARKERS_MANAGER::getMarkerFrameNumber(int marker_id)
|
|
{
|
|
for (int i = markers.markersArray.size() - 1; i >= 0; i--)
|
|
if (markers.markersArray[i] == marker_id) return i;
|
|
// didn't find
|
|
return -1;
|
|
}
|
|
// returns number of new Marker
|
|
int MARKERS_MANAGER::setMarkerAtFrame(int frame)
|
|
{
|
|
if (frame < 0)
|
|
return 0;
|
|
else if (frame >= (int)markers.markersArray.size())
|
|
markers.markersArray.resize(frame + 1);
|
|
else if (markers.markersArray[frame])
|
|
return markers.markersArray[frame];
|
|
|
|
int marker_num = getMarkerAboveFrame(frame) + 1;
|
|
markers.markersArray[frame] = marker_num;
|
|
if (taseditorConfig->emptyNewMarkerNotes)
|
|
markers.notes.insert(markers.notes.begin() + marker_num, 1, "");
|
|
else
|
|
// copy previous Marker note
|
|
markers.notes.insert(markers.notes.begin() + marker_num, 1, markers.notes[marker_num - 1]);
|
|
// increase following Markers' ids
|
|
int size = markers.markersArray.size();
|
|
for (frame++; frame < size; ++frame)
|
|
if (markers.markersArray[frame])
|
|
markers.markersArray[frame]++;
|
|
return marker_num;
|
|
}
|
|
void MARKERS_MANAGER::removeMarkerFromFrame(int frame)
|
|
{
|
|
if (markers.markersArray[frame])
|
|
{
|
|
// erase corresponding note
|
|
markers.notes.erase(markers.notes.begin() + markers.markersArray[frame]);
|
|
// clear Marker
|
|
markers.markersArray[frame] = 0;
|
|
// decrease following Markers' ids
|
|
int size = markers.markersArray.size();
|
|
for (frame++; frame < size; ++frame)
|
|
if (markers.markersArray[frame])
|
|
markers.markersArray[frame]--;
|
|
}
|
|
}
|
|
void MARKERS_MANAGER::toggleMarkerAtFrame(int frame)
|
|
{
|
|
if (frame >= 0 && frame < (int)markers.markersArray.size())
|
|
{
|
|
if (markers.markersArray[frame])
|
|
removeMarkerFromFrame(frame);
|
|
else
|
|
setMarkerAtFrame(frame);
|
|
}
|
|
}
|
|
|
|
bool MARKERS_MANAGER::eraseMarker(int frame, int numFrames)
|
|
{
|
|
bool markers_changed = false;
|
|
if (frame < (int)markers.markersArray.size())
|
|
{
|
|
if (numFrames == 1)
|
|
{
|
|
// erase 1 frame
|
|
// if there's a Marker, first clear it
|
|
if (markers.markersArray[frame])
|
|
{
|
|
removeMarkerFromFrame(frame);
|
|
markers_changed = true;
|
|
}
|
|
// erase 1 frame
|
|
markers.markersArray.erase(markers.markersArray.begin() + frame);
|
|
} else
|
|
{
|
|
// erase many frames
|
|
if (frame + numFrames > (int)markers.markersArray.size())
|
|
numFrames = (int)markers.markersArray.size() - frame;
|
|
// if there are Markers at those frames, first clear them
|
|
for (int i = frame; i < (frame + numFrames); ++i)
|
|
{
|
|
if (markers.markersArray[i])
|
|
{
|
|
removeMarkerFromFrame(i);
|
|
markers_changed = true;
|
|
}
|
|
}
|
|
// erase frames
|
|
markers.markersArray.erase(markers.markersArray.begin() + frame, markers.markersArray.begin() + (frame + numFrames));
|
|
}
|
|
// check if there were some Markers after this frame
|
|
// since these Markers were shifted, markers_changed should be set to true
|
|
if (!markers_changed)
|
|
{
|
|
for (int i = markers.markersArray.size() - 1; i >= frame; i--)
|
|
{
|
|
if (markers.markersArray[i])
|
|
{
|
|
markers_changed = true; // Markers moved
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return markers_changed;
|
|
}
|
|
bool MARKERS_MANAGER::insertEmpty(int at, int numFrames)
|
|
{
|
|
if (at == -1)
|
|
{
|
|
// append blank frames
|
|
markers.markersArray.resize(markers.markersArray.size() + numFrames);
|
|
return false;
|
|
} else
|
|
{
|
|
bool markers_changed = false;
|
|
// first check if there are Markers after the frame
|
|
for (int i = markers.markersArray.size() - 1; i >= at; i--)
|
|
{
|
|
if (markers.markersArray[i])
|
|
{
|
|
markers_changed = true; // Markers moved
|
|
break;
|
|
}
|
|
}
|
|
markers.markersArray.insert(markers.markersArray.begin() + at, numFrames, 0);
|
|
return markers_changed;
|
|
}
|
|
}
|
|
|
|
int MARKERS_MANAGER::getNotesSize()
|
|
{
|
|
return markers.notes.size();
|
|
}
|
|
std::string MARKERS_MANAGER::getNoteCopy(int index)
|
|
{
|
|
if (index >= 0 && index < (int)markers.notes.size())
|
|
return markers.notes[index];
|
|
else
|
|
return markers.notes[0];
|
|
}
|
|
// special version of the function
|
|
std::string MARKERS_MANAGER::getNoteCopy(MARKERS& targetMarkers, int index)
|
|
{
|
|
if (index >= 0 && index < (int)targetMarkers.notes.size())
|
|
return targetMarkers.notes[index];
|
|
else
|
|
return targetMarkers.notes[0];
|
|
}
|
|
void MARKERS_MANAGER::setNote(int index, const char* newText)
|
|
{
|
|
if (index >= 0 && index < (int)markers.notes.size())
|
|
markers.notes[index] = newText;
|
|
}
|
|
// ---------------------------------------------------------------------------------------
|
|
void MARKERS_MANAGER::makeCopyOfCurrentMarkersTo(MARKERS& destination)
|
|
{
|
|
destination.markersArray = markers.markersArray;
|
|
destination.notes = markers.notes;
|
|
destination.resetCompressedStatus();
|
|
}
|
|
void MARKERS_MANAGER::restoreMarkersFromCopy(MARKERS& source)
|
|
{
|
|
markers.markersArray = source.markersArray;
|
|
markers.notes = source.notes;
|
|
}
|
|
|
|
// return true only when difference is found before end frame (not including end frame)
|
|
bool MARKERS_MANAGER::checkMarkersDiff(MARKERS& theirMarkers)
|
|
{
|
|
int end_my = getMarkersArraySize() - 1;
|
|
int end_their = theirMarkers.markersArray.size() - 1;
|
|
int min_end = end_my;
|
|
int i;
|
|
// 1 - check if there are any Markers after min_end
|
|
if (end_my < end_their)
|
|
{
|
|
for (i = end_their; i > min_end; i--)
|
|
if (theirMarkers.markersArray[i])
|
|
return true;
|
|
} else if (end_my > end_their)
|
|
{
|
|
min_end = end_their;
|
|
for (i = end_my; i > min_end; i--)
|
|
if (markers.markersArray[i])
|
|
return true;
|
|
}
|
|
// 2 - check if there's any difference before min_end
|
|
for (i = min_end; i >= 0; i--)
|
|
{
|
|
if (markers.markersArray[i] != theirMarkers.markersArray[i])
|
|
return true;
|
|
else if (markers.markersArray[i] && // not empty
|
|
markers.notes[markers.markersArray[i]].compare(theirMarkers.notes[theirMarkers.markersArray[i]])) // notes differ
|
|
return true;
|
|
}
|
|
// 3 - check if there's difference between 0th Notes
|
|
if (markers.notes[0].compare(theirMarkers.notes[0]))
|
|
return true;
|
|
// no difference found
|
|
return false;
|
|
}
|
|
// ------------------------------------------------------------------------------------
|
|
// custom ordering function, used by std::sort
|
|
bool ordering(const std::pair<int, double>& d1, const std::pair<int, double>& d2)
|
|
{
|
|
return d1.second < d2.second;
|
|
}
|
|
|
|
void MARKERS_MANAGER::findSimilarNote()
|
|
{
|
|
currentIterationOfFindSimilar = 0;
|
|
findNextSimilarNote();
|
|
}
|
|
void MARKERS_MANAGER::findNextSimilarNote()
|
|
{
|
|
// This is implemented in TasEditorWindow.cpp
|
|
}
|
|
// ------------------------------------------------------------------------------------
|
|
void MARKERS_MANAGER::updateEditedMarkerNote()
|
|
{
|
|
if (!markerNoteEditMode)
|
|
{
|
|
return;
|
|
}
|
|
int len=0;
|
|
char new_text[MAX_NOTE_LEN];
|
|
if (markerNoteEditMode == MARKER_NOTE_EDIT_UPPER)
|
|
{
|
|
len = tasWin->upperMarkerNote->text().size();
|
|
if ( len >= MAX_NOTE_LEN )
|
|
{
|
|
len = MAX_NOTE_LEN-1;
|
|
}
|
|
strncpy( new_text, tasWin->upperMarkerNote->text().toLocal8Bit().constData(), MAX_NOTE_LEN-1 );
|
|
new_text[len] = 0;
|
|
// check changes
|
|
if (strcmp(getNoteCopy(playback->displayedMarkerNumber).c_str(), new_text))
|
|
{
|
|
setNote(playback->displayedMarkerNumber, new_text);
|
|
if (playback->displayedMarkerNumber)
|
|
{
|
|
history->registerMarkersChange(MODTYPE_MARKER_RENAME, getMarkerFrameNumber(playback->displayedMarkerNumber), -1, new_text);
|
|
}
|
|
else
|
|
{
|
|
// zeroth Marker - just assume it's set on frame 0
|
|
history->registerMarkersChange(MODTYPE_MARKER_RENAME, 0, -1, new_text);
|
|
}
|
|
// notify Selection to change text in the lower Marker (in case both are showing same Marker)
|
|
selection->mustFindCurrentMarker = true;
|
|
}
|
|
}
|
|
else if (markerNoteEditMode == MARKER_NOTE_EDIT_LOWER)
|
|
{
|
|
len = tasWin->lowerMarkerNote->text().size();
|
|
if ( len >= MAX_NOTE_LEN )
|
|
{
|
|
len = MAX_NOTE_LEN-1;
|
|
}
|
|
strncpy( new_text, tasWin->lowerMarkerNote->text().toLocal8Bit().constData(), MAX_NOTE_LEN-1 );
|
|
new_text[len] = 0;
|
|
|
|
// check changes
|
|
if (strcmp(getNoteCopy(selection->displayedMarkerNumber).c_str(), new_text))
|
|
{
|
|
setNote(selection->displayedMarkerNumber, new_text);
|
|
if (selection->displayedMarkerNumber)
|
|
{
|
|
history->registerMarkersChange(MODTYPE_MARKER_RENAME, getMarkerFrameNumber(selection->displayedMarkerNumber), -1, new_text);
|
|
}
|
|
else
|
|
{
|
|
// zeroth Marker - just assume it's set on frame 0
|
|
history->registerMarkersChange(MODTYPE_MARKER_RENAME, 0, -1, new_text);
|
|
}
|
|
// notify Playback to change text in upper Marker (in case both are showing same Marker)
|
|
playback->mustFindCurrentMarker = true;
|
|
}
|
|
}
|
|
}
|
|
// ------------------------------------------------------------------------------------
|