* Tasedit: View->Show Branch Screenshots

* Tasedit: selection history, undo/redo (Ctrl-Q/Ctrl-W)
* Tasedit: Reselect Clipboard (Ctrl-B)
* Tasedit: saving/loading selection history to .tas
* Tasedit: input hotchanges (16 gradations), storing/handling data inside input_snapshot class
* Tasedit: View->Enable Hot Changes
This commit is contained in:
ansstuff 2011-11-16 22:52:37 +00:00
parent 170bcefdd2
commit 514c80e416
19 changed files with 1071 additions and 385 deletions

View File

@ -71,13 +71,14 @@ extern int frameSkipAmt;
extern bool TASEdit_follow_playback;
extern bool TASEdit_show_lag_frames;
extern bool TASEdit_show_markers;
extern bool TASEdit_show_branch_screenshots;
extern bool TASEdit_bind_markers;
extern bool TASEdit_branch_full_movie;
extern bool TASEdit_branch_only_when_rec;
extern bool TASEdit_view_branches_tree;
extern bool TASEdit_branch_scr_hud;
extern bool TASEdit_restore_position;
extern bool TASEdit_show_dot;
extern bool TASEdit_enable_hot_changes;
extern int TASEdit_greenzone_capacity;
extern int TasEdit_undo_levels;
extern int TASEdit_autosave_period;
@ -302,13 +303,14 @@ static CFGSTRUCT fceuconfig[] = {
AC(TASEdit_follow_playback),
AC(TASEdit_show_lag_frames),
AC(TASEdit_show_markers),
AC(TASEdit_show_branch_screenshots),
AC(TASEdit_bind_markers),
AC(TASEdit_branch_full_movie),
AC(TASEdit_branch_only_when_rec),
AC(TASEdit_view_branches_tree),
AC(TASEdit_branch_scr_hud),
AC(TASEdit_restore_position),
AC(TASEdit_show_dot),
AC(TASEdit_enable_hot_changes),
AC(TASEdit_greenzone_capacity),
AC(TasEdit_undo_levels),
AC(TASEdit_autosave_period),

View File

@ -221,9 +221,10 @@ TASEDITMENU MENU
BEGIN
POPUP "&File"
BEGIN
MENUITEM "&Open Project...", ID_FILE_OPENPROJECT
MENUITEM "&Save Project\tCtrl+S", ID_FILE_SAVEPROJECT
MENUITEM "S&ave Project As...", ID_FILE_SAVEPROJECTAS
MENUITEM "&Open", ID_FILE_OPENPROJECT
MENUITEM "&Save\tCtrl+S", ID_FILE_SAVEPROJECT
MENUITEM "S&ave As", ID_FILE_SAVEPROJECTAS
MENUITEM "Save &Compact", ID_FILE_SAVECOMPACT, INACTIVE
MENUITEM "&Recent", ID_TASEDIT_FILE_RECENT
MENUITEM SEPARATOR
MENUITEM "&Import FM2", ID_FILE_IMPORTFM2, INACTIVE
@ -233,29 +234,33 @@ BEGIN
END
POPUP "&Edit"
BEGIN
MENUITEM "&Undo\tCtrl+Z", ID_EDIT_UNDO
MENUITEM "&Redo\tCtrl+Y", ID_EDIT_REDO
MENUITEM "Selection Undo\tCtrl+Q", ID_EDIT_SELECTIONUNDO
MENUITEM "Selection Redo\tCtrl+W", ID_EDIT_SELECTIONREDO
MENUITEM SEPARATOR
MENUITEM "Select &All", ID_EDIT_SELECTALL
MENUITEM "Select mid &Markers\tCtrl+A", ID_EDIT_SELECTMIDMARKERS
MENUITEM "Reselect Clipboard\tCtrl+B", ID_EDIT_RESELECTCLIPBOARD
MENUITEM SEPARATOR
MENUITEM "Cu&t\tCtrl+X", ID_TASEDIT_CUT
MENUITEM "&Copy\tCtrl+C", ID_TASEDIT_COPY
MENUITEM "&Paste\tCtrl+V", ID_TASEDIT_PASTE
MENUITEM "Cu&t\tCtrl+X", ID_TASEDIT_CUT
MENUITEM SEPARATOR
MENUITEM "C&lear\tDelete", ID_EDIT_CLEAR
MENUITEM "C&lear\tDel", ID_EDIT_CLEAR
MENUITEM "&Delete\tCtrl+Del", ID_TASEDIT_DELETE
MENUITEM "&Insert\tCtrl+Ins", ID_EDIT_INSERTFRAMES
MENUITEM "Insert # of Frames\tInsert", ID_EDIT_INSERT
MENUITEM "Insert # of Frames\tIns", ID_EDIT_INSERT
MENUITEM "Cl&one\tShift+Ins", ID_EDIT_CLONEFRAMES
MENUITEM SEPARATOR
MENUITEM "Truncate movie\tCtrl+T", ID_EDIT_TRUNCATE
MENUITEM SEPARATOR
MENUITEM "&Undo\tCtrl-Z", ID_EDIT_UNDO
MENUITEM "&Redo\tCtrl-Y", ID_EDIT_REDO
MENUITEM "Truncate\tCtrl+T", ID_EDIT_TRUNCATE
END
POPUP "&View"
BEGIN
MENUITEM "Highlight &Lag Frames", ID_VIEW_SHOW_LAG_FRAMES
MENUITEM "Show &Markers", ID_VIEW_SHOW_MARKERS
MENUITEM "Show &dot in empty cells", ID_VIEW_SHOWDOTINEMPTYCELLS
MENUITEM "Show Branch &Screenshots", ID_VIEW_SHOWBRANCHSCREENSHOTS
MENUITEM "Enable Hot &Changes", ID_VIEW_ENABLEHOTCHANGES
MENUITEM SEPARATOR
MENUITEM "&Follow undo context", ID_VIEW_JUMPWHENMAKINGUNDO
END
@ -275,7 +280,8 @@ BEGIN
END
POPUP "&Help"
BEGIN
MENUITEM "&TASEdit Help...", ID_HELP_TASEDITHELP
MENUITEM "&TASEdit Help", ID_HELP_TASEDITHELP
MENUITEM "&About", ID_HELP_ABOUT
END
END
@ -1997,15 +2003,18 @@ BEGIN
"F", ACCEL_CTRL_F, VIRTKEY, CONTROL, NOINVERT
VK_INSERT, ACCEL_CTRL_INSERT, VIRTKEY, CONTROL, NOINVERT
"P", ACCEL_CTRL_P, VIRTKEY, CONTROL, NOINVERT
"Q", ACCEL_CTRL_Q, VIRTKEY, CONTROL, NOINVERT
"S", ACCEL_CTRL_S, VIRTKEY, CONTROL, NOINVERT
"T", ACCEL_CTRL_T, VIRTKEY, CONTROL, NOINVERT
"V", ACCEL_CTRL_V, VIRTKEY, CONTROL, NOINVERT
"W", ACCEL_CTRL_W, VIRTKEY, CONTROL, NOINVERT
"X", ACCEL_CTRL_X, VIRTKEY, CONTROL, NOINVERT
"Y", ACCEL_CTRL_Y, VIRTKEY, CONTROL, NOINVERT
"Z", ACCEL_CTRL_Z, VIRTKEY, CONTROL, NOINVERT
VK_DELETE, ACCEL_DEL, VIRTKEY, NOINVERT
VK_INSERT, ACCEL_INS, VIRTKEY, NOINVERT
VK_INSERT, ACCEL_SHIFT_INS, VIRTKEY, SHIFT, NOINVERT
"B", ACCEL_CTRL_B, VIRTKEY, CONTROL, NOINVERT
END
IDR_RWACCELERATOR ACCELERATORS

View File

@ -893,6 +893,14 @@
#define ID_CONFIG_BRANCHESWORKONLYWHENRECORDING 40474
#define ID_CONFIG_HUDINBRANCHSCREENSHOTS 40475
#define ID_CONFIG_SETAUTOSAVEPERIOD 40476
#define ACCEL_CTRL_Q 40478
#define ID_EDIT_SELECTIONUNDO 40481
#define ID_EDIT_SELECTIONREDO 40482
#define ID_EDIT_RESELECTCLIPBOARD 40483
#define ID_FILE_SAVECOMPACT 40484
#define ID_HELP_ABOUT 40485
#define ID_VIEW_ENABLEHOTCHANGES 40488
#define ID_VIEW_SHOWBRANCHSCREENSHOTS 40489
#define IDC_DEBUGGER_ICONTRAY 55535
#define MW_ValueLabel2 65423
#define MW_ValueLabel1 65426
@ -902,7 +910,7 @@
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 185
#define _APS_NEXT_COMMAND_VALUE 40477
#define _APS_NEXT_COMMAND_VALUE 40490
#define _APS_NEXT_CONTROL_VALUE 1267
#define _APS_NEXT_SYMED_VALUE 101
#endif

View File

@ -2,7 +2,6 @@
#include <fstream>
#include <sstream>
#include "common.h"
#include "taseditlib/taseditproj.h"
#include "fceu.h"
#include "debugger.h"
@ -18,24 +17,26 @@
using namespace std;
//to change header font
//http://forums.devx.com/archive/index.php/t-37234.html
int old_multitrack_recording_joypad, multitrack_recording_joypad;
bool old_movie_readonly;
bool TASEdit_focus = false;
bool Tasedit_rewind_now = false;
int listItems; // number of items per list page
// saved FCEU config
int saved_eoptions;
int saved_EnableAutosave;
extern int EnableAutosave;
extern HWND hwndScrBmp;
extern EMOVIEMODE movieMode; // maybe we need normal setter for movieMode, to encapsulate it
// vars saved in cfg file
int TasEdit_wndx, TasEdit_wndy;
bool TASEdit_follow_playback = true;
bool TASEdit_show_lag_frames = true;
bool TASEdit_show_markers = true;
bool TASEdit_show_branch_screenshots = true;
bool TASEdit_bind_markers = true;
bool TASEdit_branch_full_movie = true;
bool TASEdit_branch_only_when_rec = false;
@ -46,9 +47,10 @@ int TASEdit_greenzone_capacity = GREENZONE_CAPACITY_DEFAULT;
int TasEdit_undo_levels = UNDO_LEVELS_DEFAULT;
int TASEdit_autosave_period = AUTOSAVE_PERIOD_DEFAULT;
extern bool muteTurbo;
bool TASEdit_show_dot = true;
bool TASEdit_enable_hot_changes = true;
bool TASEdit_jump_to_undo = true;
// resources
string tasedithelp = "{16CDE0C4-02B0-4A60-A88D-076319909A4D}"; //Name of TASEdit Help page
char buttonNames[NUM_JOYPAD_BUTTONS][2] = {"A", "B", "S", "T", "U", "D", "L", "R"};
char windowCaptions[6][30] = { "TAS Editor",
@ -57,8 +59,7 @@ char windowCaptions[6][30] = { "TAS Editor",
"TAS Editor (Recording 2P)",
"TAS Editor (Recording 3P)",
"TAS Editor (Recording 4P)"};
// hot changes color table
COLORREF hot_changes_colors[16] = { 0x0, 0x41f2c, 0x62a3b, 0x7344a, 0x93f59, 0xb4968, 0xc5477, 0xe5e86, 0xf6995, 0x1174a4, 0x127eb3, 0x1489c2, 0x1693d1, 0x179ee0, 0x19a8ef, 0x1bb4ff };
COLORREF hot_changes_colors[16] = { 0x0, 0x661212, 0x842B4E, 0x652C73, 0x48247D, 0x383596, 0x2947AE, 0x1E53C1, 0x135DD2, 0x116EDA, 0x107EE3, 0x0F8EEB, 0x209FF4, 0x3DB1FD, 0x51C2FF, 0x4DCDFF };
HWND hwndTasEdit = 0;
HMENU hmenu, hrmenu;
@ -73,14 +74,7 @@ HWND hwndRB_RecOff, hwndRB_RecAll, hwndRB_Rec1P, hwndRB_Rec2P, hwndRB_Rec3P, hwn
HWND hwndBranchesBitmap;
WNDPROC hwndBranchesBitmap_oldWndProc;
HFONT hMainListFont;
typedef std::set<int> TSelectionFrames;
static TSelectionFrames selectionFrames;
//hacky.. we need to think about how to convey information from the driver to the movie code.
//add a new fceud_ function?? blehhh maybe
extern EMOVIEMODE movieMode;
HFONT hMainListFont, hMainListSelectFont;
// all Taseditor functional modules
TASEDIT_PROJECT project;
@ -89,6 +83,7 @@ PLAYBACK playback;
GREENZONE greenzone;
MARKERS markers;
BOOKMARKS bookmarks;
TASEDIT_SELECTION selection;
void GetDispInfo(NMLVDISPINFO* nmlvDispInfo)
{
@ -113,7 +108,7 @@ void GetDispInfo(NMLVDISPINFO* nmlvDispInfo)
case COLUMN_FRAMENUM:
case COLUMN_FRAMENUM2:
{
U32ToDecStr(item.pszText,item.iItem,DIGITS_IN_FRAMENUM);
U32ToDecStr(item.pszText, item.iItem, DIGITS_IN_FRAMENUM);
break;
}
case COLUMN_JOYPAD1_A: case COLUMN_JOYPAD1_B: case COLUMN_JOYPAD1_S: case COLUMN_JOYPAD1_T:
@ -130,13 +125,13 @@ void GetDispInfo(NMLVDISPINFO* nmlvDispInfo)
uint8 data = currMovieData.records[item.iItem].joysticks[joy];
if(data & (1<<bit))
{
item.pszText[0] = MovieRecord::mnemonics[bit];
item.pszText[1] = 0;
item.pszText[0] = buttonNames[bit][0];
item.pszText[2] = 0;
} else
{
if (TASEdit_show_dot)
if (TASEdit_enable_hot_changes && history.GetCurrentSnapshot().GetHotChangeInfo(item.iItem, item.iSubItem - COLUMN_JOYPAD1_A))
{
item.pszText[0] = 46; // "."
item.pszText[0] = 45; // "-"
item.pszText[1] = 0;
} else item.pszText[0] = 0;
}
@ -159,11 +154,17 @@ LONG CustomDraw(NMLVCUSTOMDRAW* msg)
cell_x = msg->iSubItem;
cell_y = msg->nmcd.dwItemSpec;
//msg->clrText = 0xFF0000;
if(cell_x > COLUMN_ICONS)
{
SelectObject(msg->nmcd.hdc, hMainListFont);
// text color
if(TASEdit_enable_hot_changes && cell_x >= COLUMN_JOYPAD1_A && cell_x <= COLUMN_JOYPAD4_R)
{
msg->clrText = hot_changes_colors[history.GetCurrentSnapshot().GetHotChangeInfo(cell_y, cell_x - COLUMN_JOYPAD1_A)];
} else msg->clrText = NORMAL_TEXT_COLOR;
// bg color
if(cell_x == COLUMN_FRAMENUM || cell_x == COLUMN_FRAMENUM2)
{
// frame number
@ -277,13 +278,15 @@ void UpdateTasEdit()
{
if(!hwndTasEdit) return;
UpdateList(); // also markers are updated there
UpdateList();
markers.update();
greenzone.update();
playback.update();
bookmarks.update();
selection.update();
history.update();
project.update();
selection.update();
// update window caption
if (old_movie_readonly != movie_readonly || old_multitrack_recording_joypad != multitrack_recording_joypad)
@ -304,28 +307,11 @@ void UpdateTasEdit()
void UpdateList()
{
// first update number of items in markers array
markers.update();
//update the number of items in the list
int currLVItemCount = ListView_GetItemCount(hwndList);
int movie_size = currMovieData.getNumRecords();
if(currLVItemCount != movie_size)
{
ListView_SetItemCountEx(hwndList,movie_size,LVSICF_NOSCROLL | LVSICF_NOINVALIDATEALL);
// also reduce selection if needed
if (selectionFrames.size())
{
int delete_index;
while(1)
{
delete_index = *selectionFrames.rbegin();
if (delete_index < movie_size) break;
// reduce selection manually, because reduced list won't call ItemChanged for these rows
selectionFrames.erase(delete_index);
if (!selectionFrames.size()) break;
}
}
}
}
void RedrawWindowCaption()
@ -370,12 +356,6 @@ void RedrawRowAndBookmark(int index)
bookmarks.RedrawChangedBookmarks(index);
}
enum ECONTEXTMENU
{
CONTEXTMENU_STRAY = 0,
CONTEXTMENU_SELECTED = 1,
};
void ShowMenu(ECONTEXTMENU which, POINT& pt)
{
HMENU sub = GetSubMenu(hrmenu,(int)which);
@ -408,14 +388,8 @@ void RightClick(LPNMITEMACTIVATE info)
return;
}
//make sure that the click is in our currently selected set.
//if it is not, then we don't know what to do yet
if(selectionFrames.find(index) == selectionFrames.end())
{
return;
}
RightClickMenu(info);
if (selection.CheckFrameSelected(index))
RightClickMenu(info);
}
void InputChangedRec()
@ -430,11 +404,11 @@ void ToggleJoypadBit(int column_index, int row_index, UINT KeyFlags)
if (KeyFlags & (LVKF_SHIFT|LVKF_CONTROL))
{
//update multiple rows
for(TSelectionFrames::iterator it(selectionFrames.begin()); it != selectionFrames.end(); it++)
for(SelectionFrames::iterator it(selection.CurrentSelection().begin()); it != selection.CurrentSelection().end(); it++)
{
currMovieData.records[*it].toggleBit(joy,bit);
}
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_CHANGE, *selectionFrames.begin(), *selectionFrames.rbegin()));
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_CHANGE, *selection.CurrentSelection().begin(), *selection.CurrentSelection().rbegin()));
} else
{
//update one row
@ -456,7 +430,7 @@ void SingleClick(LPNMITEMACTIVATE info)
if(column_index == COLUMN_ICONS)
{
// click on the "icons" column - jump to the frame
ClearSelection();
selection.ClearSelection();
playback.jump(row_index);
} else if(column_index == COLUMN_FRAMENUM || column_index == COLUMN_FRAMENUM2)
{
@ -491,7 +465,7 @@ void DoubleClick(LPNMITEMACTIVATE info)
if(column_index == COLUMN_ICONS || column_index == COLUMN_FRAMENUM || column_index == COLUMN_FRAMENUM2)
{
// double click sends playback to the frame
ClearSelection();
selection.ClearSelection();
playback.jump(row_index);
} else if(column_index >= COLUMN_JOYPAD1_A && column_index <= COLUMN_JOYPAD4_R)
{
@ -501,18 +475,19 @@ void DoubleClick(LPNMITEMACTIVATE info)
void CloneFrames()
{
int frames = selectionFrames.size();
int frames = selection.CurrentSelection().size();
if (!frames) return;
currMovieData.records.reserve(currMovieData.getNumRecords() + frames);
//insert frames before each selection, but consecutive selection lines are accounted as single region
frames = 1;
TSelectionFrames::reverse_iterator next_it;
for(TSelectionFrames::reverse_iterator it(selectionFrames.rbegin()); it != selectionFrames.rend(); it++)
SelectionFrames::reverse_iterator next_it;
for(SelectionFrames::reverse_iterator it(selection.CurrentSelection().rbegin()); it != selection.CurrentSelection().rend(); it++)
{
next_it = it;
next_it++;
if (next_it == selectionFrames.rend() || (int)*next_it < ((int)*it - 1))
if (next_it == selection.CurrentSelection().rend() || (int)*next_it < ((int)*it - 1))
{
// end of current region
currMovieData.cloneRegion(*it, frames);
@ -522,24 +497,25 @@ void CloneFrames()
} else frames++;
}
UpdateList();
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_CLONE, *selectionFrames.begin()));
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_CLONE, *selection.CurrentSelection().begin()));
}
void InsertFrames()
{
int frames = selectionFrames.size();
int frames = selection.CurrentSelection().size();
if (!frames) return;
//to keep this from being even slower than it would otherwise be, go ahead and reserve records
currMovieData.records.reserve(currMovieData.getNumRecords() + frames);
//insert frames before each selection, but consecutive selection lines are accounted as single region
frames = 1;
TSelectionFrames::reverse_iterator next_it;
for(TSelectionFrames::reverse_iterator it(selectionFrames.rbegin()); it != selectionFrames.rend(); it++)
SelectionFrames::reverse_iterator next_it;
for(SelectionFrames::reverse_iterator it(selection.CurrentSelection().rbegin()); it != selection.CurrentSelection().rend(); it++)
{
next_it = it;
next_it++;
if (next_it == selectionFrames.rend() || (int)*next_it < ((int)*it - 1))
if (next_it == selection.CurrentSelection().rend() || (int)*next_it < ((int)*it - 1))
{
// end of current region
currMovieData.insertEmpty(*it,frames);
@ -549,15 +525,45 @@ void InsertFrames()
} else frames++;
}
UpdateList();
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_INSERT, *selectionFrames.begin()));
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_INSERT, *selection.CurrentSelection().begin()));
}
void InsertNumFrames()
{
int frames = selection.CurrentSelection().size();
if(CWin32InputBox::GetInteger("Insert number of Frames", "How many frames?", frames, hwndTasEdit) == IDOK)
{
if (frames > 0)
{
int index;
if (selection.CurrentSelection().size())
{
// insert at selection
index = *selection.CurrentSelection().begin();
selection.ClearSelection();
} else
{
// insert at playback cursor
index = currFrameCounter;
}
currMovieData.insertEmpty(index, frames);
if (TASEdit_bind_markers)
markers.insertEmpty(index, frames);
UpdateList();
// select inserted rows
selection.SetRegionSelection(index, index + frames - 1);
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_INSERT, index));
}
}
}
void DeleteFrames()
{
int start_index = *selectionFrames.begin();
int end_index = *selectionFrames.rbegin();
if (selection.CurrentSelection().size() == 0) return;
int start_index = *selection.CurrentSelection().begin();
int end_index = *selection.CurrentSelection().rbegin();
//delete frames on each selection, going backwards
for(TSelectionFrames::reverse_iterator it(selectionFrames.rbegin()); it != selectionFrames.rend(); it++)
for(SelectionFrames::reverse_iterator it(selection.CurrentSelection().rbegin()); it != selection.CurrentSelection().rend(); it++)
{
currMovieData.records.erase(currMovieData.records.begin() + *it);
if (TASEdit_bind_markers)
@ -581,24 +587,25 @@ void DeleteFrames()
void ClearFrames(bool cut)
{
if (selection.CurrentSelection().size() == 0) return;
//clear input on each selection
for(TSelectionFrames::iterator it(selectionFrames.begin()); it != selectionFrames.end(); it++)
for(SelectionFrames::iterator it(selection.CurrentSelection().begin()); it != selection.CurrentSelection().end(); it++)
{
currMovieData.records[*it].clear();
}
if (cut)
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_CUT, *selectionFrames.begin(), *selectionFrames.rbegin()));
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_CUT, *selection.CurrentSelection().begin(), *selection.CurrentSelection().rbegin()));
else
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_CLEAR, *selectionFrames.begin(), *selectionFrames.rbegin()));
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_CLEAR, *selection.CurrentSelection().begin(), *selection.CurrentSelection().rbegin()));
}
void Truncate()
{
int frame = currFrameCounter;
if (selectionFrames.size())
if (selection.CurrentSelection().size())
{
frame=*selectionFrames.begin();
ClearSelection();
frame = *selection.CurrentSelection().begin();
selection.ClearSelection();
}
if (currMovieData.getNumRecords() > frame+1)
{
@ -625,7 +632,7 @@ void ColumnSet(int column)
// Markers column
//inspect the selected frames, if they are all set, then unset all, else set all
bool unset_found = false;
for(TSelectionFrames::iterator it(selectionFrames.begin()); it != selectionFrames.end(); it++)
for(SelectionFrames::iterator it(selection.CurrentSelection().begin()); it != selection.CurrentSelection().end(); it++)
{
if(!(markers.markers_array[*it] & MARKER_FLAG_BIT))
{
@ -636,18 +643,18 @@ void ColumnSet(int column)
if (unset_found)
{
// set all
for(TSelectionFrames::iterator it(selectionFrames.begin()); it != selectionFrames.end(); it++)
for(SelectionFrames::iterator it(selection.CurrentSelection().begin()); it != selection.CurrentSelection().end(); it++)
markers.markers_array[*it] |= MARKER_FLAG_BIT;
history.RegisterChanges(MODTYPE_MARKER_SET, *selectionFrames.begin(), *selectionFrames.rbegin());
history.RegisterChanges(MODTYPE_MARKER_SET, *selection.CurrentSelection().begin(), *selection.CurrentSelection().rbegin());
} else
{
// unset all
for(TSelectionFrames::iterator it(selectionFrames.begin()); it != selectionFrames.end(); it++)
for(SelectionFrames::iterator it(selection.CurrentSelection().begin()); it != selection.CurrentSelection().end(); it++)
markers.markers_array[*it] &= ~MARKER_FLAG_BIT;
history.RegisterChanges(MODTYPE_MARKER_UNSET, *selectionFrames.begin(), *selectionFrames.rbegin());
history.RegisterChanges(MODTYPE_MARKER_UNSET, *selection.CurrentSelection().begin(), *selection.CurrentSelection().rbegin());
}
project.SetProjectChanged();
ClearSelection();
selection.ClearSelection();
// no need to RedrawList();
} else
{
@ -657,7 +664,7 @@ void ColumnSet(int column)
int button = (column - COLUMN_JOYPAD1_A) % NUM_JOYPAD_BUTTONS;
//inspect the selected frames, if they are all set, then unset all, else set all
bool newValue = false;
for(TSelectionFrames::iterator it(selectionFrames.begin()); it != selectionFrames.end(); it++)
for(SelectionFrames::iterator it(selection.CurrentSelection().begin()); it != selection.CurrentSelection().end(); it++)
{
if(!(currMovieData.records[*it].checkBit(joy,button)))
{
@ -666,114 +673,30 @@ void ColumnSet(int column)
}
}
// apply newValue
for(TSelectionFrames::iterator it(selectionFrames.begin()); it != selectionFrames.end(); it++)
for(SelectionFrames::iterator it(selection.CurrentSelection().begin()); it != selection.CurrentSelection().end(); it++)
currMovieData.records[*it].setBitValue(joy,button,newValue);
if (newValue)
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_SET, *selectionFrames.begin(), *selectionFrames.rbegin()));
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_SET, *selection.CurrentSelection().begin(), *selection.CurrentSelection().rbegin()));
else
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_UNSET, *selectionFrames.begin(), *selectionFrames.rbegin()));
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_UNSET, *selection.CurrentSelection().begin(), *selection.CurrentSelection().rbegin()));
}
}
void ClearSelection()
{
ListView_SetItemState(hwndList, -1, 0, LVIS_FOCUSED|LVIS_SELECTED);
}
void ClearRowSelection(int index)
{
ListView_SetItemState(hwndList,index,0, LVIS_SELECTED);
}
void SelectAll()
{
ListView_SetItemState(hwndList,-1,LVIS_SELECTED, LVIS_SELECTED);
RedrawList();
}
void SelectMidMarkers()
{
int center, upper_border, lower_border;
int upper_marker, lower_marker;
int movie_size = currMovieData.getNumRecords();
// if selection size=0 then playback cursor is selected and serves as center
if (selectionFrames.size())
{
upper_border = center = *selectionFrames.begin();
lower_border = *selectionFrames.rbegin();
} else lower_border = upper_border = center = currFrameCounter;
// find markers
// searching up starting from center-0
for (upper_marker = center; upper_marker >= 0; upper_marker--)
if (markers.markers_array[upper_marker] & MARKER_FLAG_BIT) break;
// searching down starting from center+1
for (lower_marker = center+1; lower_marker < movie_size; ++lower_marker)
if (markers.markers_array[lower_marker] & MARKER_FLAG_BIT) break;
if (upper_marker == -1 && lower_marker == movie_size)
{
SelectAll();
return;
}
//ClearSelection(); - need to clear without clearing focused, because otherwise there's strange bug when quickly pressing Ctrl+A right after clicking on already selected row
ListView_SetItemState(hwndList, -1, 0, LVIS_SELECTED);
// selecting circle:
if (upper_border > upper_marker+1 || lower_border < lower_marker-1 || lower_border > lower_marker)
{
// default: select all between markers
for (int i = upper_marker+1; i < lower_marker; ++i)
{
ListView_SetItemState(hwndList,i,LVIS_SELECTED,LVIS_SELECTED);
}
} else if (upper_border == upper_marker+1 && lower_border == lower_marker-1)
{
// already selected all between markers - now select both markers or at least select the marker that is not outside movie range
if (upper_marker < 0) upper_marker = 0;
if (lower_marker >= movie_size) lower_marker = movie_size - 1;
for (int i = upper_marker; i <= lower_marker; ++i)
{
ListView_SetItemState(hwndList,i,LVIS_SELECTED,LVIS_SELECTED);
}
} else if (upper_border <= upper_marker && lower_border >= lower_marker)
{
// selected all between markers and both markers selected too - now deselect lower marker
for (int i = upper_marker; i < lower_marker; ++i)
{
ListView_SetItemState(hwndList,i,LVIS_SELECTED,LVIS_SELECTED);
}
} else if (upper_border == upper_marker && lower_border == lower_marker-1)
{
// selected all between markers and upper marker selected too - now deselect upper marker and (if lower marker < movie_size) reselect lower marker
if (lower_marker >= movie_size) lower_marker = movie_size - 1;
for (int i = upper_marker+1; i <= lower_marker; ++i)
{
ListView_SetItemState(hwndList,i,LVIS_SELECTED,LVIS_SELECTED);
}
} else if (upper_border == upper_marker+1 && lower_border == lower_marker)
{
// selected all between markers and lower marker selected too - now deselect lower marker (return to "selected all between markers")
for (int i = upper_marker + 1; i < lower_marker; ++i)
{
ListView_SetItemState(hwndList,i,LVIS_SELECTED,LVIS_SELECTED);
}
}
}
bool Copy()
{
if (selectionFrames.size()==0) return false;
if (selection.CurrentSelection().size() == 0) return false;
int cframe=*selectionFrames.begin()-1;
int cframe = *selection.CurrentSelection().begin()-1;
try
{
int range = *selectionFrames.rbegin() - *selectionFrames.begin()+1;
int range = *selection.CurrentSelection().rbegin() - *selection.CurrentSelection().begin()+1;
//std::string outbuf clipString("TAS");
std::stringstream clipString;
clipString << "TAS " << range << std::endl;
for(TSelectionFrames::iterator it(selectionFrames.begin()); it != selectionFrames.end(); it++)
for(SelectionFrames::iterator it(selection.CurrentSelection().begin()); it != selection.CurrentSelection().end(); it++)
{
if (*it>cframe+1)
{
@ -793,7 +716,7 @@ bool Copy()
{
if (currMovieData.records[*it].joysticks[joy] & (1<<bit))
{
clipString << MovieRecord::mnemonics[bit];
clipString << buttonNames[bit];
}
}
}
@ -823,7 +746,8 @@ bool Copy()
{
return false;
}
// copied successfully
selection.MemorizeClipboardSelection();
return true;
}
void Cut()
@ -836,10 +760,10 @@ void Cut()
bool Paste()
{
bool result = false;
if (selectionFrames.size()==0)
if (selection.CurrentSelection().size()==0)
return false;
int pos = *selectionFrames.begin();
int pos = *selection.CurrentSelection().begin();
if (!OpenClipboard(hwndTasEdit))
return false;
@ -895,7 +819,7 @@ bool Paste()
default:
for (int bit=0; bit<NUM_JOYPAD_BUTTONS; ++bit)
{
if (*frame==MovieRecord::mnemonics[bit])
if (*frame == buttonNames[bit][0])
{
currMovieData.records[pos].joysticks[joy]|=(1<<bit);
break;
@ -908,7 +832,7 @@ bool Paste()
pGlobal = strchr(pGlobal, '\n');
}
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_PASTE, *selectionFrames.begin()));
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_PASTE, *selection.CurrentSelection().begin()));
result = true;
}
@ -929,7 +853,7 @@ LRESULT APIENTRY HeaderWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam
case WM_LBUTTONDOWN:
case WM_LBUTTONDBLCLK:
{
if (selectionFrames.size())
if (selection.CurrentSelection().size())
{
//perform hit test
HD_HITTESTINFO info;
@ -1193,7 +1117,6 @@ void OpenProject()
bool last_fourscore = currMovieData.fourscore;
// Load project
project.LoadProject(project.GetProjectFile());
UpdateList();
// update fourscore status
if (last_fourscore && !currMovieData.fourscore)
RemoveFourscore();
@ -1208,9 +1131,6 @@ void OpenProject()
// Saves current project
bool SaveProjectAs()
{
//Save project as new user selected filename
//flag project as not changed
const char TPfilter[]="TASEdit Project (*.tas)\0*.tas\0All Files (*.*)\0*.*\0\0"; //Filetype filter
OPENFILENAME ofn;
@ -1304,51 +1224,6 @@ void Export()
}
}
//used to track selection
void ItemRangeChanged(NMLVODSTATECHANGE* info)
{
bool ON = !(info->uOldState & LVIS_SELECTED) && (info->uNewState & LVIS_SELECTED);
bool OFF = (info->uOldState & LVIS_SELECTED) && !(info->uNewState & LVIS_SELECTED);
if(ON)
for(int i=info->iFrom;i<=info->iTo;i++)
selectionFrames.insert(i);
else
for(int i=info->iFrom;i<=info->iTo;i++)
selectionFrames.erase(i);
}
void ItemChanged(NMLISTVIEW* info)
{
int item = info->iItem;
bool ON = !(info->uOldState & LVIS_SELECTED) && (info->uNewState & LVIS_SELECTED);
bool OFF = (info->uOldState & LVIS_SELECTED) && !(info->uNewState & LVIS_SELECTED);
//if the item is -1, apply the change to all items
if(item == -1)
{
if(OFF)
{
// clear all
selectionFrames.clear();
} else if (ON)
{
// select all
for(int i = currMovieData.getNumRecords() - 1; i >= 0; i--)
{
selectionFrames.insert(i);
}
}
}
else
{
if(ON)
selectionFrames.insert(item);
else if(OFF)
selectionFrames.erase(item);
}
}
BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
@ -1391,7 +1266,6 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar
TasEdit_wndy = wrect.top;
WindowBoundsCheckNoResize(TasEdit_wndx,TasEdit_wndy,wrect.right);
// also move screenshot bitmap if it's open
extern HWND hwndScrBmp;
if (hwndScrBmp)
SetWindowPos(hwndScrBmp, 0, TasEdit_wndx + bookmarks.scr_bmp_x, TasEdit_wndy + bookmarks.scr_bmp_y, 0, 0,SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
}
@ -1421,10 +1295,10 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar
RightClick((LPNMITEMACTIVATE)lParam);
break;
case LVN_ITEMCHANGED:
ItemChanged((LPNMLISTVIEW) lParam);
selection.ItemChanged((LPNMLISTVIEW) lParam);
break;
case LVN_ODSTATECHANGED:
ItemRangeChanged((LPNMLVODSTATECHANGE) lParam);
selection.ItemRangeChanged((LPNMLVODSTATECHANGE) lParam);
break;
/*
case LVN_ENDSCROLL:
@ -1518,7 +1392,7 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar
ExitTasEdit();
break;
case ID_EDIT_SELECTALL:
SelectAll();
selection.SelectAll();
break;
case ACCEL_CTRL_X:
case ID_TASEDIT_CUT:
@ -1535,7 +1409,7 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar
case ACCEL_CTRL_DELETE:
case ID_TASEDIT_DELETE:
case ID_CONTEXT_SELECTED_DELETEFRAMES:
if (selectionFrames.size()) DeleteFrames();
DeleteFrames();
break;
case ACCEL_CTRL_T:
case ID_EDIT_TRUNCATE:
@ -1551,41 +1425,17 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar
case ID_EDIT_INSERT:
case MENU_CONTEXT_STRAY_INSERTFRAMES:
case ID_CONTEXT_SELECTED_INSERTFRAMES2:
{
int frames = selectionFrames.size();
if(CWin32InputBox::GetInteger("Insert number of Frames", "How many frames?", frames, hwndDlg) == IDOK)
{
if (frames > 0)
{
if (selectionFrames.size())
{
// insert at selection
int index = *selectionFrames.begin();
currMovieData.insertEmpty(index, frames);
if (TASEdit_bind_markers)
markers.insertEmpty(index, frames);
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_INSERT, index));
} else
{
// insert at playback cursor
currMovieData.insertEmpty(currFrameCounter, frames);
if (TASEdit_bind_markers)
markers.insertEmpty(currFrameCounter, frames);
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_INSERT, currFrameCounter));
}
}
}
}
InsertNumFrames();
break;
case ACCEL_CTRL_INSERT:
case ID_EDIT_INSERTFRAMES:
case ID_CONTEXT_SELECTED_INSERTFRAMES:
if (selectionFrames.size()) InsertFrames();
InsertFrames();
break;
case ACCEL_DEL:
case ID_EDIT_CLEAR:
case ID_CONTEXT_SELECTED_CLEARFRAMES:
if (selectionFrames.size()) ClearFrames();
ClearFrames();
break;
case TASEDIT_PLAYSTOP:
playback.ToggleEmulationPause();
@ -1598,7 +1448,7 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar
// if switched off then jump to selection
if (TASEdit_follow_playback)
FollowPlayback();
else if (selectionFrames.size())
else if (selection.CurrentSelection().size())
FollowSelection();
else if (playback.pauseframe)
FollowPauseframe();
@ -1613,12 +1463,17 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar
//switch "Show Markers" flag
TASEdit_show_markers ^= 1;
CheckMenuItem(hmenu, ID_VIEW_SHOW_MARKERS, TASEdit_show_markers?MF_CHECKED : MF_UNCHECKED);
RedrawList();
RedrawList(); // no need to redraw Bookmarks, as Markers are only shown in main list
break;
case ID_VIEW_SHOWDOTINEMPTYCELLS:
TASEdit_show_dot ^= 1;
CheckMenuItem(hmenu, ID_VIEW_SHOWDOTINEMPTYCELLS, TASEdit_show_dot?MF_CHECKED : MF_UNCHECKED);
RedrawList();
case ID_VIEW_SHOWBRANCHSCREENSHOTS:
//switch "Show Branch Screenshots" flag
TASEdit_show_branch_screenshots ^= 1;
CheckMenuItem(hmenu, ID_VIEW_SHOWBRANCHSCREENSHOTS, TASEdit_show_branch_screenshots?MF_CHECKED : MF_UNCHECKED);
break;
case ID_VIEW_ENABLEHOTCHANGES:
TASEdit_enable_hot_changes ^= 1;
CheckMenuItem(hmenu, ID_VIEW_ENABLEHOTCHANGES, TASEdit_enable_hot_changes?MF_CHECKED : MF_UNCHECKED);
RedrawList(); // redraw buttons text
break;
case ID_VIEW_JUMPWHENMAKINGUNDO:
TASEdit_jump_to_undo ^= 1;
@ -1660,6 +1515,7 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar
{
TasEdit_undo_levels = new_size;
history.init(TasEdit_undo_levels);
selection.init(TasEdit_undo_levels);
// hot changes were cleared, so update list
RedrawList();
}
@ -1680,15 +1536,6 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar
}
break;
}
case ID_CONFIG_MUTETURBO:
muteTurbo ^= 1;
CheckMenuItem(hmenu, ID_CONFIG_MUTETURBO, muteTurbo?MF_CHECKED : MF_UNCHECKED);
break;
case ID_CONFIG_BINDMARKERSTOINPUT:
//switch "Bind Markers to Input" flag
TASEdit_bind_markers ^= 1;
CheckMenuItem(hmenu, ID_CONFIG_BINDMARKERSTOINPUT, TASEdit_bind_markers?MF_CHECKED : MF_UNCHECKED);
break;
case ID_CONFIG_BRANCHESRESTOREFULLMOVIE:
//switch "Branches restore entire Movie" flag
TASEdit_branch_full_movie ^= 1;
@ -1705,6 +1552,15 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar
TASEdit_branch_scr_hud ^= 1;
CheckMenuItem(hmenu, ID_CONFIG_HUDINBRANCHSCREENSHOTS, TASEdit_branch_scr_hud?MF_CHECKED : MF_UNCHECKED);
break;
case ID_CONFIG_BINDMARKERSTOINPUT:
//switch "Bind Markers to Input" flag
TASEdit_bind_markers ^= 1;
CheckMenuItem(hmenu, ID_CONFIG_BINDMARKERSTOINPUT, TASEdit_bind_markers?MF_CHECKED : MF_UNCHECKED);
break;
case ID_CONFIG_MUTETURBO:
muteTurbo ^= 1;
CheckMenuItem(hmenu, ID_CONFIG_MUTETURBO, muteTurbo?MF_CHECKED : MF_UNCHECKED);
break;
case IDC_PROGRESS_BUTTON:
// click on progressbar - stop seeking
if (playback.pauseframe) playback.SeekingStop();
@ -1746,12 +1602,12 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar
case ACCEL_CTRL_A:
case ID_EDIT_SELECTMIDMARKERS:
case ID_SELECTED_SELECTMIDMARKERS:
SelectMidMarkers();
selection.SelectMidMarkers();
break;
case ACCEL_SHIFT_INS:
case ID_EDIT_CLONEFRAMES:
case ID_SELECTED_CLONE:
if (selectionFrames.size()) CloneFrames();
CloneFrames();
break;
case ACCEL_CTRL_Z:
case ID_EDIT_UNDO:
@ -1777,6 +1633,27 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar
}
break;
}
case ID_EDIT_SELECTIONUNDO:
case ACCEL_CTRL_Q:
{
selection.undo();
FollowSelection();
break;
}
case ID_EDIT_SELECTIONREDO:
case ACCEL_CTRL_W:
{
selection.redo();
FollowSelection();
break;
}
case ID_EDIT_RESELECTCLIPBOARD:
case ACCEL_CTRL_B:
{
selection.ReselectClipboard();
FollowSelection();
break;
}
}
break;
@ -1831,10 +1708,12 @@ void FollowUndo()
}
void FollowSelection()
{
if (selection.CurrentSelection().size() == 0) return;
int list_items = listItems;
if (currMovieData.fourscore) list_items--;
int selection_start = *selectionFrames.begin();
int selection_end = *selectionFrames.rbegin();
int selection_start = *selection.CurrentSelection().begin();
int selection_end = *selection.CurrentSelection().rbegin();
int selection_items = 1 + selection_end - selection_start;
if (selection_items <= list_items)
@ -1917,22 +1796,23 @@ void EnterTasEdit()
CheckDlgButton(hwndTasEdit, CHECK_FOLLOW_CURSOR, TASEdit_follow_playback?MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hmenu, ID_VIEW_SHOW_LAG_FRAMES, TASEdit_show_lag_frames?MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hmenu, ID_VIEW_SHOW_MARKERS, TASEdit_show_markers?MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hmenu, ID_VIEW_SHOWBRANCHSCREENSHOTS, TASEdit_show_branch_screenshots?MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hmenu, ID_VIEW_JUMPWHENMAKINGUNDO, TASEdit_jump_to_undo?MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hmenu, ID_CONFIG_BINDMARKERSTOINPUT, TASEdit_bind_markers?MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hmenu, ID_CONFIG_BRANCHESRESTOREFULLMOVIE, TASEdit_branch_full_movie?MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hmenu, ID_CONFIG_BRANCHESWORKONLYWHENRECORDING, TASEdit_branch_only_when_rec?MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hmenu, ID_CONFIG_HUDINBRANCHSCREENSHOTS, TASEdit_branch_scr_hud?MF_CHECKED : MF_UNCHECKED);
CheckDlgButton(hwndTasEdit,CHECK_AUTORESTORE_PLAYBACK,TASEdit_restore_position?BST_CHECKED:BST_UNCHECKED);
CheckMenuItem(hmenu, ID_CONFIG_BINDMARKERSTOINPUT, TASEdit_bind_markers?MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hmenu, ID_VIEW_ENABLEHOTCHANGES, TASEdit_enable_hot_changes?MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hmenu, ID_CONFIG_MUTETURBO, muteTurbo?MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hmenu, ID_VIEW_SHOWDOTINEMPTYCELLS, TASEdit_show_dot?MF_CHECKED : MF_UNCHECKED);
CheckDlgButton(hwndTasEdit,CHECK_AUTORESTORE_PLAYBACK,TASEdit_restore_position?BST_CHECKED:BST_UNCHECKED);
SetWindowPos(hwndTasEdit,HWND_TOP,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE|SWP_NOOWNERZORDER);
SetWindowPos(hwndTasEdit, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE|SWP_NOOWNERZORDER);
// init modules
greenzone.init();
playback.init();
// either start new movie or use current movie
if (movieMode == MOVIEMODE_INACTIVE)
if (FCEUMOV_Mode(MOVIEMODE_INACTIVE))
{
FCEUI_StopMovie();
CreateCleanMovie();
@ -1948,15 +1828,23 @@ void EnterTasEdit()
movie_readonly = true;
multitrack_recording_joypad = MULTITRACK_RECORDING_ALL;
RecheckRecordingRadioButtons();
// switch to tasedit mode
movieMode = MOVIEMODE_TASEDIT;
// create font for main listview
hMainListFont = CreateFont(13, 8, /*Height,Width*/
// create fonts for main listview
hMainListFont = CreateFont(15, 10, /*Height,Width*/
0, 0, /*escapement,orientation*/
FW_REGULAR, FALSE, FALSE, FALSE, /*weight, italic, underline, strikeout*/
FW_BOLD, FALSE, FALSE, FALSE, /*weight, italic, underline, strikeout*/
ANSI_CHARSET, OUT_DEVICE_PRECIS, CLIP_MASK, /*charset, precision, clipping*/
DEFAULT_QUALITY, DEFAULT_PITCH, /*quality, and pitch*/
"Courier"); /*font name*/
hMainListSelectFont = CreateFont(14, 7, /*Height,Width*/
0, 0, /*escapement,orientation*/
FW_BOLD, FALSE, FALSE, FALSE, /*weight, italic, underline, strikeout*/
ANSI_CHARSET, OUT_DEVICE_PRECIS, CLIP_MASK, /*charset, precision, clipping*/
DEFAULT_QUALITY, DEFAULT_PITCH, /*quality, and pitch*/
"Arial"); /*font name*/
// prepare the main listview
ListView_SetExtendedListViewStyleEx(hwndList, LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES, LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES);
@ -2041,7 +1929,7 @@ void EnterTasEdit()
// frame number column
lvc.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_FMT;
lvc.fmt = LVCFMT_CENTER;
lvc.cx = 74;
lvc.cx = 75;
lvc.pszText = "Frame#";
ListView_InsertColumn(hwndList, colidx++, &lvc);
// pads columns
@ -2079,12 +1967,15 @@ void EnterTasEdit()
// jump_frame column
lvc.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_FMT;
lvc.fmt = LVCFMT_CENTER;
lvc.cx = 72;
lvc.cx = 74;
ListView_InsertColumn(hwndBookmarksList, 1, &lvc);
// time column
lvc.cx = 82;
lvc.cx = 80;
ListView_InsertColumn(hwndBookmarksList, 2, &lvc);
// subclass BranchesBitmap
hwndBranchesBitmap_oldWndProc = (WNDPROC)SetWindowLong(hwndBranchesBitmap, GWL_WNDPROC, (LONG)BranchesBitmapWndProc);
// prepare the history listview
ListView_SetExtendedListViewStyleEx(hwndHistoryList, LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES, LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES);
// subclass the listview
@ -2094,14 +1985,12 @@ void EnterTasEdit()
lvc.fmt = LVCFMT_LEFT;
ListView_InsertColumn(hwndHistoryList, 0, &lvc);
// subclass BranchesBitmap
hwndBranchesBitmap_oldWndProc = (WNDPROC)SetWindowLong(hwndBranchesBitmap, GWL_WNDPROC, (LONG)BranchesBitmapWndProc);
// init variables
markers.init();
project.init();
bookmarks.init();
history.init(TasEdit_undo_levels);
selection.init(TasEdit_undo_levels);
SetFocus(hwndHistoryList); // to show darkblue cursor
SetFocus(hwndList);
FCEU_DispMessage("Tasedit engaged",0);
@ -2132,6 +2021,7 @@ bool ExitTasEdit()
bookmarks.free();
history.free();
playback.SeekingStop();
selection.free();
movieMode = MOVIEMODE_INACTIVE;
FCEU_DispMessage("Tasedit disengaged",0);

View File

@ -65,6 +65,8 @@
#define DIGITS_IN_FRAMENUM 7
#define ARROW_IMAGE_ID 20
// listview colors
#define NORMAL_TEXT_COLOR 0x0
#define NORMAL_FRAMENUM_COLOR 0xFFFFFF
#define NORMAL_INPUT_COLOR1 0xEDEDED
#define NORMAL_INPUT_COLOR2 0xDEDEDE
@ -94,7 +96,7 @@
#define UNDOHINT_INPUT_COLOR2 0xE5B7CC
#define MARKED_FRAMENUM_COLOR 0xC0FCFF
#define CUR_MARKED_FRAMENUM_COLOR 0xDEF7F4
#define CUR_MARKED_FRAMENUM_COLOR 0xDEF7F3
#define MARKED_UNDOHINT_FRAMENUM_COLOR 0xE1E7EC
// greenzone cleaning masks
@ -103,6 +105,11 @@
#define EVERY4TH 0xFFFFFFFC
#define EVERY2ND 0xFFFFFFFE
// -----------------------------
enum ECONTEXTMENU
{
CONTEXTMENU_STRAY = 0,
CONTEXTMENU_SELECTED = 1,
};
void EnterTasEdit();
void InitDialog();
bool ExitTasEdit();
@ -114,8 +121,6 @@ void FollowPlayback();
void FollowUndo();
void FollowSelection();
void FollowPauseframe();
void ClearSelection();
void ClearRowSelection(int index);
void AddFourscore();
void RemoveFourscore();
void RedrawWindowCaption();
@ -132,10 +137,9 @@ void OpenProject();
bool SaveProject();
bool SaveProjectAs();
bool AskSaveProject();
void SelectAll();
void SelectMidMarkers();
void CloneFrames();
void InsertFrames();
void InsertNumFrames();
void DeleteFrames();
void ClearFrames(bool cut = false);
void Truncate();

View File

@ -7,8 +7,11 @@
#include "zlib.h"
extern GREENZONE greenzone;
extern INPUT_HISTORY history;
extern bool TASEdit_branch_scr_hud;
extern bool TASEdit_enable_hot_changes;
extern uint8 *XBuf;
extern uint8 *XBackBuf;
@ -27,8 +30,12 @@ void BOOKMARK::init()
void BOOKMARK::set()
{
snapshot.init(currMovieData, false);
// copy input and hotchanges
snapshot.init(currMovieData, TASEdit_enable_hot_changes);
snapshot.jump_frame = currFrameCounter;
if (TASEdit_enable_hot_changes)
snapshot.copyHotChanges(&history.GetCurrentSnapshot());
// copy savestate
savestate = greenzone.savestates[currFrameCounter];
// save screenshot
uLongf comprlen = (SCREENSHOT_SIZE>>9)+12 + SCREENSHOT_SIZE;

View File

@ -46,13 +46,14 @@ extern bool TASEdit_bind_markers;
extern bool TASEdit_branch_full_movie;
extern bool TASEdit_branch_only_when_rec;
extern bool TASEdit_view_branches_tree;
extern bool TASEdit_show_branch_screenshots;
BOOKMARKS::BOOKMARKS()
{
// create font
hBookmarksFont = CreateFont(13, 8, /*Height,Width*/
hBookmarksFont = CreateFont(15, 10, /*Height,Width*/
0, 0, /*escapement,orientation*/
FW_REGULAR, FALSE, FALSE, FALSE, /*weight, italic, underline, strikeout*/
FW_BOLD, FALSE, FALSE, FALSE, /*weight, italic, underline, strikeout*/
ANSI_CHARSET, OUT_DEVICE_PRECIS, CLIP_MASK, /*charset, precision, clipping*/
DEFAULT_QUALITY, DEFAULT_PITCH, /*quality, and pitch*/
"Courier"); /*font name*/
@ -292,11 +293,11 @@ void BOOKMARKS::update()
if (must_check_item_under_mouse)
CheckMousePos();
// render branches_bitmap
if (must_redraw_branches_tree)
if (edit_mode == EDIT_MODE_BRANCHES && must_redraw_branches_tree)
RedrawBranchesTree();
// change screenshot_bitmap alpha if needed
if (item_under_mouse >= 0 && item_under_mouse < TOTAL_BOOKMARKS)
if (item_under_mouse >= 0 && item_under_mouse < TOTAL_BOOKMARKS && TASEdit_show_branch_screenshots)
{
if (!hwndScrBmp)
{
@ -315,18 +316,24 @@ void BOOKMARKS::update()
{
scr_bmp_phase++;
// update alpha
SetLayeredWindowAttributes(hwndScrBmp, 0, (255 * scr_bmp_phase) / SCR_BMP_PHASE_MAX, LWA_ALPHA);
int phase_alpha = scr_bmp_phase;
if (phase_alpha > SCR_BMP_PHASE_ALPHA_MAX) phase_alpha = SCR_BMP_PHASE_ALPHA_MAX;
SetLayeredWindowAttributes(hwndScrBmp, 0, (255 * phase_alpha) / SCR_BMP_PHASE_ALPHA_MAX, LWA_ALPHA);
UpdateLayeredWindow(hwndScrBmp, 0, 0, 0, 0, 0, 0, &blend, ULW_ALPHA);
}
} else
{
// fade and finally hide screenshot
if (scr_bmp_phase > 0)
scr_bmp_phase--;
if (scr_bmp_phase > 0)
{
scr_bmp_phase--;
if (hwndScrBmp)
{
// update alpha
SetLayeredWindowAttributes(hwndScrBmp, 0, (255 * scr_bmp_phase) / SCR_BMP_PHASE_MAX, LWA_ALPHA);
int phase_alpha = scr_bmp_phase;
if (phase_alpha > SCR_BMP_PHASE_ALPHA_MAX) phase_alpha = SCR_BMP_PHASE_ALPHA_MAX;
SetLayeredWindowAttributes(hwndScrBmp, 0, (255 * phase_alpha) / SCR_BMP_PHASE_ALPHA_MAX, LWA_ALPHA);
UpdateLayeredWindow(hwndScrBmp, 0, 0, 0, 0, 0, 0, &blend, ULW_ALPHA);
}
} else
@ -426,11 +433,11 @@ void BOOKMARKS::unleash(int slot)
if (!bookmarks_array[slot].not_empty) return;
int jump_frame = bookmarks_array[slot].snapshot.jump_frame;
// revert movie to the input_snapshot state
bool markers_changed = false;
// revert current movie to the input_snapshot state
if (TASEdit_branch_full_movie)
{
// update Markers
bool markers_changed = false;
if (TASEdit_bind_markers)
{
if (bookmarks_array[slot].snapshot.checkMarkersDiff())
@ -448,12 +455,12 @@ void BOOKMARKS::unleash(int slot)
currMovieData.records.resize(bookmarks_array[slot].snapshot.size);
bookmarks_array[slot].snapshot.toMovie(currMovieData, first_change);
UpdateList();
history.RegisterBranch(MODTYPE_BRANCH_0 + slot, first_change, bookmarks_array[slot].snapshot.description);
history.RegisterBranching(MODTYPE_BRANCH_0 + slot, first_change, slot);
greenzone.Invalidate(first_change);
bookmarks_array[slot].unleashed();
} else if (markers_changed)
{
history.RegisterBranch(MODTYPE_BRANCH_MARKERS_0 + slot, first_change, bookmarks_array[slot].snapshot.description);
history.RegisterBranching(MODTYPE_BRANCH_MARKERS_0 + slot, first_change, slot);
RedrawList();
bookmarks_array[slot].unleashed();
} else
@ -464,7 +471,6 @@ void BOOKMARKS::unleash(int slot)
} else
{
// update Markers
bool markers_changed = false;
if (TASEdit_bind_markers)
{
if (bookmarks_array[slot].snapshot.checkMarkersDiff(jump_frame))
@ -482,12 +488,12 @@ void BOOKMARKS::unleash(int slot)
if (currMovieData.getNumRecords() <= jump_frame) currMovieData.records.resize(jump_frame+1); // but if old movie is shorter, include last frame as blank frame
bookmarks_array[slot].snapshot.toMovie(currMovieData, first_change, jump_frame-1);
UpdateList();
history.RegisterBranch(MODTYPE_BRANCH_0 + slot, first_change, bookmarks_array[slot].snapshot.description);
history.RegisterBranching(MODTYPE_BRANCH_0 + slot, first_change, slot);
greenzone.Invalidate(first_change);
bookmarks_array[slot].unleashed();
} else if (markers_changed)
{
history.RegisterBranch(MODTYPE_BRANCH_MARKERS_0 + slot, first_change, bookmarks_array[slot].snapshot.description);
history.RegisterBranching(MODTYPE_BRANCH_MARKERS_0 + slot, first_change, slot);
RedrawList();
bookmarks_array[slot].unleashed();
} else
@ -746,15 +752,25 @@ void BOOKMARKS::RedrawBranchesTree()
BitBlt(hBitmapDC, branch_x, branch_y, DIGIT_BITMAP_WIDTH, DIGIT_BITMAP_HEIGHT, hSpritesheetDC, i * DIGIT_BITMAP_WIDTH, 0, SRCCOPY);
}
}
// jump_frame of item under cursor (except cloud - it doesn't have particular frame)
if (item_under_mouse > ITEM_UNDER_MOUSE_CLOUD)
{
char framenum_string[DIGITS_IN_FRAMENUM+1] = {0};
if (item_under_mouse < TOTAL_BOOKMARKS)
U32ToDecStr(framenum_string, bookmarks_array[item_under_mouse].snapshot.jump_frame, DIGITS_IN_FRAMENUM);
else
U32ToDecStr(framenum_string, currFrameCounter, DIGITS_IN_FRAMENUM);
TextOut(hBitmapDC, BRANCHES_BITMAP_FRAMENUM_X, BRANCHES_BITMAP_FRAMENUM_Y, (LPCSTR)framenum_string, DIGITS_IN_FRAMENUM);
}
// time of item under cursor
if (item_under_mouse > ITEM_UNDER_MOUSE_NONE)
{
if (item_under_mouse == ITEM_UNDER_MOUSE_CLOUD)
TextOut(hBitmapDC, 0, 0, (LPCSTR)cloud_time, 8);
TextOut(hBitmapDC, BRANCHES_BITMAP_TIME_X, BRANCHES_BITMAP_TIME_Y, (LPCSTR)cloud_time, TIME_DESC_LENGTH-1);
else if (item_under_mouse < TOTAL_BOOKMARKS)
TextOut(hBitmapDC, 0, 0, (LPCSTR)bookmarks_array[item_under_mouse].snapshot.description, 8);
TextOut(hBitmapDC, BRANCHES_BITMAP_TIME_X, BRANCHES_BITMAP_TIME_Y, (LPCSTR)bookmarks_array[item_under_mouse].snapshot.description, TIME_DESC_LENGTH-1);
else
TextOut(hBitmapDC, 0, 0, (LPCSTR)current_pos_time, 8);
TextOut(hBitmapDC, BRANCHES_BITMAP_TIME_X, BRANCHES_BITMAP_TIME_Y, (LPCSTR)current_pos_time, TIME_DESC_LENGTH-1);
}
// finished
must_redraw_branches_tree = false;

View File

@ -19,6 +19,10 @@
#define BRANCHES_BITMAP_WIDTH 170
#define BRANCHES_BITMAP_HEIGHT 145
#define BRANCHES_ANIMATION_FRAMES 12
#define BRANCHES_BITMAP_FRAMENUM_X 0
#define BRANCHES_BITMAP_FRAMENUM_Y 0
#define BRANCHES_BITMAP_TIME_X 0
#define BRANCHES_BITMAP_TIME_Y BRANCHES_BITMAP_HEIGHT - 17
// constants for drawing branches tree
#define BRANCHES_CANVAS_WIDTH 146
#define BRANCHES_CANVAS_HEIGHT 130
@ -82,8 +86,8 @@
#define TIME_DESC_LENGTH 9 // "HH:MM:SS"
// screenshot bitmap
#define SCR_BMP_PHASE_MAX 10
#define SCR_BMP_PHASE_MAX 11
#define SCR_BMP_PHASE_ALPHA_MAX 8
class BOOKMARKS
{

View File

@ -231,7 +231,7 @@ bool GREENZONE::load(EMUFILE *is)
if ((int)is->fread(save_id, GREENZONE_ID_LEN) < GREENZONE_ID_LEN) goto error;
if (strcmp(greenzone_save_id, save_id)) goto error; // string is not valid
// read size
if (read32le((uint32 *)&size, is) && size >= 0 && size <= currMovieData.getNumRecords())
if (read32le(&size, is) && size >= 0 && size <= currMovieData.getNumRecords())
{
greenZoneCount = size;
savestates.resize(greenZoneCount);
@ -246,7 +246,7 @@ bool GREENZONE::load(EMUFILE *is)
int e = uncompress(&lag_history[0], &destlen, &cbuf[0], comprlen);
if (e != Z_OK && e != Z_BUF_ERROR) goto error;
// read playback position
if (read32le((uint32 *)&frame, is))
if (read32le(&frame, is))
{
currFrameCounter = frame;
int greenzone_tail_frame = currFrameCounter - TASEdit_greenzone_capacity;
@ -257,7 +257,7 @@ bool GREENZONE::load(EMUFILE *is)
// read savestates
while(1)
{
if (!read32le((uint32 *)&frame, is)) break;
if (!read32le(&frame, is)) break;
if (frame < 0) break; // -1 = eof
// update TASEditor progressbar from time to time
if (frame / PROGRESSBAR_UPDATE_RATE > last_tick)
@ -268,7 +268,7 @@ bool GREENZONE::load(EMUFILE *is)
// read lua_colorings
// read monitorings
// read savestate
if (!read32le((uint32 *)&size, is)) break;
if (!read32le(&size, is)) break;
if (size < 0) break;
if (frame <= greenzone_tail_frame16
|| (frame <= greenzone_tail_frame8 && (frame & 0xF))

View File

@ -1,14 +1,14 @@
//Implementation file of Input History class (Undo feature)
#include "movie.h"
#include "../common.h"
#include "../tasedit.h"
#include "taseditproj.h"
#include "../tasedit.h" // just for mainlist functions, later this should be deleted
extern void FCEU_printf(char *format, ...);
extern HWND hwndHistoryList;
extern bool TASEdit_bind_markers;
extern bool TASEdit_enable_hot_changes;
extern bool TASEdit_branch_full_movie;
extern MARKERS markers;
extern BOOKMARKS bookmarks;
@ -68,13 +68,13 @@ void INPUT_HISTORY::init(int new_size)
undo_hint_pos = old_undo_hint_pos = undo_hint_time = -1;
old_show_undo_hint = show_undo_hint = false;
// clear snapshots history
history_total_items = 0;
free();
input_snapshots.resize(history_size);
history_start_pos = 0;
history_cursor_pos = -1;
// create initial snapshot
INPUT_SNAPSHOT inp;
inp.init(currMovieData, true);
inp.init(currMovieData, TASEdit_enable_hot_changes);
strcat(inp.description, modCaptions[0]);
inp.jump_frame = -1;
AddInputSnapshotToHistory(inp);
@ -85,6 +85,7 @@ void INPUT_HISTORY::init(int new_size)
void INPUT_HISTORY::free()
{
input_snapshots.resize(0);
history_total_items = 0;
}
void INPUT_HISTORY::update()
@ -149,11 +150,16 @@ int INPUT_HISTORY::jump(int new_pos)
currMovieData.records.resize(input_snapshots[real_pos].size);
input_snapshots[real_pos].toMovie(currMovieData, first_change);
bookmarks.ChangesMadeSinceBranch();
// list will be redrawn by greenzone invalidation
} else if (markers_changed)
{
markers.update();
bookmarks.ChangesMadeSinceBranch();
RedrawList();
} else if (TASEdit_enable_hot_changes)
{
// when using Hot Changes, list should be always redrawn, because old changes become less hot
RedrawList();
}
return first_change;
@ -173,7 +179,7 @@ void INPUT_HISTORY::AddInputSnapshotToHistory(INPUT_SNAPSHOT &inp)
int real_pos;
if (history_cursor_pos+1 >= history_size)
{
// reached the end of available history_size - move history_start_pos (thus deleting older snapshot)
// reached the end of available history_size - move history_start_pos (thus deleting oldest snapshot)
history_cursor_pos = history_size-1;
history_start_pos = (history_start_pos + 1) % history_size;
} else
@ -211,13 +217,12 @@ void INPUT_HISTORY::AddInputSnapshotToHistory(INPUT_SNAPSHOT &inp)
RedrawHistoryList();
}
// returns frame of first actual change
int INPUT_HISTORY::RegisterChanges(int mod_type, int start, int end)
{
// create new input shanshot
INPUT_SNAPSHOT inp;
inp.init(currMovieData, true);
inp.init(currMovieData, TASEdit_enable_hot_changes);
if (mod_type == MODTYPE_MARKER_SET || mod_type == MODTYPE_MARKER_UNSET)
{
// special case: changed markers, but input didn't change
@ -235,6 +240,8 @@ int INPUT_HISTORY::RegisterChanges(int mod_type, int start, int end)
strcat(inp.description, "-");
strcat(inp.description, framenum);
}
if (TASEdit_enable_hot_changes)
inp.copyHotChanges(&GetCurrentSnapshot());
AddInputSnapshotToHistory(inp);
bookmarks.ChangesMadeSinceBranch();
return -1;
@ -247,10 +254,6 @@ int INPUT_HISTORY::RegisterChanges(int mod_type, int start, int end)
if (first_changes >= 0)
{
// differences found
// fade old hot_changes by 1
// highlight new hot changes
// fill description:
strcat(inp.description, modCaptions[mod_type]);
switch (mod_type)
@ -278,14 +281,14 @@ int INPUT_HISTORY::RegisterChanges(int mod_type, int start, int end)
}
case MODTYPE_RECORD:
{
// add info which joypads were affected
inp.jump_frame = start;
// also add info which joypads were affected
int num = (inp.input_type + 1) * 2; // hacky, only for distingushing between normal2p and fourscore
for (int i = 0; i < num; ++i)
{
if (inp.checkJoypadDiff(input_snapshots[real_pos], first_changes, i))
strcat(inp.description, joypadCaptions[i]);
}
inp.jump_frame = start;
}
}
// add upper and lower frame to description
@ -299,21 +302,75 @@ int INPUT_HISTORY::RegisterChanges(int mod_type, int start, int end)
strcat(inp.description, "-");
strcat(inp.description, framenum);
}
// set hotchanges
if (TASEdit_enable_hot_changes)
{
// inherit previous hotchanges and set new changes
switch (mod_type)
{
case MODTYPE_DELETE:
inp.inheritHotChanges_DeleteSelection(&input_snapshots[real_pos]);
break;
case MODTYPE_INSERT:
case MODTYPE_PASTEINSERT:
case MODTYPE_CLONE:
inp.inheritHotChanges_InsertSelection(&input_snapshots[real_pos]);
break;
case MODTYPE_CHANGE:
case MODTYPE_SET:
case MODTYPE_UNSET:
case MODTYPE_CLEAR:
case MODTYPE_CUT:
case MODTYPE_PASTE:
case MODTYPE_RECORD:
inp.inheritHotChanges(&input_snapshots[real_pos]);
inp.fillHotChanges(input_snapshots[real_pos], first_changes, end);
break;
case MODTYPE_TRUNCATE:
inp.copyHotChanges(&input_snapshots[real_pos]);
// do not add new hotchanges and do not fade old hotchanges, because there was nothing added
break;
case MODTYPE_IMPORT:
// do not inherit old hotchanges, because imported input (most likely) doesn't have direct connection with recent edits, so old hotchanges are irrelevant and should not be copied
inp.fillHotChanges(input_snapshots[real_pos], first_changes, end);
break;
}
}
AddInputSnapshotToHistory(inp);
bookmarks.ChangesMadeSinceBranch();
}
return first_changes;
}
}
void INPUT_HISTORY::RegisterBranch(int mod_type, int first_change, char* branch_creation_time)
void INPUT_HISTORY::RegisterBranching(int mod_type, int first_change, int slot)
{
// create new input shanshot
// create new input snapshot
INPUT_SNAPSHOT inp;
inp.init(currMovieData, true);
// fill description:
inp.init(currMovieData, TASEdit_enable_hot_changes);
// fill description: modification type + time of the Branch
strcat(inp.description, modCaptions[mod_type]);
strcat(inp.description, branch_creation_time);
strcat(inp.description, bookmarks.bookmarks_array[slot].snapshot.description);
inp.jump_frame = first_change;
if (TASEdit_enable_hot_changes)
{
if (mod_type < MODTYPE_BRANCH_MARKERS_0)
{
// input was changed
// copy hotchanges of the Branch
if (TASEdit_branch_full_movie)
{
inp.copyHotChanges(&bookmarks.bookmarks_array[slot].snapshot);
} else
{
// input was branched partially, so copy hotchanges only up to and not including jump_frame of the Branch
inp.copyHotChanges(&bookmarks.bookmarks_array[slot].snapshot, bookmarks.bookmarks_array[slot].snapshot.jump_frame);
}
} else
{
// input was not changed, only Markers were changed
inp.copyHotChanges(&GetCurrentSnapshot());
}
}
AddInputSnapshotToHistory(inp);
}
@ -345,8 +402,8 @@ bool INPUT_HISTORY::load(EMUFILE *is)
if ((int)is->fread(save_id, HISTORY_ID_LEN) < HISTORY_ID_LEN) goto error;
if (strcmp(history_save_id, save_id)) goto error; // string is not valid
// read vars
if (!read32le((uint32 *)&history_cursor_pos, is)) goto error;
if (!read32le((uint32 *)&history_total_items, is)) goto error;
if (!read32le(&history_cursor_pos, is)) goto error;
if (!read32le(&history_total_items, is)) goto error;
if (history_cursor_pos > history_total_items) goto error;
history_start_pos = 0;
// read snapshots
@ -373,7 +430,6 @@ bool INPUT_HISTORY::load(EMUFILE *is)
// load snapshots
for (i = 0; i < history_total_items; ++i)
{
// skip snapshots if current history_size is less then history_total_items
if (input_snapshots[i].load(is)) goto error;
playback.SetProgressbar(i, history_total_items);
}

View File

@ -63,11 +63,7 @@ public:
void AddInputSnapshotToHistory(INPUT_SNAPSHOT &inp);
int RegisterChanges(int mod_type, int start = 0, int end =-1);
void RegisterBranch(int mod_type, int first_change, char* branch_creation_time);
int InputChanged(int start, int end);
int InputInserted(int start);
int InputDeleted(int start);
void RegisterBranching(int mod_type, int first_change, int slot);
INPUT_SNAPSHOT& GetCurrentSnapshot();
INPUT_SNAPSHOT& GetNextToCurrentSnapshot();

View File

@ -1,8 +1,6 @@
//Implementation file of Input Snapshot class (Undo feature)
#include "movie.h"
#include "inputsnapshot.h"
#include "markers.h"
#include "taseditproj.h"
#include "zlib.h"
const int bytes_per_frame[NUM_SUPPORTED_INPUT_TYPES] = {2, 4}; // 16bits for normal joypads, 32bits for fourscore
@ -10,10 +8,10 @@ const int bytes_per_frame[NUM_SUPPORTED_INPUT_TYPES] = {2, 4}; // 16bits for nor
extern void FCEU_printf(char *format, ...);
extern MARKERS markers;
extern TASEDIT_SELECTION selection;
INPUT_SNAPSHOT::INPUT_SNAPSHOT()
{
}
void INPUT_SNAPSHOT::init(MovieData& md, bool hotchanges)
@ -64,6 +62,7 @@ void INPUT_SNAPSHOT::init(MovieData& md, bool hotchanges)
struct tm * timeinfo = localtime(&raw_time);
strftime(description, 10, "%H:%M:%S", timeinfo);
}
// copy all stored markers to Markers
void INPUT_SNAPSHOT::toMarkers()
{
@ -72,7 +71,7 @@ void INPUT_SNAPSHOT::toMarkers()
// copy some stored markers to Markers manually, from Frame 0 to end frame (including end frame)
void INPUT_SNAPSHOT::copyToMarkers(int end)
{
if (markers.markers_array.size() <= end) markers.markers_array.resize(end+1);
if ((int)markers.markers_array.size() <= end) markers.markers_array.resize(end+1);
for (int i = end; i >= 0; i--)
markers.markers_array[i] = markers_array[i];
}
@ -178,7 +177,6 @@ void INPUT_SNAPSHOT::save(EMUFILE *os)
// returns true if couldn't load
bool INPUT_SNAPSHOT::load(EMUFILE *is)
{
int len;
uint8 tmp;
// read vars
if (!read32le(&size, is)) return true;
@ -326,7 +324,7 @@ bool INPUT_SNAPSHOT::checkMarkersDiff()
// return true only when difference is found before end frame (not including end frame)
bool INPUT_SNAPSHOT::checkMarkersDiff(int end)
{
if (markers_array.size() != markers.markers_array.size() && (markers_array.size()-1 < end || markers.markers_array.size()-1 < end)) return true;
if (markers_array.size() != markers.markers_array.size() && ((int)markers_array.size()-1 < end || (int)markers.markers_array.size()-1 < end)) return true;
for (int i = end-1; i >= 0; i--)
if ((markers_array[i] - markers.markers_array[i]) & MARKER_FLAG_BIT) return true;
return false;
@ -338,7 +336,6 @@ int INPUT_SNAPSHOT::findFirstChange(INPUT_SNAPSHOT& inp, int start, int end)
// search for differences to the specified end (or to the end of this snapshot)
if (end < 0 || end >= size) end = size-1;
int inp_end = inp.size;
switch(input_type)
{
case FOURSCORE:
@ -391,7 +388,7 @@ int INPUT_SNAPSHOT::findFirstChange(INPUT_SNAPSHOT& inp, int start, int end)
}
}
// if current size is less then previous, return size-1 as the frame of difference
if (size < inp.size) return size-1;
if (size < inp_end) return size-1;
// no changes were found
return -1;
}
@ -432,7 +429,214 @@ int INPUT_SNAPSHOT::findFirstChange(MovieData& md, int start, int end)
return -1; // no changes were found
}
// --------------------------------------------------------
void INPUT_SNAPSHOT::copyHotChanges(INPUT_SNAPSHOT* source_of_hotchanges, int limit_frame_of_source)
{
// copy hot changes from source snapshot
if (source_of_hotchanges && source_of_hotchanges->has_hot_changes)
{
int min = hot_changes.size();
if (min > (int)source_of_hotchanges->hot_changes.size())
min = source_of_hotchanges->hot_changes.size();
// special case for Branches: if limit_frame if specified, then copy only hotchanges from 0 to limit_frame
if (limit_frame_of_source >= 0)
{
if (min > limit_frame_of_source * bytes_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY)
min = limit_frame_of_source * bytes_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY;
}
memcpy(&hot_changes[0], &source_of_hotchanges->hot_changes[0], min);
}
}
void INPUT_SNAPSHOT::inheritHotChanges(INPUT_SNAPSHOT* source_of_hotchanges)
{
// copy hot changes from source snapshot and fade them
if (source_of_hotchanges && source_of_hotchanges->has_hot_changes)
{
int min = hot_changes.size();
if (min > (int)source_of_hotchanges->hot_changes.size())
min = source_of_hotchanges->hot_changes.size();
memcpy(&hot_changes[0], &source_of_hotchanges->hot_changes[0], min);
FadeHotChanges();
}
}
void INPUT_SNAPSHOT::inheritHotChanges_DeleteSelection(INPUT_SNAPSHOT* source_of_hotchanges)
{
// copy hot changes from source snapshot, but omit deleted frames (which are represented by current selection)
if (source_of_hotchanges && source_of_hotchanges->has_hot_changes)
{
int bytes = bytes_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY;
int frame = 0, pos = 0, source_pos = 0;
int this_size = hot_changes.size(), source_size = source_of_hotchanges->hot_changes.size();
SelectionFrames::iterator it(selection.CurrentSelection().begin());
while (pos < this_size && source_pos < source_size)
{
if (it != selection.CurrentSelection().end() && frame == *it)
{
// this frame is selected
it++;
// omit the frame
source_pos += bytes;
} else
{
// copy hotchanges of this frame
memcpy(&hot_changes[pos], &source_of_hotchanges->hot_changes[source_pos], bytes);
pos += bytes;
source_pos += bytes;
}
frame++;
}
FadeHotChanges();
}
}
void INPUT_SNAPSHOT::inheritHotChanges_InsertSelection(INPUT_SNAPSHOT* source_of_hotchanges)
{
// copy hot changes from source snapshot, but insert filled lines for inserted frames (which are represented by current selection)
if (source_of_hotchanges && source_of_hotchanges->has_hot_changes)
{
int bytes = bytes_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY;
int frame = 0, region_len = 0, pos = 0, source_pos = 0;
int this_size = hot_changes.size(), source_size = source_of_hotchanges->hot_changes.size();
SelectionFrames::iterator it(selection.CurrentSelection().begin());
while (pos < this_size && source_pos < source_size)
{
if (it != selection.CurrentSelection().end() && frame == *it)
{
// this frame is selected
it++;
region_len++;
// set filled line to the frame
memset(&hot_changes[pos], 0xFF, bytes);
pos += bytes;
} else
{
// this frame is not selected
frame -= region_len;
region_len = 0;
// copy hotchanges of this frame
memcpy(&hot_changes[pos], &source_of_hotchanges->hot_changes[source_pos], bytes);
pos += bytes;
source_pos += bytes;
}
frame++;
}
FadeHotChanges();
} else
{
// no old data, just fill selected lines
int bytes = bytes_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY;
int frame = 0, region_len = 0, pos = 0;
int this_size = hot_changes.size();
SelectionFrames::iterator it(selection.CurrentSelection().begin());
while (pos < this_size)
{
if (it != selection.CurrentSelection().end() && frame == *it)
{
// this frame is selected
it++;
region_len++;
// set filled line to the frame
memset(&hot_changes[pos], 0xFF, bytes);
pos += bytes;
// exit loop when all selection frames are handled
if (it == selection.CurrentSelection().end()) break;
} else
{
// this frame is not selected
frame -= region_len;
region_len = 0;
// leave zeros in this frame
pos += bytes;
}
frame++;
}
}
}
void INPUT_SNAPSHOT::fillHotChanges(INPUT_SNAPSHOT& inp, int start, int end)
{
// search for differences to the specified end (or to the end of this snapshot)
if (end < 0 || end >= size) end = size-1;
int inp_end = inp.size;
switch(input_type)
{
case FOURSCORE:
{
for (int frame = start, pos = start * bytes_per_frame[input_type]; frame <= end; ++frame)
{
// set changed if found different byte, or found emptiness in inp when there's non-zero value here
if (frame < inp_end)
{
if (joysticks[pos] != inp.joysticks[pos])
SetMaxHotChange_Bits(frame, 0, joysticks[pos] ^ inp.joysticks[pos]);
pos++;
if (joysticks[pos] != inp.joysticks[pos])
SetMaxHotChange_Bits(frame, 1, joysticks[pos] ^ inp.joysticks[pos]);
pos++;
if (joysticks[pos] != inp.joysticks[pos])
SetMaxHotChange_Bits(frame, 2, joysticks[pos] ^ inp.joysticks[pos]);
pos++;
if (joysticks[pos] != inp.joysticks[pos])
SetMaxHotChange_Bits(frame, 3, joysticks[pos] ^ inp.joysticks[pos]);
pos++;
} else
{
if (joysticks[pos])
SetMaxHotChange_Bits(frame, 0, joysticks[pos]);
pos++;
if (joysticks[pos])
SetMaxHotChange_Bits(frame, 1, joysticks[pos]);
pos++;
if (joysticks[pos])
SetMaxHotChange_Bits(frame, 2, joysticks[pos]);
pos++;
if (joysticks[pos])
SetMaxHotChange_Bits(frame, 3, joysticks[pos]);
pos++;
}
}
break;
}
case NORMAL_2JOYPADS:
{
for (int frame = start, pos = start * bytes_per_frame[input_type]; frame <= end; ++frame)
{
// set changed if found different byte, or found emptiness in inp when there's non-zero value here
if (frame < inp_end)
{
if (joysticks[pos] != inp.joysticks[pos])
SetMaxHotChange_Bits(frame, 0, joysticks[pos] ^ inp.joysticks[pos]);
pos++;
if (joysticks[pos] != inp.joysticks[pos])
SetMaxHotChange_Bits(frame, 1, joysticks[pos] ^ inp.joysticks[pos]);
pos++;
} else
{
if (joysticks[pos])
SetMaxHotChange_Bits(frame, 0, joysticks[pos]);
pos++;
if (joysticks[pos])
SetMaxHotChange_Bits(frame, 1, joysticks[pos]);
pos++;
}
}
break;
}
}
}
void INPUT_SNAPSHOT::SetMaxHotChange_Bits(int frame, int joypad, uint8 joy_bits)
{
uint8 mask = 1;
// check all 8 buttons and set max hot_changes for bits that are set
for (int i = 0; i < 8; ++i)
{
if (joy_bits & mask)
SetMaxHotChange(frame, joypad * 8 + i);
mask <<= 1;
}
}
void INPUT_SNAPSHOT::SetMaxHotChange(int frame, int absolute_button)
{
if (frame < 0 || frame >= size || !has_hot_changes) return;
@ -444,10 +648,10 @@ void INPUT_SNAPSHOT::SetMaxHotChange(int frame, int absolute_button)
// 32 buttons = 16bytes
if (absolute_button & 1)
// odd buttons (B, T, D, R) - set upper 4 bits of the byte
hot_changes[(frame << 4) | (absolute_button >> 1)] &= 0xF0;
hot_changes[(frame << 4) | (absolute_button >> 1)] |= 0xF0;
else
// even buttons (A, S, U, L) - set lower 4 bits of the byte
hot_changes[(frame << 4) | (absolute_button >> 1)] &= 0x0F;
hot_changes[(frame << 4) | (absolute_button >> 1)] |= 0x0F;
break;
}
case NORMAL_2JOYPADS:
@ -455,18 +659,35 @@ void INPUT_SNAPSHOT::SetMaxHotChange(int frame, int absolute_button)
// 16 buttons = 8bytes
if (absolute_button & 1)
// odd buttons (B, T, D, R) - set upper 4 bits of the byte
hot_changes[(frame << 3) | (absolute_button >> 1)] &= 0xF0;
hot_changes[(frame << 3) | (absolute_button >> 1)] |= 0xF0;
else
// even buttons (A, S, U, L) - set lower 4 bits of the byte
hot_changes[(frame << 3) | (absolute_button >> 1)] &= 0x0F;
hot_changes[(frame << 3) | (absolute_button >> 1)] |= 0x0F;
break;
}
}
}
void INPUT_SNAPSHOT::FadeHotChanges()
{
uint8 hi_half, low_half;
for (int i = hot_changes.size() - 1; i >= 0; i--)
{
if (hot_changes[i])
{
hi_half = hot_changes[i] >> 4;
low_half = hot_changes[i] & 15;
if (hi_half) hi_half--;
if (low_half) low_half--;
hot_changes[i] = (hi_half << 4) | low_half;
}
}
}
int INPUT_SNAPSHOT::GetHotChangeInfo(int frame, int absolute_button)
{
if (frame < 0 || frame >= size || !has_hot_changes) return 0;
if (absolute_button < 0 || absolute_button > 31) return 0;
if (!has_hot_changes || frame < 0 || frame >= size || absolute_button < 0 || absolute_button > 31)
return 0;
uint8 val;
switch(input_type)
@ -493,4 +714,3 @@ int INPUT_SNAPSHOT::GetHotChangeInfo(int frame, int absolute_button)
return val & 15;
}

