* 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_follow_playback;
extern bool TASEdit_show_lag_frames; extern bool TASEdit_show_lag_frames;
extern bool TASEdit_show_markers; extern bool TASEdit_show_markers;
extern bool TASEdit_show_branch_screenshots;
extern bool TASEdit_bind_markers; extern bool TASEdit_bind_markers;
extern bool TASEdit_branch_full_movie; extern bool TASEdit_branch_full_movie;
extern bool TASEdit_branch_only_when_rec; extern bool TASEdit_branch_only_when_rec;
extern bool TASEdit_view_branches_tree; extern bool TASEdit_view_branches_tree;
extern bool TASEdit_branch_scr_hud; extern bool TASEdit_branch_scr_hud;
extern bool TASEdit_restore_position; extern bool TASEdit_restore_position;
extern bool TASEdit_show_dot; extern bool TASEdit_enable_hot_changes;
extern int TASEdit_greenzone_capacity; extern int TASEdit_greenzone_capacity;
extern int TasEdit_undo_levels; extern int TasEdit_undo_levels;
extern int TASEdit_autosave_period; extern int TASEdit_autosave_period;
@ -302,13 +303,14 @@ static CFGSTRUCT fceuconfig[] = {
AC(TASEdit_follow_playback), AC(TASEdit_follow_playback),
AC(TASEdit_show_lag_frames), AC(TASEdit_show_lag_frames),
AC(TASEdit_show_markers), AC(TASEdit_show_markers),
AC(TASEdit_show_branch_screenshots),
AC(TASEdit_bind_markers), AC(TASEdit_bind_markers),
AC(TASEdit_branch_full_movie), AC(TASEdit_branch_full_movie),
AC(TASEdit_branch_only_when_rec), AC(TASEdit_branch_only_when_rec),
AC(TASEdit_view_branches_tree), AC(TASEdit_view_branches_tree),
AC(TASEdit_branch_scr_hud), AC(TASEdit_branch_scr_hud),
AC(TASEdit_restore_position), AC(TASEdit_restore_position),
AC(TASEdit_show_dot), AC(TASEdit_enable_hot_changes),
AC(TASEdit_greenzone_capacity), AC(TASEdit_greenzone_capacity),
AC(TasEdit_undo_levels), AC(TasEdit_undo_levels),
AC(TASEdit_autosave_period), AC(TASEdit_autosave_period),

View File

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

View File

@ -893,6 +893,14 @@
#define ID_CONFIG_BRANCHESWORKONLYWHENRECORDING 40474 #define ID_CONFIG_BRANCHESWORKONLYWHENRECORDING 40474
#define ID_CONFIG_HUDINBRANCHSCREENSHOTS 40475 #define ID_CONFIG_HUDINBRANCHSCREENSHOTS 40475
#define ID_CONFIG_SETAUTOSAVEPERIOD 40476 #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 IDC_DEBUGGER_ICONTRAY 55535
#define MW_ValueLabel2 65423 #define MW_ValueLabel2 65423
#define MW_ValueLabel1 65426 #define MW_ValueLabel1 65426
@ -902,7 +910,7 @@
#ifdef APSTUDIO_INVOKED #ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS #ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 185 #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_CONTROL_VALUE 1267
#define _APS_NEXT_SYMED_VALUE 101 #define _APS_NEXT_SYMED_VALUE 101
#endif #endif

View File

@ -2,7 +2,6 @@
#include <fstream> #include <fstream>
#include <sstream> #include <sstream>
#include "common.h"
#include "taseditlib/taseditproj.h" #include "taseditlib/taseditproj.h"
#include "fceu.h" #include "fceu.h"
#include "debugger.h" #include "debugger.h"
@ -18,24 +17,26 @@
using namespace std; 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; int old_multitrack_recording_joypad, multitrack_recording_joypad;
bool old_movie_readonly; bool old_movie_readonly;
bool TASEdit_focus = false; bool TASEdit_focus = false;
bool Tasedit_rewind_now = false; bool Tasedit_rewind_now = false;
int listItems; // number of items per list page int listItems; // number of items per list page
// saved FCEU config // saved FCEU config
int saved_eoptions; int saved_eoptions;
int saved_EnableAutosave; int saved_EnableAutosave;
extern int 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 // vars saved in cfg file
int TasEdit_wndx, TasEdit_wndy; int TasEdit_wndx, TasEdit_wndy;
bool TASEdit_follow_playback = true; bool TASEdit_follow_playback = true;
bool TASEdit_show_lag_frames = true; bool TASEdit_show_lag_frames = true;
bool TASEdit_show_markers = true; bool TASEdit_show_markers = true;
bool TASEdit_show_branch_screenshots = true;
bool TASEdit_bind_markers = true; bool TASEdit_bind_markers = true;
bool TASEdit_branch_full_movie = true; bool TASEdit_branch_full_movie = true;
bool TASEdit_branch_only_when_rec = false; 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_undo_levels = UNDO_LEVELS_DEFAULT;
int TASEdit_autosave_period = AUTOSAVE_PERIOD_DEFAULT; int TASEdit_autosave_period = AUTOSAVE_PERIOD_DEFAULT;
extern bool muteTurbo; extern bool muteTurbo;
bool TASEdit_show_dot = true; bool TASEdit_enable_hot_changes = true;
bool TASEdit_jump_to_undo = true; bool TASEdit_jump_to_undo = true;
// resources
string tasedithelp = "{16CDE0C4-02B0-4A60-A88D-076319909A4D}"; //Name of TASEdit Help page 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 buttonNames[NUM_JOYPAD_BUTTONS][2] = {"A", "B", "S", "T", "U", "D", "L", "R"};
char windowCaptions[6][30] = { "TAS Editor", char windowCaptions[6][30] = { "TAS Editor",
@ -57,8 +59,7 @@ char windowCaptions[6][30] = { "TAS Editor",
"TAS Editor (Recording 2P)", "TAS Editor (Recording 2P)",
"TAS Editor (Recording 3P)", "TAS Editor (Recording 3P)",
"TAS Editor (Recording 4P)"}; "TAS Editor (Recording 4P)"};
// hot changes color table COLORREF hot_changes_colors[16] = { 0x0, 0x661212, 0x842B4E, 0x652C73, 0x48247D, 0x383596, 0x2947AE, 0x1E53C1, 0x135DD2, 0x116EDA, 0x107EE3, 0x0F8EEB, 0x209FF4, 0x3DB1FD, 0x51C2FF, 0x4DCDFF };
COLORREF hot_changes_colors[16] = { 0x0, 0x41f2c, 0x62a3b, 0x7344a, 0x93f59, 0xb4968, 0xc5477, 0xe5e86, 0xf6995, 0x1174a4, 0x127eb3, 0x1489c2, 0x1693d1, 0x179ee0, 0x19a8ef, 0x1bb4ff };
HWND hwndTasEdit = 0; HWND hwndTasEdit = 0;
HMENU hmenu, hrmenu; HMENU hmenu, hrmenu;
@ -73,14 +74,7 @@ HWND hwndRB_RecOff, hwndRB_RecAll, hwndRB_Rec1P, hwndRB_Rec2P, hwndRB_Rec3P, hwn
HWND hwndBranchesBitmap; HWND hwndBranchesBitmap;
WNDPROC hwndBranchesBitmap_oldWndProc; WNDPROC hwndBranchesBitmap_oldWndProc;
HFONT hMainListFont; HFONT hMainListFont, hMainListSelectFont;
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;
// all Taseditor functional modules // all Taseditor functional modules
TASEDIT_PROJECT project; TASEDIT_PROJECT project;
@ -89,6 +83,7 @@ PLAYBACK playback;
GREENZONE greenzone; GREENZONE greenzone;
MARKERS markers; MARKERS markers;
BOOKMARKS bookmarks; BOOKMARKS bookmarks;
TASEDIT_SELECTION selection;
void GetDispInfo(NMLVDISPINFO* nmlvDispInfo) void GetDispInfo(NMLVDISPINFO* nmlvDispInfo)
{ {
@ -113,7 +108,7 @@ void GetDispInfo(NMLVDISPINFO* nmlvDispInfo)
case COLUMN_FRAMENUM: case COLUMN_FRAMENUM:
case COLUMN_FRAMENUM2: case COLUMN_FRAMENUM2:
{ {
U32ToDecStr(item.pszText,item.iItem,DIGITS_IN_FRAMENUM); U32ToDecStr(item.pszText, item.iItem, DIGITS_IN_FRAMENUM);
break; break;
} }
case COLUMN_JOYPAD1_A: case COLUMN_JOYPAD1_B: case COLUMN_JOYPAD1_S: case COLUMN_JOYPAD1_T: 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]; uint8 data = currMovieData.records[item.iItem].joysticks[joy];
if(data & (1<<bit)) if(data & (1<<bit))
{ {
item.pszText[0] = MovieRecord::mnemonics[bit]; item.pszText[0] = buttonNames[bit][0];
item.pszText[1] = 0; item.pszText[2] = 0;
} else } 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; item.pszText[1] = 0;
} else item.pszText[0] = 0; } else item.pszText[0] = 0;
} }
@ -159,11 +154,17 @@ LONG CustomDraw(NMLVCUSTOMDRAW* msg)
cell_x = msg->iSubItem; cell_x = msg->iSubItem;
cell_y = msg->nmcd.dwItemSpec; cell_y = msg->nmcd.dwItemSpec;
//msg->clrText = 0xFF0000;
if(cell_x > COLUMN_ICONS) if(cell_x > COLUMN_ICONS)
{ {
SelectObject(msg->nmcd.hdc, hMainListFont); 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) if(cell_x == COLUMN_FRAMENUM || cell_x == COLUMN_FRAMENUM2)
{ {
// frame number // frame number
@ -277,13 +278,15 @@ void UpdateTasEdit()
{ {
if(!hwndTasEdit) return; if(!hwndTasEdit) return;
UpdateList(); // also markers are updated there UpdateList();
markers.update();
greenzone.update(); greenzone.update();
playback.update(); playback.update();
bookmarks.update(); bookmarks.update();
selection.update();
history.update(); history.update();
project.update(); project.update();
selection.update();
// update window caption // update window caption
if (old_movie_readonly != movie_readonly || old_multitrack_recording_joypad != multitrack_recording_joypad) if (old_movie_readonly != movie_readonly || old_multitrack_recording_joypad != multitrack_recording_joypad)
@ -304,28 +307,11 @@ void UpdateTasEdit()
void UpdateList() void UpdateList()
{ {
// first update number of items in markers array
markers.update();
//update the number of items in the list //update the number of items in the list
int currLVItemCount = ListView_GetItemCount(hwndList); int currLVItemCount = ListView_GetItemCount(hwndList);
int movie_size = currMovieData.getNumRecords(); int movie_size = currMovieData.getNumRecords();
if(currLVItemCount != movie_size) if(currLVItemCount != movie_size)
{
ListView_SetItemCountEx(hwndList,movie_size,LVSICF_NOSCROLL | LVSICF_NOINVALIDATEALL); 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() void RedrawWindowCaption()
@ -370,12 +356,6 @@ void RedrawRowAndBookmark(int index)
bookmarks.RedrawChangedBookmarks(index); bookmarks.RedrawChangedBookmarks(index);
} }
enum ECONTEXTMENU
{
CONTEXTMENU_STRAY = 0,
CONTEXTMENU_SELECTED = 1,
};
void ShowMenu(ECONTEXTMENU which, POINT& pt) void ShowMenu(ECONTEXTMENU which, POINT& pt)
{ {
HMENU sub = GetSubMenu(hrmenu,(int)which); HMENU sub = GetSubMenu(hrmenu,(int)which);
@ -408,14 +388,8 @@ void RightClick(LPNMITEMACTIVATE info)
return; return;
} }
//make sure that the click is in our currently selected set. if (selection.CheckFrameSelected(index))
//if it is not, then we don't know what to do yet RightClickMenu(info);
if(selectionFrames.find(index) == selectionFrames.end())
{
return;
}
RightClickMenu(info);
} }
void InputChangedRec() void InputChangedRec()
@ -430,11 +404,11 @@ void ToggleJoypadBit(int column_index, int row_index, UINT KeyFlags)
if (KeyFlags & (LVKF_SHIFT|LVKF_CONTROL)) if (KeyFlags & (LVKF_SHIFT|LVKF_CONTROL))
{ {
//update multiple rows //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); 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 } else
{ {
//update one row //update one row
@ -456,7 +430,7 @@ void SingleClick(LPNMITEMACTIVATE info)
if(column_index == COLUMN_ICONS) if(column_index == COLUMN_ICONS)
{ {
// click on the "icons" column - jump to the frame // click on the "icons" column - jump to the frame
ClearSelection(); selection.ClearSelection();
playback.jump(row_index); playback.jump(row_index);
} else if(column_index == COLUMN_FRAMENUM || column_index == COLUMN_FRAMENUM2) } 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) if(column_index == COLUMN_ICONS || column_index == COLUMN_FRAMENUM || column_index == COLUMN_FRAMENUM2)
{ {
// double click sends playback to the frame // double click sends playback to the frame
ClearSelection(); selection.ClearSelection();
playback.jump(row_index); playback.jump(row_index);
} else if(column_index >= COLUMN_JOYPAD1_A && column_index <= COLUMN_JOYPAD4_R) } else if(column_index >= COLUMN_JOYPAD1_A && column_index <= COLUMN_JOYPAD4_R)
{ {
@ -501,18 +475,19 @@ void DoubleClick(LPNMITEMACTIVATE info)
void CloneFrames() void CloneFrames()
{ {
int frames = selectionFrames.size(); int frames = selection.CurrentSelection().size();
if (!frames) return;
currMovieData.records.reserve(currMovieData.getNumRecords() + frames); currMovieData.records.reserve(currMovieData.getNumRecords() + frames);
//insert frames before each selection, but consecutive selection lines are accounted as single region //insert frames before each selection, but consecutive selection lines are accounted as single region
frames = 1; frames = 1;
TSelectionFrames::reverse_iterator next_it; SelectionFrames::reverse_iterator next_it;
for(TSelectionFrames::reverse_iterator it(selectionFrames.rbegin()); it != selectionFrames.rend(); it++) for(SelectionFrames::reverse_iterator it(selection.CurrentSelection().rbegin()); it != selection.CurrentSelection().rend(); it++)
{ {
next_it = it; next_it = it;
next_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 // end of current region
currMovieData.cloneRegion(*it, frames); currMovieData.cloneRegion(*it, frames);
@ -522,24 +497,25 @@ void CloneFrames()
} else frames++; } else frames++;
} }
UpdateList(); UpdateList();
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_CLONE, *selectionFrames.begin())); greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_CLONE, *selection.CurrentSelection().begin()));
} }
void InsertFrames() 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 //to keep this from being even slower than it would otherwise be, go ahead and reserve records
currMovieData.records.reserve(currMovieData.getNumRecords() + frames); currMovieData.records.reserve(currMovieData.getNumRecords() + frames);
//insert frames before each selection, but consecutive selection lines are accounted as single region //insert frames before each selection, but consecutive selection lines are accounted as single region
frames = 1; frames = 1;
TSelectionFrames::reverse_iterator next_it; SelectionFrames::reverse_iterator next_it;
for(TSelectionFrames::reverse_iterator it(selectionFrames.rbegin()); it != selectionFrames.rend(); it++) for(SelectionFrames::reverse_iterator it(selection.CurrentSelection().rbegin()); it != selection.CurrentSelection().rend(); it++)
{ {
next_it = it; next_it = it;
next_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 // end of current region
currMovieData.insertEmpty(*it,frames); currMovieData.insertEmpty(*it,frames);
@ -549,15 +525,45 @@ void InsertFrames()
} else frames++; } else frames++;
} }
UpdateList(); 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() void DeleteFrames()
{ {
int start_index = *selectionFrames.begin(); if (selection.CurrentSelection().size() == 0) return;
int end_index = *selectionFrames.rbegin(); int start_index = *selection.CurrentSelection().begin();
int end_index = *selection.CurrentSelection().rbegin();
//delete frames on each selection, going backwards //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); currMovieData.records.erase(currMovieData.records.begin() + *it);
if (TASEdit_bind_markers) if (TASEdit_bind_markers)
@ -581,24 +587,25 @@ void DeleteFrames()
void ClearFrames(bool cut) void ClearFrames(bool cut)
{ {
if (selection.CurrentSelection().size() == 0) return;
//clear input on each selection //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(); currMovieData.records[*it].clear();
} }
if (cut) 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 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() void Truncate()
{ {
int frame = currFrameCounter; int frame = currFrameCounter;
if (selectionFrames.size()) if (selection.CurrentSelection().size())
{ {
frame=*selectionFrames.begin(); frame = *selection.CurrentSelection().begin();
ClearSelection(); selection.ClearSelection();
} }
if (currMovieData.getNumRecords() > frame+1) if (currMovieData.getNumRecords() > frame+1)
{ {
@ -625,7 +632,7 @@ void ColumnSet(int column)
// Markers column // Markers column
//inspect the selected frames, if they are all set, then unset all, else set all //inspect the selected frames, if they are all set, then unset all, else set all
bool unset_found = false; 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)) if(!(markers.markers_array[*it] & MARKER_FLAG_BIT))
{ {
@ -636,18 +643,18 @@ void ColumnSet(int column)
if (unset_found) if (unset_found)
{ {
// set all // 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; 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 } else
{ {
// unset all // 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; 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(); project.SetProjectChanged();
ClearSelection(); selection.ClearSelection();
// no need to RedrawList(); // no need to RedrawList();
} else } else
{ {
@ -657,7 +664,7 @@ void ColumnSet(int column)
int button = (column - COLUMN_JOYPAD1_A) % NUM_JOYPAD_BUTTONS; int button = (column - COLUMN_JOYPAD1_A) % NUM_JOYPAD_BUTTONS;
//inspect the selected frames, if they are all set, then unset all, else set all //inspect the selected frames, if they are all set, then unset all, else set all
bool newValue = false; 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))) if(!(currMovieData.records[*it].checkBit(joy,button)))
{ {
@ -666,114 +673,30 @@ void ColumnSet(int column)
} }
} }
// apply newValue // 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); currMovieData.records[*it].setBitValue(joy,button,newValue);
if (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 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() 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 try
{ {
int range = *selectionFrames.rbegin() - *selectionFrames.begin()+1; int range = *selection.CurrentSelection().rbegin() - *selection.CurrentSelection().begin()+1;
//std::string outbuf clipString("TAS"); //std::string outbuf clipString("TAS");
std::stringstream clipString; std::stringstream clipString;
clipString << "TAS " << range << std::endl; 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) if (*it>cframe+1)
{ {
@ -793,7 +716,7 @@ bool Copy()
{ {
if (currMovieData.records[*it].joysticks[joy] & (1<<bit)) if (currMovieData.records[*it].joysticks[joy] & (1<<bit))
{ {
clipString << MovieRecord::mnemonics[bit]; clipString << buttonNames[bit];
} }
} }
} }
@ -823,7 +746,8 @@ bool Copy()
{ {
return false; return false;
} }
// copied successfully
selection.MemorizeClipboardSelection();
return true; return true;
} }
void Cut() void Cut()
@ -836,10 +760,10 @@ void Cut()
bool Paste() bool Paste()
{ {
bool result = false; bool result = false;
if (selectionFrames.size()==0) if (selection.CurrentSelection().size()==0)
return false; return false;
int pos = *selectionFrames.begin(); int pos = *selection.CurrentSelection().begin();
if (!OpenClipboard(hwndTasEdit)) if (!OpenClipboard(hwndTasEdit))
return false; return false;
@ -895,7 +819,7 @@ bool Paste()
default: default:
for (int bit=0; bit<NUM_JOYPAD_BUTTONS; ++bit) 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); currMovieData.records[pos].joysticks[joy]|=(1<<bit);
break; break;
@ -908,7 +832,7 @@ bool Paste()
pGlobal = strchr(pGlobal, '\n'); pGlobal = strchr(pGlobal, '\n');
} }
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_PASTE, *selectionFrames.begin())); greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_PASTE, *selection.CurrentSelection().begin()));
result = true; result = true;
} }
@ -929,7 +853,7 @@ LRESULT APIENTRY HeaderWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam
case WM_LBUTTONDOWN: case WM_LBUTTONDOWN:
case WM_LBUTTONDBLCLK: case WM_LBUTTONDBLCLK:
{ {
if (selectionFrames.size()) if (selection.CurrentSelection().size())
{ {
//perform hit test //perform hit test
HD_HITTESTINFO info; HD_HITTESTINFO info;
@ -1193,7 +1117,6 @@ void OpenProject()
bool last_fourscore = currMovieData.fourscore; bool last_fourscore = currMovieData.fourscore;
// Load project // Load project
project.LoadProject(project.GetProjectFile()); project.LoadProject(project.GetProjectFile());
UpdateList();
// update fourscore status // update fourscore status
if (last_fourscore && !currMovieData.fourscore) if (last_fourscore && !currMovieData.fourscore)
RemoveFourscore(); RemoveFourscore();
@ -1208,9 +1131,6 @@ void OpenProject()
// Saves current project // Saves current project
bool SaveProjectAs() 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 const char TPfilter[]="TASEdit Project (*.tas)\0*.tas\0All Files (*.*)\0*.*\0\0"; //Filetype filter
OPENFILENAME ofn; 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) BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{ {
switch(uMsg) switch(uMsg)
@ -1391,7 +1266,6 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar
TasEdit_wndy = wrect.top; TasEdit_wndy = wrect.top;
WindowBoundsCheckNoResize(TasEdit_wndx,TasEdit_wndy,wrect.right); WindowBoundsCheckNoResize(TasEdit_wndx,TasEdit_wndy,wrect.right);
// also move screenshot bitmap if it's open // also move screenshot bitmap if it's open
extern HWND hwndScrBmp;
if (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); 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); RightClick((LPNMITEMACTIVATE)lParam);
break; break;
case LVN_ITEMCHANGED: case LVN_ITEMCHANGED:
ItemChanged((LPNMLISTVIEW) lParam); selection.ItemChanged((LPNMLISTVIEW) lParam);
break; break;
case LVN_ODSTATECHANGED: case LVN_ODSTATECHANGED:
ItemRangeChanged((LPNMLVODSTATECHANGE) lParam); selection.ItemRangeChanged((LPNMLVODSTATECHANGE) lParam);
break; break;
/* /*
case LVN_ENDSCROLL: case LVN_ENDSCROLL:
@ -1518,7 +1392,7 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar
ExitTasEdit(); ExitTasEdit();
break; break;
case ID_EDIT_SELECTALL: case ID_EDIT_SELECTALL:
SelectAll(); selection.SelectAll();
break; break;
case ACCEL_CTRL_X: case ACCEL_CTRL_X:
case ID_TASEDIT_CUT: case ID_TASEDIT_CUT:
@ -1535,7 +1409,7 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar
case ACCEL_CTRL_DELETE: case ACCEL_CTRL_DELETE:
case ID_TASEDIT_DELETE: case ID_TASEDIT_DELETE:
case ID_CONTEXT_SELECTED_DELETEFRAMES: case ID_CONTEXT_SELECTED_DELETEFRAMES:
if (selectionFrames.size()) DeleteFrames(); DeleteFrames();
break; break;
case ACCEL_CTRL_T: case ACCEL_CTRL_T:
case ID_EDIT_TRUNCATE: case ID_EDIT_TRUNCATE:
@ -1551,41 +1425,17 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar
case ID_EDIT_INSERT: case ID_EDIT_INSERT:
case MENU_CONTEXT_STRAY_INSERTFRAMES: case MENU_CONTEXT_STRAY_INSERTFRAMES:
case ID_CONTEXT_SELECTED_INSERTFRAMES2: case ID_CONTEXT_SELECTED_INSERTFRAMES2:
{ InsertNumFrames();
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));
}
}
}
}
break; break;
case ACCEL_CTRL_INSERT: case ACCEL_CTRL_INSERT:
case ID_EDIT_INSERTFRAMES: case ID_EDIT_INSERTFRAMES:
case ID_CONTEXT_SELECTED_INSERTFRAMES: case ID_CONTEXT_SELECTED_INSERTFRAMES:
if (selectionFrames.size()) InsertFrames(); InsertFrames();
break; break;
case ACCEL_DEL: case ACCEL_DEL:
case ID_EDIT_CLEAR: case ID_EDIT_CLEAR:
case ID_CONTEXT_SELECTED_CLEARFRAMES: case ID_CONTEXT_SELECTED_CLEARFRAMES:
if (selectionFrames.size()) ClearFrames(); ClearFrames();
break; break;
case TASEDIT_PLAYSTOP: case TASEDIT_PLAYSTOP:
playback.ToggleEmulationPause(); 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 switched off then jump to selection
if (TASEdit_follow_playback) if (TASEdit_follow_playback)
FollowPlayback(); FollowPlayback();
else if (selectionFrames.size()) else if (selection.CurrentSelection().size())
FollowSelection(); FollowSelection();
else if (playback.pauseframe) else if (playback.pauseframe)
FollowPauseframe(); FollowPauseframe();
@ -1613,12 +1463,17 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar
//switch "Show Markers" flag //switch "Show Markers" flag
TASEdit_show_markers ^= 1; TASEdit_show_markers ^= 1;
CheckMenuItem(hmenu, ID_VIEW_SHOW_MARKERS, TASEdit_show_markers?MF_CHECKED : MF_UNCHECKED); 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; break;
case ID_VIEW_SHOWDOTINEMPTYCELLS: case ID_VIEW_SHOWBRANCHSCREENSHOTS:
TASEdit_show_dot ^= 1; //switch "Show Branch Screenshots" flag
CheckMenuItem(hmenu, ID_VIEW_SHOWDOTINEMPTYCELLS, TASEdit_show_dot?MF_CHECKED : MF_UNCHECKED); TASEdit_show_branch_screenshots ^= 1;
RedrawList(); 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; break;
case ID_VIEW_JUMPWHENMAKINGUNDO: case ID_VIEW_JUMPWHENMAKINGUNDO:
TASEdit_jump_to_undo ^= 1; 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; TasEdit_undo_levels = new_size;
history.init(TasEdit_undo_levels); history.init(TasEdit_undo_levels);
selection.init(TasEdit_undo_levels);
// hot changes were cleared, so update list // hot changes were cleared, so update list
RedrawList(); RedrawList();
} }
@ -1680,15 +1536,6 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar
} }
break; 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: case ID_CONFIG_BRANCHESRESTOREFULLMOVIE:
//switch "Branches restore entire Movie" flag //switch "Branches restore entire Movie" flag
TASEdit_branch_full_movie ^= 1; 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; TASEdit_branch_scr_hud ^= 1;
CheckMenuItem(hmenu, ID_CONFIG_HUDINBRANCHSCREENSHOTS, TASEdit_branch_scr_hud?MF_CHECKED : MF_UNCHECKED); CheckMenuItem(hmenu, ID_CONFIG_HUDINBRANCHSCREENSHOTS, TASEdit_branch_scr_hud?MF_CHECKED : MF_UNCHECKED);
break; 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: case IDC_PROGRESS_BUTTON:
// click on progressbar - stop seeking // click on progressbar - stop seeking
if (playback.pauseframe) playback.SeekingStop(); 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 ACCEL_CTRL_A:
case ID_EDIT_SELECTMIDMARKERS: case ID_EDIT_SELECTMIDMARKERS:
case ID_SELECTED_SELECTMIDMARKERS: case ID_SELECTED_SELECTMIDMARKERS:
SelectMidMarkers(); selection.SelectMidMarkers();
break; break;
case ACCEL_SHIFT_INS: case ACCEL_SHIFT_INS:
case ID_EDIT_CLONEFRAMES: case ID_EDIT_CLONEFRAMES:
case ID_SELECTED_CLONE: case ID_SELECTED_CLONE:
if (selectionFrames.size()) CloneFrames(); CloneFrames();
break; break;
case ACCEL_CTRL_Z: case ACCEL_CTRL_Z:
case ID_EDIT_UNDO: case ID_EDIT_UNDO:
@ -1777,6 +1633,27 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar
} }
break; 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; break;
@ -1831,10 +1708,12 @@ void FollowUndo()
} }
void FollowSelection() void FollowSelection()
{ {
if (selection.CurrentSelection().size() == 0) return;
int list_items = listItems; int list_items = listItems;
if (currMovieData.fourscore) list_items--; if (currMovieData.fourscore) list_items--;
int selection_start = *selectionFrames.begin(); int selection_start = *selection.CurrentSelection().begin();
int selection_end = *selectionFrames.rbegin(); int selection_end = *selection.CurrentSelection().rbegin();
int selection_items = 1 + selection_end - selection_start; int selection_items = 1 + selection_end - selection_start;
if (selection_items <= list_items) if (selection_items <= list_items)
@ -1917,22 +1796,23 @@ void EnterTasEdit()
CheckDlgButton(hwndTasEdit, CHECK_FOLLOW_CURSOR, TASEdit_follow_playback?MF_CHECKED : MF_UNCHECKED); 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_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_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_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_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_BRANCHESWORKONLYWHENRECORDING, TASEdit_branch_only_when_rec?MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hmenu, ID_CONFIG_HUDINBRANCHSCREENSHOTS, TASEdit_branch_scr_hud?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_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 // init modules
greenzone.init(); greenzone.init();
playback.init(); playback.init();
// either start new movie or use current movie // either start new movie or use current movie
if (movieMode == MOVIEMODE_INACTIVE) if (FCEUMOV_Mode(MOVIEMODE_INACTIVE))
{ {
FCEUI_StopMovie(); FCEUI_StopMovie();
CreateCleanMovie(); CreateCleanMovie();
@ -1948,15 +1828,23 @@ void EnterTasEdit()
movie_readonly = true; movie_readonly = true;
multitrack_recording_joypad = MULTITRACK_RECORDING_ALL; multitrack_recording_joypad = MULTITRACK_RECORDING_ALL;
RecheckRecordingRadioButtons(); RecheckRecordingRadioButtons();
// switch to tasedit mode
movieMode = MOVIEMODE_TASEDIT; movieMode = MOVIEMODE_TASEDIT;
// create font for main listview // create fonts for main listview
hMainListFont = CreateFont(13, 8, /*Height,Width*/ hMainListFont = CreateFont(15, 10, /*Height,Width*/
0, 0, /*escapement,orientation*/ 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*/ ANSI_CHARSET, OUT_DEVICE_PRECIS, CLIP_MASK, /*charset, precision, clipping*/
DEFAULT_QUALITY, DEFAULT_PITCH, /*quality, and pitch*/ DEFAULT_QUALITY, DEFAULT_PITCH, /*quality, and pitch*/
"Courier"); /*font name*/ "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 // prepare the main listview
ListView_SetExtendedListViewStyleEx(hwndList, LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES, LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES); ListView_SetExtendedListViewStyleEx(hwndList, LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES, LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES);
@ -2041,7 +1929,7 @@ void EnterTasEdit()
// frame number column // frame number column
lvc.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_FMT; lvc.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_FMT;
lvc.fmt = LVCFMT_CENTER; lvc.fmt = LVCFMT_CENTER;
lvc.cx = 74; lvc.cx = 75;
lvc.pszText = "Frame#"; lvc.pszText = "Frame#";
ListView_InsertColumn(hwndList, colidx++, &lvc); ListView_InsertColumn(hwndList, colidx++, &lvc);
// pads columns // pads columns
@ -2079,12 +1967,15 @@ void EnterTasEdit()
// jump_frame column // jump_frame column
lvc.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_FMT; lvc.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_FMT;
lvc.fmt = LVCFMT_CENTER; lvc.fmt = LVCFMT_CENTER;
lvc.cx = 72; lvc.cx = 74;
ListView_InsertColumn(hwndBookmarksList, 1, &lvc); ListView_InsertColumn(hwndBookmarksList, 1, &lvc);
// time column // time column
lvc.cx = 82; lvc.cx = 80;
ListView_InsertColumn(hwndBookmarksList, 2, &lvc); ListView_InsertColumn(hwndBookmarksList, 2, &lvc);
// subclass BranchesBitmap
hwndBranchesBitmap_oldWndProc = (WNDPROC)SetWindowLong(hwndBranchesBitmap, GWL_WNDPROC, (LONG)BranchesBitmapWndProc);
// prepare the history listview // prepare the history listview
ListView_SetExtendedListViewStyleEx(hwndHistoryList, LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES, LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES); ListView_SetExtendedListViewStyleEx(hwndHistoryList, LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES, LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES);
// subclass the listview // subclass the listview
@ -2094,14 +1985,12 @@ void EnterTasEdit()
lvc.fmt = LVCFMT_LEFT; lvc.fmt = LVCFMT_LEFT;
ListView_InsertColumn(hwndHistoryList, 0, &lvc); ListView_InsertColumn(hwndHistoryList, 0, &lvc);
// subclass BranchesBitmap
hwndBranchesBitmap_oldWndProc = (WNDPROC)SetWindowLong(hwndBranchesBitmap, GWL_WNDPROC, (LONG)BranchesBitmapWndProc);
// init variables // init variables
markers.init(); markers.init();
project.init(); project.init();
bookmarks.init(); bookmarks.init();
history.init(TasEdit_undo_levels); history.init(TasEdit_undo_levels);
selection.init(TasEdit_undo_levels);
SetFocus(hwndHistoryList); // to show darkblue cursor SetFocus(hwndHistoryList); // to show darkblue cursor
SetFocus(hwndList); SetFocus(hwndList);
FCEU_DispMessage("Tasedit engaged",0); FCEU_DispMessage("Tasedit engaged",0);
@ -2132,6 +2021,7 @@ bool ExitTasEdit()
bookmarks.free(); bookmarks.free();
history.free(); history.free();
playback.SeekingStop(); playback.SeekingStop();
selection.free();
movieMode = MOVIEMODE_INACTIVE; movieMode = MOVIEMODE_INACTIVE;
FCEU_DispMessage("Tasedit disengaged",0); FCEU_DispMessage("Tasedit disengaged",0);

View File

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

View File

@ -7,8 +7,11 @@
#include "zlib.h" #include "zlib.h"
extern GREENZONE greenzone; extern GREENZONE greenzone;
extern INPUT_HISTORY history;
extern bool TASEdit_branch_scr_hud; extern bool TASEdit_branch_scr_hud;
extern bool TASEdit_enable_hot_changes;
extern uint8 *XBuf; extern uint8 *XBuf;
extern uint8 *XBackBuf; extern uint8 *XBackBuf;
@ -27,8 +30,12 @@ void BOOKMARK::init()
void BOOKMARK::set() void BOOKMARK::set()
{ {
snapshot.init(currMovieData, false); // copy input and hotchanges
snapshot.init(currMovieData, TASEdit_enable_hot_changes);
snapshot.jump_frame = currFrameCounter; snapshot.jump_frame = currFrameCounter;
if (TASEdit_enable_hot_changes)
snapshot.copyHotChanges(&history.GetCurrentSnapshot());
// copy savestate
savestate = greenzone.savestates[currFrameCounter]; savestate = greenzone.savestates[currFrameCounter];
// save screenshot // save screenshot
uLongf comprlen = (SCREENSHOT_SIZE>>9)+12 + SCREENSHOT_SIZE; 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_full_movie;
extern bool TASEdit_branch_only_when_rec; extern bool TASEdit_branch_only_when_rec;
extern bool TASEdit_view_branches_tree; extern bool TASEdit_view_branches_tree;
extern bool TASEdit_show_branch_screenshots;
BOOKMARKS::BOOKMARKS() BOOKMARKS::BOOKMARKS()
{ {
// create font // create font
hBookmarksFont = CreateFont(13, 8, /*Height,Width*/ hBookmarksFont = CreateFont(15, 10, /*Height,Width*/
0, 0, /*escapement,orientation*/ 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*/ ANSI_CHARSET, OUT_DEVICE_PRECIS, CLIP_MASK, /*charset, precision, clipping*/
DEFAULT_QUALITY, DEFAULT_PITCH, /*quality, and pitch*/ DEFAULT_QUALITY, DEFAULT_PITCH, /*quality, and pitch*/
"Courier"); /*font name*/ "Courier"); /*font name*/
@ -292,11 +293,11 @@ void BOOKMARKS::update()
if (must_check_item_under_mouse) if (must_check_item_under_mouse)
CheckMousePos(); CheckMousePos();
// render branches_bitmap // render branches_bitmap
if (must_redraw_branches_tree) if (edit_mode == EDIT_MODE_BRANCHES && must_redraw_branches_tree)
RedrawBranchesTree(); RedrawBranchesTree();
// change screenshot_bitmap alpha if needed // 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) if (!hwndScrBmp)
{ {
@ -315,18 +316,24 @@ void BOOKMARKS::update()
{ {
scr_bmp_phase++; scr_bmp_phase++;
// update alpha // 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); UpdateLayeredWindow(hwndScrBmp, 0, 0, 0, 0, 0, 0, &blend, ULW_ALPHA);
} }
} else } else
{ {
// fade and finally hide screenshot
if (scr_bmp_phase > 0)
scr_bmp_phase--;
if (scr_bmp_phase > 0) if (scr_bmp_phase > 0)
{ {
scr_bmp_phase--;
if (hwndScrBmp) if (hwndScrBmp)
{ {
// update alpha // 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); UpdateLayeredWindow(hwndScrBmp, 0, 0, 0, 0, 0, 0, &blend, ULW_ALPHA);
} }
} else } else
@ -426,11 +433,11 @@ void BOOKMARKS::unleash(int slot)
if (!bookmarks_array[slot].not_empty) return; if (!bookmarks_array[slot].not_empty) return;
int jump_frame = bookmarks_array[slot].snapshot.jump_frame; 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) if (TASEdit_branch_full_movie)
{ {
// update Markers // update Markers
bool markers_changed = false;
if (TASEdit_bind_markers) if (TASEdit_bind_markers)
{ {
if (bookmarks_array[slot].snapshot.checkMarkersDiff()) if (bookmarks_array[slot].snapshot.checkMarkersDiff())
@ -448,12 +455,12 @@ void BOOKMARKS::unleash(int slot)
currMovieData.records.resize(bookmarks_array[slot].snapshot.size); currMovieData.records.resize(bookmarks_array[slot].snapshot.size);
bookmarks_array[slot].snapshot.toMovie(currMovieData, first_change); bookmarks_array[slot].snapshot.toMovie(currMovieData, first_change);
UpdateList(); 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); greenzone.Invalidate(first_change);
bookmarks_array[slot].unleashed(); bookmarks_array[slot].unleashed();
} else if (markers_changed) } 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(); RedrawList();
bookmarks_array[slot].unleashed(); bookmarks_array[slot].unleashed();
} else } else
@ -464,7 +471,6 @@ void BOOKMARKS::unleash(int slot)
} else } else
{ {
// update Markers // update Markers
bool markers_changed = false;
if (TASEdit_bind_markers) if (TASEdit_bind_markers)
{ {
if (bookmarks_array[slot].snapshot.checkMarkersDiff(jump_frame)) 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 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); bookmarks_array[slot].snapshot.toMovie(currMovieData, first_change, jump_frame-1);
UpdateList(); 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); greenzone.Invalidate(first_change);
bookmarks_array[slot].unleashed(); bookmarks_array[slot].unleashed();
} else if (markers_changed) } 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(); RedrawList();
bookmarks_array[slot].unleashed(); bookmarks_array[slot].unleashed();
} else } 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); 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 // time of item under cursor
if (item_under_mouse > ITEM_UNDER_MOUSE_NONE) if (item_under_mouse > ITEM_UNDER_MOUSE_NONE)
{ {
if (item_under_mouse == ITEM_UNDER_MOUSE_CLOUD) 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) 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 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 // finished
must_redraw_branches_tree = false; must_redraw_branches_tree = false;

View File

@ -19,6 +19,10 @@
#define BRANCHES_BITMAP_WIDTH 170 #define BRANCHES_BITMAP_WIDTH 170
#define BRANCHES_BITMAP_HEIGHT 145 #define BRANCHES_BITMAP_HEIGHT 145
#define BRANCHES_ANIMATION_FRAMES 12 #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 // constants for drawing branches tree
#define BRANCHES_CANVAS_WIDTH 146 #define BRANCHES_CANVAS_WIDTH 146
#define BRANCHES_CANVAS_HEIGHT 130 #define BRANCHES_CANVAS_HEIGHT 130
@ -82,8 +86,8 @@
#define TIME_DESC_LENGTH 9 // "HH:MM:SS" #define TIME_DESC_LENGTH 9 // "HH:MM:SS"
// screenshot bitmap // screenshot bitmap
#define SCR_BMP_PHASE_MAX 10 #define SCR_BMP_PHASE_MAX 11
#define SCR_BMP_PHASE_ALPHA_MAX 8
class BOOKMARKS 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 ((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 if (strcmp(greenzone_save_id, save_id)) goto error; // string is not valid
// read size // read size
if (read32le((uint32 *)&size, is) && size >= 0 && size <= currMovieData.getNumRecords()) if (read32le(&size, is) && size >= 0 && size <= currMovieData.getNumRecords())
{ {
greenZoneCount = size; greenZoneCount = size;
savestates.resize(greenZoneCount); savestates.resize(greenZoneCount);
@ -246,7 +246,7 @@ bool GREENZONE::load(EMUFILE *is)
int e = uncompress(&lag_history[0], &destlen, &cbuf[0], comprlen); int e = uncompress(&lag_history[0], &destlen, &cbuf[0], comprlen);
if (e != Z_OK && e != Z_BUF_ERROR) goto error; if (e != Z_OK && e != Z_BUF_ERROR) goto error;
// read playback position // read playback position
if (read32le((uint32 *)&frame, is)) if (read32le(&frame, is))
{ {
currFrameCounter = frame; currFrameCounter = frame;
int greenzone_tail_frame = currFrameCounter - TASEdit_greenzone_capacity; int greenzone_tail_frame = currFrameCounter - TASEdit_greenzone_capacity;
@ -257,7 +257,7 @@ bool GREENZONE::load(EMUFILE *is)
// read savestates // read savestates
while(1) while(1)
{ {
if (!read32le((uint32 *)&frame, is)) break; if (!read32le(&frame, is)) break;
if (frame < 0) break; // -1 = eof if (frame < 0) break; // -1 = eof
// update TASEditor progressbar from time to time // update TASEditor progressbar from time to time
if (frame / PROGRESSBAR_UPDATE_RATE > last_tick) if (frame / PROGRESSBAR_UPDATE_RATE > last_tick)
@ -268,7 +268,7 @@ bool GREENZONE::load(EMUFILE *is)
// read lua_colorings // read lua_colorings
// read monitorings // read monitorings
// read savestate // read savestate
if (!read32le((uint32 *)&size, is)) break; if (!read32le(&size, is)) break;
if (size < 0) break; if (size < 0) break;
if (frame <= greenzone_tail_frame16 if (frame <= greenzone_tail_frame16
|| (frame <= greenzone_tail_frame8 && (frame & 0xF)) || (frame <= greenzone_tail_frame8 && (frame & 0xF))

View File

@ -1,14 +1,14 @@
//Implementation file of Input History class (Undo feature) //Implementation file of Input History class (Undo feature)
#include "movie.h"
#include "../common.h"
#include "../tasedit.h"
#include "taseditproj.h" #include "taseditproj.h"
#include "../tasedit.h" // just for mainlist functions, later this should be deleted
extern void FCEU_printf(char *format, ...); extern void FCEU_printf(char *format, ...);
extern HWND hwndHistoryList; extern HWND hwndHistoryList;
extern bool TASEdit_bind_markers; extern bool TASEdit_bind_markers;
extern bool TASEdit_enable_hot_changes;
extern bool TASEdit_branch_full_movie;
extern MARKERS markers; extern MARKERS markers;
extern BOOKMARKS bookmarks; 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; undo_hint_pos = old_undo_hint_pos = undo_hint_time = -1;
old_show_undo_hint = show_undo_hint = false; old_show_undo_hint = show_undo_hint = false;
// clear snapshots history // clear snapshots history
history_total_items = 0; free();
input_snapshots.resize(history_size); input_snapshots.resize(history_size);
history_start_pos = 0; history_start_pos = 0;
history_cursor_pos = -1; history_cursor_pos = -1;
// create initial snapshot // create initial snapshot
INPUT_SNAPSHOT inp; INPUT_SNAPSHOT inp;
inp.init(currMovieData, true); inp.init(currMovieData, TASEdit_enable_hot_changes);
strcat(inp.description, modCaptions[0]); strcat(inp.description, modCaptions[0]);
inp.jump_frame = -1; inp.jump_frame = -1;
AddInputSnapshotToHistory(inp); AddInputSnapshotToHistory(inp);
@ -85,6 +85,7 @@ void INPUT_HISTORY::init(int new_size)
void INPUT_HISTORY::free() void INPUT_HISTORY::free()
{ {
input_snapshots.resize(0); input_snapshots.resize(0);
history_total_items = 0;
} }
void INPUT_HISTORY::update() void INPUT_HISTORY::update()
@ -149,11 +150,16 @@ int INPUT_HISTORY::jump(int new_pos)
currMovieData.records.resize(input_snapshots[real_pos].size); currMovieData.records.resize(input_snapshots[real_pos].size);
input_snapshots[real_pos].toMovie(currMovieData, first_change); input_snapshots[real_pos].toMovie(currMovieData, first_change);
bookmarks.ChangesMadeSinceBranch(); bookmarks.ChangesMadeSinceBranch();
// list will be redrawn by greenzone invalidation
} else if (markers_changed) } else if (markers_changed)
{ {
markers.update(); markers.update();
bookmarks.ChangesMadeSinceBranch(); bookmarks.ChangesMadeSinceBranch();
RedrawList(); 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; return first_change;
@ -173,7 +179,7 @@ void INPUT_HISTORY::AddInputSnapshotToHistory(INPUT_SNAPSHOT &inp)
int real_pos; int real_pos;
if (history_cursor_pos+1 >= history_size) 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_cursor_pos = history_size-1;
history_start_pos = (history_start_pos + 1) % history_size; history_start_pos = (history_start_pos + 1) % history_size;
} else } else
@ -211,13 +217,12 @@ void INPUT_HISTORY::AddInputSnapshotToHistory(INPUT_SNAPSHOT &inp)
RedrawHistoryList(); RedrawHistoryList();
} }
// returns frame of first actual change // returns frame of first actual change
int INPUT_HISTORY::RegisterChanges(int mod_type, int start, int end) int INPUT_HISTORY::RegisterChanges(int mod_type, int start, int end)
{ {
// create new input shanshot // create new input shanshot
INPUT_SNAPSHOT inp; 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) if (mod_type == MODTYPE_MARKER_SET || mod_type == MODTYPE_MARKER_UNSET)
{ {
// special case: changed markers, but input didn't change // 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, "-");
strcat(inp.description, framenum); strcat(inp.description, framenum);
} }
if (TASEdit_enable_hot_changes)
inp.copyHotChanges(&GetCurrentSnapshot());
AddInputSnapshotToHistory(inp); AddInputSnapshotToHistory(inp);
bookmarks.ChangesMadeSinceBranch(); bookmarks.ChangesMadeSinceBranch();
return -1; return -1;
@ -247,10 +254,6 @@ int INPUT_HISTORY::RegisterChanges(int mod_type, int start, int end)
if (first_changes >= 0) if (first_changes >= 0)
{ {
// differences found // differences found
// fade old hot_changes by 1
// highlight new hot changes
// fill description: // fill description:
strcat(inp.description, modCaptions[mod_type]); strcat(inp.description, modCaptions[mod_type]);
switch (mod_type) switch (mod_type)
@ -278,14 +281,14 @@ int INPUT_HISTORY::RegisterChanges(int mod_type, int start, int end)
} }
case MODTYPE_RECORD: 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 int num = (inp.input_type + 1) * 2; // hacky, only for distingushing between normal2p and fourscore
for (int i = 0; i < num; ++i) for (int i = 0; i < num; ++i)
{ {
if (inp.checkJoypadDiff(input_snapshots[real_pos], first_changes, i)) if (inp.checkJoypadDiff(input_snapshots[real_pos], first_changes, i))
strcat(inp.description, joypadCaptions[i]); strcat(inp.description, joypadCaptions[i]);
} }
inp.jump_frame = start;
} }
} }
// add upper and lower frame to description // 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, "-");
strcat(inp.description, framenum); 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); AddInputSnapshotToHistory(inp);
bookmarks.ChangesMadeSinceBranch(); bookmarks.ChangesMadeSinceBranch();
} }
return first_changes; 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; INPUT_SNAPSHOT inp;
inp.init(currMovieData, true); inp.init(currMovieData, TASEdit_enable_hot_changes);
// fill description: // fill description: modification type + time of the Branch
strcat(inp.description, modCaptions[mod_type]); 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; 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); 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 ((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 if (strcmp(history_save_id, save_id)) goto error; // string is not valid
// read vars // read vars
if (!read32le((uint32 *)&history_cursor_pos, is)) goto error; if (!read32le(&history_cursor_pos, is)) goto error;
if (!read32le((uint32 *)&history_total_items, is)) goto error; if (!read32le(&history_total_items, is)) goto error;
if (history_cursor_pos > history_total_items) goto error; if (history_cursor_pos > history_total_items) goto error;
history_start_pos = 0; history_start_pos = 0;
// read snapshots // read snapshots
@ -373,7 +430,6 @@ bool INPUT_HISTORY::load(EMUFILE *is)
// load snapshots // load snapshots
for (i = 0; i < history_total_items; ++i) 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; if (input_snapshots[i].load(is)) goto error;
playback.SetProgressbar(i, history_total_items); playback.SetProgressbar(i, history_total_items);
} }

View File

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

View File

@ -1,8 +1,6 @@
//Implementation file of Input Snapshot class (Undo feature) //Implementation file of Input Snapshot class (Undo feature)
#include "movie.h" #include "taseditproj.h"
#include "inputsnapshot.h"
#include "markers.h"
#include "zlib.h" #include "zlib.h"
const int bytes_per_frame[NUM_SUPPORTED_INPUT_TYPES] = {2, 4}; // 16bits for normal joypads, 32bits for fourscore 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 void FCEU_printf(char *format, ...);
extern MARKERS markers; extern MARKERS markers;
extern TASEDIT_SELECTION selection;
INPUT_SNAPSHOT::INPUT_SNAPSHOT() INPUT_SNAPSHOT::INPUT_SNAPSHOT()
{ {
} }
void INPUT_SNAPSHOT::init(MovieData& md, bool hotchanges) 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); struct tm * timeinfo = localtime(&raw_time);
strftime(description, 10, "%H:%M:%S", timeinfo); strftime(description, 10, "%H:%M:%S", timeinfo);
} }
// copy all stored markers to Markers // copy all stored markers to Markers
void INPUT_SNAPSHOT::toMarkers() 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) // copy some stored markers to Markers manually, from Frame 0 to end frame (including end frame)
void INPUT_SNAPSHOT::copyToMarkers(int end) 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--) for (int i = end; i >= 0; i--)
markers.markers_array[i] = markers_array[i]; markers.markers_array[i] = markers_array[i];
} }
@ -178,7 +177,6 @@ void INPUT_SNAPSHOT::save(EMUFILE *os)
// returns true if couldn't load // returns true if couldn't load
bool INPUT_SNAPSHOT::load(EMUFILE *is) bool INPUT_SNAPSHOT::load(EMUFILE *is)
{ {
int len;
uint8 tmp; uint8 tmp;
// read vars // read vars
if (!read32le(&size, is)) return true; 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) // return true only when difference is found before end frame (not including end frame)
bool INPUT_SNAPSHOT::checkMarkersDiff(int end) 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--) for (int i = end-1; i >= 0; i--)
if ((markers_array[i] - markers.markers_array[i]) & MARKER_FLAG_BIT) return true; if ((markers_array[i] - markers.markers_array[i]) & MARKER_FLAG_BIT) return true;
return false; 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) // search for differences to the specified end (or to the end of this snapshot)
if (end < 0 || end >= size) end = size-1; if (end < 0 || end >= size) end = size-1;
int inp_end = inp.size; int inp_end = inp.size;
switch(input_type) switch(input_type)
{ {
case FOURSCORE: 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 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 // no changes were found
return -1; return -1;
} }
@ -432,7 +429,214 @@ int INPUT_SNAPSHOT::findFirstChange(MovieData& md, int start, int end)
return -1; // no changes were found 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) void INPUT_SNAPSHOT::SetMaxHotChange(int frame, int absolute_button)
{ {
if (frame < 0 || frame >= size || !has_hot_changes) return; 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 // 32 buttons = 16bytes
if (absolute_button & 1) if (absolute_button & 1)
// odd buttons (B, T, D, R) - set upper 4 bits of the byte // 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 else
// even buttons (A, S, U, L) - set lower 4 bits of the byte // 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; break;
} }
case NORMAL_2JOYPADS: case NORMAL_2JOYPADS:
@ -455,18 +659,35 @@ void INPUT_SNAPSHOT::SetMaxHotChange(int frame, int absolute_button)
// 16 buttons = 8bytes // 16 buttons = 8bytes
if (absolute_button & 1) if (absolute_button & 1)
// odd buttons (B, T, D, R) - set upper 4 bits of the byte // 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 else
// even buttons (A, S, U, L) - set lower 4 bits of the byte // 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; 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) int INPUT_SNAPSHOT::GetHotChangeInfo(int frame, int absolute_button)
{ {
if (frame < 0 || frame >= size || !has_hot_changes) return 0; if (!has_hot_changes || frame < 0 || frame >= size || absolute_button < 0 || absolute_button > 31)
if (absolute_button < 0 || absolute_button > 31) return 0; return 0;
uint8 val; uint8 val;
switch(input_type) switch(input_type)
@ -493,4 +714,3 @@ int INPUT_SNAPSHOT::GetHotChangeInfo(int frame, int absolute_button)
return val & 15; return val & 15;
} }