View File

@ -33,11 +33,21 @@ public:
int findFirstChange(INPUT_SNAPSHOT& inp, int start = 0, int end = -1);
int findFirstChange(MovieData& md, int start = 0, int end = -1);
void copyHotChanges(INPUT_SNAPSHOT* source_of_hotchanges, int limit_frame_of_source = -1);
void inheritHotChanges(INPUT_SNAPSHOT* source_of_hotchanges);
void inheritHotChanges_DeleteSelection(INPUT_SNAPSHOT* source_of_hotchanges);
void inheritHotChanges_InsertSelection(INPUT_SNAPSHOT* source_of_hotchanges);
void fillHotChanges(INPUT_SNAPSHOT& inp, int start = 0, int end = -1);
void SetMaxHotChange_Bits(int frame, int joypad, uint8 joy_bits);
void SetMaxHotChange(int frame, int absolute_button);
void FadeHotChanges();
int GetHotChangeInfo(int frame, int absolute_button);
int size; // in frames
int input_type; // 0=normal, 1=fourscore, in future may support other input types
int input_type; // 0=normal, 1=fourscore; theoretically TASEdit can support other input types, although some stuff may be unintentionally hardcoded
std::vector<uint8> joysticks; // Format: joy0-for-frame0, joy1-for-frame0, joy2-for-frame0, joy3-for-frame0, joy0-for-frame1, joy1-for-frame1, ...
std::vector<uint8> commands; // Format: commands-for-frame0, commands-for-frame1, ...
std::vector<uint8> hot_changes; // Format: buttons01joy0-for-frame0, buttons23joy0-for-frame0, buttons45joy0-for-frame0, buttons67joy0-for-frame0, buttons01joy1-for-frame0, ...
@ -46,6 +56,7 @@ public:
bool coherent; // indicates whether this state was made right after previous state
int jump_frame; // for jumping when making undo
char description[SNAPSHOT_DESC_MAX_LENGTH];
bool has_hot_changes;
private:
void compress_data();
@ -56,6 +67,5 @@ private:
std::vector<uint8> hot_changes_compressed;
std::vector<uint8> markers_array_compressed;
bool has_hot_changes;
};

View File

@ -0,0 +1,386 @@
//Implementation file of TASEDIT_SELECTION class
#include "../common.h"
#include "taseditproj.h"
//#include "../tasedit.h"
char selection_save_id[SELECTION_ID_LEN] = "SELECTION";
extern MARKERS markers;
extern HWND hwndList;
extern void RedrawList();
TASEDIT_SELECTION::TASEDIT_SELECTION()
{
}
void TASEDIT_SELECTION::init(int new_size)
{
// init vars
if (new_size > 0)
history_size = new_size + 1;
// clear selections history
free();
selections_history.resize(history_size);
history_start_pos = 0;
history_cursor_pos = -1;
// create initial selection
AddNewSelectionToHistory();
track_selection_changes = true;
}
void TASEDIT_SELECTION::free()
{
// clear history
selections_history.resize(0);
history_total_items = 0;
clipboard_selection.clear();
}
void TASEDIT_SELECTION::update()
{
// keep selection within list limits
if (CurrentSelection().size())
{
int delete_index;
int movie_size = currMovieData.getNumRecords();
while(1)
{
delete_index = *CurrentSelection().rbegin();
if (delete_index < movie_size) break;
CurrentSelection().erase(delete_index);
if (!CurrentSelection().size()) break;
}
}
}
bool TASEDIT_SELECTION::CheckFrameSelected(int frame)
{
if(CurrentSelection().find(frame) == CurrentSelection().end())
return false;
return true;
}
void TASEDIT_SELECTION::save(EMUFILE *os)
{
// write "SELECTION" string
os->fwrite(selection_save_id, SELECTION_ID_LEN);
// write vars
write32le(history_cursor_pos, os);
write32le(history_total_items, os);
// write selections starting from history_start_pos
for (int i = 0; i < history_total_items; ++i)
{
saveSelection(selections_history[(history_start_pos + i) % history_size], os);
}
// write clipboard_selection
saveSelection(clipboard_selection, os);
}
// returns true if couldn't load
bool TASEDIT_SELECTION::load(EMUFILE *is)
{
// read "SELECTION" string
char save_id[SELECTION_ID_LEN];
if ((int)is->fread(save_id, SELECTION_ID_LEN) < SELECTION_ID_LEN) goto error;
if (strcmp(selection_save_id, save_id)) goto error; // string is not valid
// read vars
if (!read32le(&history_cursor_pos, is)) goto error;
if (!read32le(&history_total_items, is)) goto error;
if (history_cursor_pos > history_total_items) goto error;
history_start_pos = 0;
// read selections
int i;
int total = history_total_items;
if (history_total_items > history_size)
{
// user can't afford that much undo levels, skip some selections
int num_selections_to_skip = history_total_items - history_size;
// first try to skip selections over history_cursor_pos (future selections), because "redo" is less important than "undo"
int num_redo_selections = history_total_items-1 - history_cursor_pos;
if (num_selections_to_skip >= num_redo_selections)
{
// skip all redo selections
history_total_items = history_cursor_pos+1;
num_selections_to_skip -= num_redo_selections;
// and still need to skip some undo selections
for (i = 0; i < num_selections_to_skip; ++i)
if (skiploadSelection(is)) goto error;
total -= num_selections_to_skip;
history_cursor_pos -= num_selections_to_skip;
}
history_total_items -= num_selections_to_skip;
}
// load selections
for (i = 0; i < history_total_items; ++i)
{
if (loadSelection(selections_history[i], is)) goto error;
}
// skip redo selections if needed
for (; i < total; ++i)
if (skiploadSelection(is)) goto error;
// read clipboard_selection
if (loadSelection(clipboard_selection, is)) goto error;
// all ok
EnforceSelectionToList();
return false;
error:
return true;
}
void TASEDIT_SELECTION::saveSelection(SelectionFrames& selection, EMUFILE *os)
{
write32le(selection.size(), os);
if (selection.size())
{
for(SelectionFrames::iterator it(selection.begin()); it != selection.end(); it++)
write32le(*it, os);
}
}
bool TASEDIT_SELECTION::loadSelection(SelectionFrames& selection, EMUFILE *is)
{
int temp_int, temp_size;
selection.clear();
if (!read32le(&temp_size, is)) return true;
selection.clear();
for(; temp_size > 0; temp_size--)
{
if (!read32le(&temp_int, is)) return true;
selection.insert(temp_int);
}
return false;
}
bool TASEDIT_SELECTION::skiploadSelection(EMUFILE *is)
{
int temp_int, temp_size;
if (!read32le(&temp_size, is)) return true;
for(; temp_size > 0; temp_size--)
if (!read32le(&temp_int, is)) return true;
return false;
}
// ----------------------------------------------------------
//used to track selection
void TASEDIT_SELECTION::ItemRangeChanged(NMLVODSTATECHANGE* info)
{
bool ON = !(info->uOldState & LVIS_SELECTED) && (info->uNewState & LVIS_SELECTED);
bool OFF = (info->uOldState & LVIS_SELECTED) && !(info->uNewState & LVIS_SELECTED);
if(ON)
for(int i = info->iFrom; i <= info->iTo; ++i)
CurrentSelection().insert(i);
else
for(int i = info->iFrom; i <= info->iTo; ++i)
CurrentSelection().erase(i);
}
void TASEDIT_SELECTION::ItemChanged(NMLISTVIEW* info)
{
int item = info->iItem;
bool ON = !(info->uOldState & LVIS_SELECTED) && (info->uNewState & LVIS_SELECTED);
bool OFF = (info->uOldState & LVIS_SELECTED) && !(info->uNewState & LVIS_SELECTED);
//if the item is -1, apply the change to all items
if(item == -1)
{
if(OFF)
{
// clear all (actually add new empty selection to history)
if (CurrentSelection().size() && track_selection_changes)
AddNewSelectionToHistory();
} else if (ON)
{
// select all
for(int i = currMovieData.getNumRecords() - 1; i >= 0; i--)
{
CurrentSelection().insert(i);
}
}
} else
{
if(ON)
CurrentSelection().insert(item);
else if(OFF)
CurrentSelection().erase(item);
}
}
// ----------------------------------------------------------
void TASEDIT_SELECTION::AddNewSelectionToHistory()
{
// create new empty selection
SelectionFrames selectionFrames;
// increase current position
// history uses conveyor of selections (vector with fixed size) to avoid resizing
if (history_cursor_pos+1 >= history_size)
{
// reached the end of available history_size - move history_start_pos (thus deleting oldest selection)
history_cursor_pos = history_size-1;
history_start_pos = (history_start_pos + 1) % history_size;
} else
{
// didn't reach the end of history yet
history_cursor_pos++;
if (history_cursor_pos >= history_total_items)
history_total_items = history_cursor_pos+1;
}
// add
selections_history[(history_start_pos + history_cursor_pos) % history_size] = selectionFrames;
}
SelectionFrames& TASEDIT_SELECTION::CurrentSelection()
{
return selections_history[(history_start_pos + history_cursor_pos) % history_size];
}
void TASEDIT_SELECTION::jump(int new_pos)
{
if (new_pos < 0) new_pos = 0; else if (new_pos >= history_total_items) new_pos = history_total_items-1;
if (new_pos == history_cursor_pos) return;
// make jump
history_cursor_pos = new_pos;
// update list items
EnforceSelectionToList();
// also keep selection within list
update();
}
void TASEDIT_SELECTION::undo()
{
jump(history_cursor_pos - 1);
}
void TASEDIT_SELECTION::redo()
{
jump(history_cursor_pos + 1);
}
void TASEDIT_SELECTION::MemorizeClipboardSelection()
{
// copy current selection data to clipboard_selection
clipboard_selection = CurrentSelection();
}
void TASEDIT_SELECTION::ReselectClipboard()
{
if (clipboard_selection.size() == 0) return;
ClearSelection();
CurrentSelection() = clipboard_selection;
EnforceSelectionToList();
// also keep selection within list
update();
}
// ----------------------------------------------------------
void TASEDIT_SELECTION::ClearSelection()
{
ListView_SetItemState(hwndList, -1, 0, LVIS_FOCUSED|LVIS_SELECTED);
}
void TASEDIT_SELECTION::ClearRowSelection(int index)
{
ListView_SetItemState(hwndList, index, 0, LVIS_SELECTED);
}
void TASEDIT_SELECTION::EnforceSelectionToList()
{
track_selection_changes = false;
ClearSelection();
for(SelectionFrames::reverse_iterator it(CurrentSelection().rbegin()); it != CurrentSelection().rend(); it++)
{
ListView_SetItemState(hwndList, *it, LVIS_SELECTED, LVIS_SELECTED);
}
track_selection_changes = true;
}
void TASEDIT_SELECTION::SelectAll()
{
ListView_SetItemState(hwndList, -1, LVIS_SELECTED, LVIS_SELECTED);
//RedrawList();
}
void TASEDIT_SELECTION::SetRowSelection(int index)
{
ListView_SetItemState(hwndList, index, LVIS_SELECTED, LVIS_SELECTED);
}
void TASEDIT_SELECTION::SetRegionSelection(int start, int end)
{
for (int i = start; i <= end; ++i)
ListView_SetItemState(hwndList, i, LVIS_SELECTED, LVIS_SELECTED);
}
void TASEDIT_SELECTION::SelectMidMarkers()
{
int center, upper_border, lower_border;
int upper_marker, lower_marker;
int movie_size = currMovieData.getNumRecords();
// if selection size=0 then playback cursor is selected and serves as center
if (CurrentSelection().size())
{
upper_border = center = *CurrentSelection().begin();
lower_border = *CurrentSelection().rbegin();
} else lower_border = upper_border = center = currFrameCounter;
// find markers
// searching up starting from center-0
for (upper_marker = center; upper_marker >= 0; upper_marker--)
if (markers.markers_array[upper_marker] & MARKER_FLAG_BIT) break;
// searching down starting from center+1
for (lower_marker = center+1; lower_marker < movie_size; ++lower_marker)
if (markers.markers_array[lower_marker] & MARKER_FLAG_BIT) break;
// clear selection without clearing focused, because otherwise there's strange bug when quickly pressing Ctrl+A right after clicking on already selected row
ListView_SetItemState(hwndList, -1, 0, LVIS_SELECTED);
// special case
if (upper_marker == -1 && lower_marker == movie_size)
{
SelectAll();
return;
}
// selecting circle:
if (upper_border > upper_marker+1 || lower_border < lower_marker-1 || lower_border > lower_marker)
{
// default: select all between markers
for (int i = upper_marker+1; i < lower_marker; ++i)
{
ListView_SetItemState(hwndList, i, LVIS_SELECTED, LVIS_SELECTED);
}
} else if (upper_border == upper_marker+1 && lower_border == lower_marker-1)
{
// already selected all between markers - now select both markers or at least select the marker that is not outside movie range
if (upper_marker < 0) upper_marker = 0;
if (lower_marker >= movie_size) lower_marker = movie_size - 1;
for (int i = upper_marker; i <= lower_marker; ++i)
{
ListView_SetItemState(hwndList, i, LVIS_SELECTED, LVIS_SELECTED);
}
} else if (upper_border <= upper_marker && lower_border >= lower_marker)
{
// selected all between markers and both markers selected too - now deselect lower marker
for (int i = upper_marker; i < lower_marker; ++i)
{
ListView_SetItemState(hwndList, i, LVIS_SELECTED, LVIS_SELECTED);
}
} else if (upper_border == upper_marker && lower_border == lower_marker-1)
{
// selected all between markers and upper marker selected too - now deselect upper marker and (if lower marker < movie_size) reselect lower marker
if (lower_marker >= movie_size) lower_marker = movie_size - 1;
for (int i = upper_marker+1; i <= lower_marker; ++i)
{
ListView_SetItemState(hwndList, i, LVIS_SELECTED, LVIS_SELECTED);
}
} else if (upper_border == upper_marker+1 && lower_border == lower_marker)
{
// selected all between markers and lower marker selected too - now deselect lower marker (return to "selected all between markers")
for (int i = upper_marker + 1; i < lower_marker; ++i)
{
ListView_SetItemState(hwndList, i, LVIS_SELECTED, LVIS_SELECTED);
}
}
}