View File

@ -33,11 +33,21 @@ public:
int findFirstChange(INPUT_SNAPSHOT& inp, int start = 0, int end = -1); int findFirstChange(INPUT_SNAPSHOT& inp, int start = 0, int end = -1);
int findFirstChange(MovieData& md, 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 SetMaxHotChange(int frame, int absolute_button);
void FadeHotChanges();
int GetHotChangeInfo(int frame, int absolute_button); int GetHotChangeInfo(int frame, int absolute_button);
int size; // in frames 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> 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> 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, ... 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 bool coherent; // indicates whether this state was made right after previous state
int jump_frame; // for jumping when making undo int jump_frame; // for jumping when making undo
char description[SNAPSHOT_DESC_MAX_LENGTH]; char description[SNAPSHOT_DESC_MAX_LENGTH];
bool has_hot_changes;
private: private:
void compress_data(); void compress_data();
@ -56,6 +67,5 @@ private:
std::vector<uint8> hot_changes_compressed; std::vector<uint8> hot_changes_compressed;
std::vector<uint8> markers_array_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 GREENZONE greenzone;
extern PLAYBACK playback; extern PLAYBACK playback;
extern INPUT_HISTORY history; extern INPUT_HISTORY history;
extern TASEDIT_SELECTION selection;
extern void FCEU_printf(char *format, ...); extern void FCEU_printf(char *format, ...);
extern int TASEdit_autosave_period; extern int TASEdit_autosave_period;
@ -57,6 +58,7 @@ bool TASEDIT_PROJECT::saveProject()
bookmarks.save(ofs); bookmarks.save(ofs);
greenzone.save(ofs); greenzone.save(ofs);
history.save(ofs); history.save(ofs);
selection.save(ofs);
delete ofs; delete ofs;
@ -79,6 +81,7 @@ bool TASEDIT_PROJECT::LoadProject(std::string PFN)
bool error; bool error;
LoadFM2(currMovieData, &ifs, ifs.size(), false); LoadFM2(currMovieData, &ifs, ifs.size(), false);
LoadSubtitles(currMovieData); LoadSubtitles(currMovieData);
UpdateList();
// try to load markers // try to load markers
error = markers.load(&ifs); error = markers.load(&ifs);
if (error) if (error)
@ -113,6 +116,15 @@ bool TASEDIT_PROJECT::LoadProject(std::string PFN)
{ {
FCEU_printf("Error loading history\n"); FCEU_printf("Error loading history\n");
history.init(); history.init();
} else
{
// try to load selection
error = selection.load(&ifs);
}
if (error)
{
FCEU_printf("Error loading selection\n");
selection.init();
} }
reset(); reset();

View File

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

View File

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

View File

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