View File

@ -0,0 +1,57 @@
//Specification file for TASEDIT_SELECTION class
#define SELECTION_ID_LEN 10
class TASEDIT_SELECTION
{
public:
TASEDIT_SELECTION();
void init(int new_size = 0);
void free();
void update();
void save(EMUFILE *os);
bool load(EMUFILE *is);
void saveSelection(SelectionFrames& selection, EMUFILE *os);
bool loadSelection(SelectionFrames& selection, EMUFILE *is);
bool skiploadSelection(EMUFILE *is);
void ItemRangeChanged(NMLVODSTATECHANGE* info);
void ItemChanged(NMLISTVIEW* info);
void AddNewSelectionToHistory();
SelectionFrames& CurrentSelection();
void undo();
void redo();
void jump(int new_pos);
void MemorizeClipboardSelection();
void ReselectClipboard();
void ClearSelection();
void ClearRowSelection(int index);
void EnforceSelectionToList();
void SelectAll();
void SetRowSelection(int index);
void SetRegionSelection(int start, int end);
void SelectMidMarkers();
bool CheckFrameSelected(int frame);
private:
bool track_selection_changes;
std::vector<SelectionFrames> selections_history;
SelectionFrames clipboard_selection;
int history_cursor_pos;
int history_start_pos;
int history_total_items;
int history_size;
};

View File

@ -9,6 +9,7 @@ extern BOOKMARKS bookmarks;
extern GREENZONE greenzone;
extern PLAYBACK playback;
extern INPUT_HISTORY history;
extern TASEDIT_SELECTION selection;
extern void FCEU_printf(char *format, ...);
extern int TASEdit_autosave_period;
@ -57,6 +58,7 @@ bool TASEDIT_PROJECT::saveProject()
bookmarks.save(ofs);
greenzone.save(ofs);
history.save(ofs);
selection.save(ofs);
delete ofs;
@ -79,6 +81,7 @@ bool TASEDIT_PROJECT::LoadProject(std::string PFN)
bool error;
LoadFM2(currMovieData, &ifs, ifs.size(), false);
LoadSubtitles(currMovieData);
UpdateList();
// try to load markers
error = markers.load(&ifs);
if (error)
@ -113,6 +116,15 @@ bool TASEDIT_PROJECT::LoadProject(std::string PFN)
{
FCEU_printf("Error loading history\n");
history.init();
} else
{
// try to load selection
error = selection.load(&ifs);
}
if (error)
{
FCEU_printf("Error loading selection\n");
selection.init();
}
reset();

View File

@ -1,12 +1,17 @@
//Specification file for the TASEdit Project class
#include <set>
typedef std::set<int> SelectionFrames;
#include "movie.h"
#include "../common.h"
#include "inputsnapshot.h"
#include "inputhistory.h"
#include "playback.h"
#include "greenzone.h"
#include "markers.h"
#include "bookmarks.h"
#include "tasedit_sel.h"
class TASEDIT_PROJECT
{

View File

@ -426,6 +426,7 @@
<ClCompile Include="..\src\drivers\win\taseditlib\inputsnapshot.cpp" />
<ClCompile Include="..\src\drivers\win\taseditlib\markers.cpp" />
<ClCompile Include="..\src\drivers\win\taseditlib\playback.cpp" />
<ClCompile Include="..\src\drivers\win\taseditlib\tasedit_sel.cpp" />
<ClCompile Include="..\src\drivers\win\texthook.cpp" />
<ClCompile Include="..\src\drivers\win\throttle.cpp" />
<ClCompile Include="..\src\drivers\win\timing.cpp" />
@ -738,6 +739,7 @@
<ClInclude Include="..\src\drivers\win\state.h" />
<ClInclude Include="..\src\drivers\win\tasedit.h" />
<ClInclude Include="..\src\drivers\win\taseditlib\inputhistory.h" />
<ClInclude Include="..\src\drivers\win\taseditlib\tasedit_sel.h" />
<ClInclude Include="..\src\drivers\win\texthook.h" />
<ClInclude Include="..\src\drivers\win\throttle.h" />
<ClInclude Include="..\src\drivers\win\timing.h" />

View File

@ -928,6 +928,7 @@
<ClCompile Include="..\src\drivers\win\taseditlib\playback.cpp">
<Filter>drivers\win\taseditlib</Filter>
</ClCompile>
<ClCompile Include="..\src\drivers\win\taseditlib\tasedit_sel.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\src\cart.h">
@ -1375,6 +1376,7 @@
<ClInclude Include="..\src\drivers\win\taseditlib\inputhistory.h">
<Filter>drivers\win\taseditlib</Filter>
</ClInclude>
<ClInclude Include="..\src\drivers\win\taseditlib\tasedit_sel.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\src\drivers\win\res.rc">