diff --git a/src/drivers/win/config.cpp b/src/drivers/win/config.cpp index 34811d2b..b662ac69 100644 --- a/src/drivers/win/config.cpp +++ b/src/drivers/win/config.cpp @@ -71,13 +71,14 @@ extern int frameSkipAmt; extern bool TASEdit_follow_playback; extern bool TASEdit_show_lag_frames; extern bool TASEdit_show_markers; +extern bool TASEdit_show_branch_screenshots; extern bool TASEdit_bind_markers; extern bool TASEdit_branch_full_movie; extern bool TASEdit_branch_only_when_rec; extern bool TASEdit_view_branches_tree; extern bool TASEdit_branch_scr_hud; extern bool TASEdit_restore_position; -extern bool TASEdit_show_dot; +extern bool TASEdit_enable_hot_changes; extern int TASEdit_greenzone_capacity; extern int TasEdit_undo_levels; extern int TASEdit_autosave_period; @@ -302,13 +303,14 @@ static CFGSTRUCT fceuconfig[] = { AC(TASEdit_follow_playback), AC(TASEdit_show_lag_frames), AC(TASEdit_show_markers), + AC(TASEdit_show_branch_screenshots), AC(TASEdit_bind_markers), AC(TASEdit_branch_full_movie), AC(TASEdit_branch_only_when_rec), AC(TASEdit_view_branches_tree), AC(TASEdit_branch_scr_hud), AC(TASEdit_restore_position), - AC(TASEdit_show_dot), + AC(TASEdit_enable_hot_changes), AC(TASEdit_greenzone_capacity), AC(TasEdit_undo_levels), AC(TASEdit_autosave_period), diff --git a/src/drivers/win/res.rc b/src/drivers/win/res.rc index cc5e3950..3123d032 100644 --- a/src/drivers/win/res.rc +++ b/src/drivers/win/res.rc @@ -221,9 +221,10 @@ TASEDITMENU MENU BEGIN POPUP "&File" BEGIN - MENUITEM "&Open Project...", ID_FILE_OPENPROJECT - MENUITEM "&Save Project\tCtrl+S", ID_FILE_SAVEPROJECT - MENUITEM "S&ave Project As...", ID_FILE_SAVEPROJECTAS + MENUITEM "&Open", ID_FILE_OPENPROJECT + MENUITEM "&Save\tCtrl+S", ID_FILE_SAVEPROJECT + MENUITEM "S&ave As", ID_FILE_SAVEPROJECTAS + MENUITEM "Save &Compact", ID_FILE_SAVECOMPACT, INACTIVE MENUITEM "&Recent", ID_TASEDIT_FILE_RECENT MENUITEM SEPARATOR MENUITEM "&Import FM2", ID_FILE_IMPORTFM2, INACTIVE @@ -233,29 +234,33 @@ BEGIN END POPUP "&Edit" BEGIN + MENUITEM "&Undo\tCtrl+Z", ID_EDIT_UNDO + MENUITEM "&Redo\tCtrl+Y", ID_EDIT_REDO + MENUITEM "Selection Undo\tCtrl+Q", ID_EDIT_SELECTIONUNDO + MENUITEM "Selection Redo\tCtrl+W", ID_EDIT_SELECTIONREDO + MENUITEM SEPARATOR MENUITEM "Select &All", ID_EDIT_SELECTALL MENUITEM "Select mid &Markers\tCtrl+A", ID_EDIT_SELECTMIDMARKERS + MENUITEM "Reselect Clipboard\tCtrl+B", ID_EDIT_RESELECTCLIPBOARD MENUITEM SEPARATOR - MENUITEM "Cu&t\tCtrl+X", ID_TASEDIT_CUT MENUITEM "&Copy\tCtrl+C", ID_TASEDIT_COPY MENUITEM "&Paste\tCtrl+V", ID_TASEDIT_PASTE + MENUITEM "Cu&t\tCtrl+X", ID_TASEDIT_CUT MENUITEM SEPARATOR - MENUITEM "C&lear\tDelete", ID_EDIT_CLEAR + MENUITEM "C&lear\tDel", ID_EDIT_CLEAR MENUITEM "&Delete\tCtrl+Del", ID_TASEDIT_DELETE MENUITEM "&Insert\tCtrl+Ins", ID_EDIT_INSERTFRAMES - MENUITEM "Insert # of Frames\tInsert", ID_EDIT_INSERT + MENUITEM "Insert # of Frames\tIns", ID_EDIT_INSERT MENUITEM "Cl&one\tShift+Ins", ID_EDIT_CLONEFRAMES MENUITEM SEPARATOR - MENUITEM "Truncate movie\tCtrl+T", ID_EDIT_TRUNCATE - MENUITEM SEPARATOR - MENUITEM "&Undo\tCtrl-Z", ID_EDIT_UNDO - MENUITEM "&Redo\tCtrl-Y", ID_EDIT_REDO + MENUITEM "Truncate\tCtrl+T", ID_EDIT_TRUNCATE END POPUP "&View" BEGIN MENUITEM "Highlight &Lag Frames", ID_VIEW_SHOW_LAG_FRAMES MENUITEM "Show &Markers", ID_VIEW_SHOW_MARKERS - MENUITEM "Show &dot in empty cells", ID_VIEW_SHOWDOTINEMPTYCELLS + MENUITEM "Show Branch &Screenshots", ID_VIEW_SHOWBRANCHSCREENSHOTS + MENUITEM "Enable Hot &Changes", ID_VIEW_ENABLEHOTCHANGES MENUITEM SEPARATOR MENUITEM "&Follow undo context", ID_VIEW_JUMPWHENMAKINGUNDO END @@ -275,7 +280,8 @@ BEGIN END POPUP "&Help" BEGIN - MENUITEM "&TASEdit Help...", ID_HELP_TASEDITHELP + MENUITEM "&TASEdit Help", ID_HELP_TASEDITHELP + MENUITEM "&About", ID_HELP_ABOUT END END @@ -1997,15 +2003,18 @@ BEGIN "F", ACCEL_CTRL_F, VIRTKEY, CONTROL, NOINVERT VK_INSERT, ACCEL_CTRL_INSERT, VIRTKEY, CONTROL, NOINVERT "P", ACCEL_CTRL_P, VIRTKEY, CONTROL, NOINVERT + "Q", ACCEL_CTRL_Q, VIRTKEY, CONTROL, NOINVERT "S", ACCEL_CTRL_S, VIRTKEY, CONTROL, NOINVERT "T", ACCEL_CTRL_T, VIRTKEY, CONTROL, NOINVERT "V", ACCEL_CTRL_V, VIRTKEY, CONTROL, NOINVERT + "W", ACCEL_CTRL_W, VIRTKEY, CONTROL, NOINVERT "X", ACCEL_CTRL_X, VIRTKEY, CONTROL, NOINVERT "Y", ACCEL_CTRL_Y, VIRTKEY, CONTROL, NOINVERT "Z", ACCEL_CTRL_Z, VIRTKEY, CONTROL, NOINVERT VK_DELETE, ACCEL_DEL, VIRTKEY, NOINVERT VK_INSERT, ACCEL_INS, VIRTKEY, NOINVERT VK_INSERT, ACCEL_SHIFT_INS, VIRTKEY, SHIFT, NOINVERT + "B", ACCEL_CTRL_B, VIRTKEY, CONTROL, NOINVERT END IDR_RWACCELERATOR ACCELERATORS diff --git a/src/drivers/win/resource.h b/src/drivers/win/resource.h index 46001981..d140e5b7 100644 --- a/src/drivers/win/resource.h +++ b/src/drivers/win/resource.h @@ -893,6 +893,14 @@ #define ID_CONFIG_BRANCHESWORKONLYWHENRECORDING 40474 #define ID_CONFIG_HUDINBRANCHSCREENSHOTS 40475 #define ID_CONFIG_SETAUTOSAVEPERIOD 40476 +#define ACCEL_CTRL_Q 40478 +#define ID_EDIT_SELECTIONUNDO 40481 +#define ID_EDIT_SELECTIONREDO 40482 +#define ID_EDIT_RESELECTCLIPBOARD 40483 +#define ID_FILE_SAVECOMPACT 40484 +#define ID_HELP_ABOUT 40485 +#define ID_VIEW_ENABLEHOTCHANGES 40488 +#define ID_VIEW_SHOWBRANCHSCREENSHOTS 40489 #define IDC_DEBUGGER_ICONTRAY 55535 #define MW_ValueLabel2 65423 #define MW_ValueLabel1 65426 @@ -902,7 +910,7 @@ #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 185 -#define _APS_NEXT_COMMAND_VALUE 40477 +#define _APS_NEXT_COMMAND_VALUE 40490 #define _APS_NEXT_CONTROL_VALUE 1267 #define _APS_NEXT_SYMED_VALUE 101 #endif diff --git a/src/drivers/win/tasedit.cpp b/src/drivers/win/tasedit.cpp index 49eeba39..14873f4b 100644 --- a/src/drivers/win/tasedit.cpp +++ b/src/drivers/win/tasedit.cpp @@ -2,7 +2,6 @@ #include #include -#include "common.h" #include "taseditlib/taseditproj.h" #include "fceu.h" #include "debugger.h" @@ -18,24 +17,26 @@ using namespace std; -//to change header font -//http://forums.devx.com/archive/index.php/t-37234.html - int old_multitrack_recording_joypad, multitrack_recording_joypad; bool old_movie_readonly; bool TASEdit_focus = false; bool Tasedit_rewind_now = false; int listItems; // number of items per list page + // saved FCEU config int saved_eoptions; int saved_EnableAutosave; extern int EnableAutosave; +extern HWND hwndScrBmp; +extern EMOVIEMODE movieMode; // maybe we need normal setter for movieMode, to encapsulate it + // vars saved in cfg file int TasEdit_wndx, TasEdit_wndy; bool TASEdit_follow_playback = true; bool TASEdit_show_lag_frames = true; bool TASEdit_show_markers = true; +bool TASEdit_show_branch_screenshots = true; bool TASEdit_bind_markers = true; bool TASEdit_branch_full_movie = true; bool TASEdit_branch_only_when_rec = false; @@ -46,9 +47,10 @@ int TASEdit_greenzone_capacity = GREENZONE_CAPACITY_DEFAULT; int TasEdit_undo_levels = UNDO_LEVELS_DEFAULT; int TASEdit_autosave_period = AUTOSAVE_PERIOD_DEFAULT; extern bool muteTurbo; -bool TASEdit_show_dot = true; +bool TASEdit_enable_hot_changes = true; bool TASEdit_jump_to_undo = true; +// resources string tasedithelp = "{16CDE0C4-02B0-4A60-A88D-076319909A4D}"; //Name of TASEdit Help page char buttonNames[NUM_JOYPAD_BUTTONS][2] = {"A", "B", "S", "T", "U", "D", "L", "R"}; char windowCaptions[6][30] = { "TAS Editor", @@ -57,8 +59,7 @@ char windowCaptions[6][30] = { "TAS Editor", "TAS Editor (Recording 2P)", "TAS Editor (Recording 3P)", "TAS Editor (Recording 4P)"}; -// hot changes color table -COLORREF hot_changes_colors[16] = { 0x0, 0x41f2c, 0x62a3b, 0x7344a, 0x93f59, 0xb4968, 0xc5477, 0xe5e86, 0xf6995, 0x1174a4, 0x127eb3, 0x1489c2, 0x1693d1, 0x179ee0, 0x19a8ef, 0x1bb4ff }; +COLORREF hot_changes_colors[16] = { 0x0, 0x661212, 0x842B4E, 0x652C73, 0x48247D, 0x383596, 0x2947AE, 0x1E53C1, 0x135DD2, 0x116EDA, 0x107EE3, 0x0F8EEB, 0x209FF4, 0x3DB1FD, 0x51C2FF, 0x4DCDFF }; HWND hwndTasEdit = 0; HMENU hmenu, hrmenu; @@ -73,14 +74,7 @@ HWND hwndRB_RecOff, hwndRB_RecAll, hwndRB_Rec1P, hwndRB_Rec2P, hwndRB_Rec3P, hwn HWND hwndBranchesBitmap; WNDPROC hwndBranchesBitmap_oldWndProc; -HFONT hMainListFont; - -typedef std::set TSelectionFrames; -static TSelectionFrames selectionFrames; - -//hacky.. we need to think about how to convey information from the driver to the movie code. -//add a new fceud_ function?? blehhh maybe -extern EMOVIEMODE movieMode; +HFONT hMainListFont, hMainListSelectFont; // all Taseditor functional modules TASEDIT_PROJECT project; @@ -89,6 +83,7 @@ PLAYBACK playback; GREENZONE greenzone; MARKERS markers; BOOKMARKS bookmarks; +TASEDIT_SELECTION selection; void GetDispInfo(NMLVDISPINFO* nmlvDispInfo) { @@ -113,7 +108,7 @@ void GetDispInfo(NMLVDISPINFO* nmlvDispInfo) case COLUMN_FRAMENUM: case COLUMN_FRAMENUM2: { - U32ToDecStr(item.pszText,item.iItem,DIGITS_IN_FRAMENUM); + U32ToDecStr(item.pszText, item.iItem, DIGITS_IN_FRAMENUM); break; } case COLUMN_JOYPAD1_A: case COLUMN_JOYPAD1_B: case COLUMN_JOYPAD1_S: case COLUMN_JOYPAD1_T: @@ -130,13 +125,13 @@ void GetDispInfo(NMLVDISPINFO* nmlvDispInfo) uint8 data = currMovieData.records[item.iItem].joysticks[joy]; if(data & (1<iSubItem; cell_y = msg->nmcd.dwItemSpec; - //msg->clrText = 0xFF0000; + if(cell_x > COLUMN_ICONS) { SelectObject(msg->nmcd.hdc, hMainListFont); + // text color + if(TASEdit_enable_hot_changes && cell_x >= COLUMN_JOYPAD1_A && cell_x <= COLUMN_JOYPAD4_R) + { + msg->clrText = hot_changes_colors[history.GetCurrentSnapshot().GetHotChangeInfo(cell_y, cell_x - COLUMN_JOYPAD1_A)]; + } else msg->clrText = NORMAL_TEXT_COLOR; + // bg color if(cell_x == COLUMN_FRAMENUM || cell_x == COLUMN_FRAMENUM2) { // frame number @@ -277,13 +278,15 @@ void UpdateTasEdit() { if(!hwndTasEdit) return; - UpdateList(); // also markers are updated there - + UpdateList(); + markers.update(); greenzone.update(); playback.update(); bookmarks.update(); + selection.update(); history.update(); project.update(); + selection.update(); // update window caption if (old_movie_readonly != movie_readonly || old_multitrack_recording_joypad != multitrack_recording_joypad) @@ -304,28 +307,11 @@ void UpdateTasEdit() void UpdateList() { - // first update number of items in markers array - markers.update(); //update the number of items in the list int currLVItemCount = ListView_GetItemCount(hwndList); int movie_size = currMovieData.getNumRecords(); if(currLVItemCount != movie_size) - { ListView_SetItemCountEx(hwndList,movie_size,LVSICF_NOSCROLL | LVSICF_NOINVALIDATEALL); - // also reduce selection if needed - if (selectionFrames.size()) - { - int delete_index; - while(1) - { - delete_index = *selectionFrames.rbegin(); - if (delete_index < movie_size) break; - // reduce selection manually, because reduced list won't call ItemChanged for these rows - selectionFrames.erase(delete_index); - if (!selectionFrames.size()) break; - } - } - } } void RedrawWindowCaption() @@ -370,12 +356,6 @@ void RedrawRowAndBookmark(int index) bookmarks.RedrawChangedBookmarks(index); } -enum ECONTEXTMENU -{ - CONTEXTMENU_STRAY = 0, - CONTEXTMENU_SELECTED = 1, -}; - void ShowMenu(ECONTEXTMENU which, POINT& pt) { HMENU sub = GetSubMenu(hrmenu,(int)which); @@ -408,14 +388,8 @@ void RightClick(LPNMITEMACTIVATE info) return; } - //make sure that the click is in our currently selected set. - //if it is not, then we don't know what to do yet - if(selectionFrames.find(index) == selectionFrames.end()) - { - return; - } - - RightClickMenu(info); + if (selection.CheckFrameSelected(index)) + RightClickMenu(info); } void InputChangedRec() @@ -430,11 +404,11 @@ void ToggleJoypadBit(int column_index, int row_index, UINT KeyFlags) if (KeyFlags & (LVKF_SHIFT|LVKF_CONTROL)) { //update multiple rows - for(TSelectionFrames::iterator it(selectionFrames.begin()); it != selectionFrames.end(); it++) + for(SelectionFrames::iterator it(selection.CurrentSelection().begin()); it != selection.CurrentSelection().end(); it++) { currMovieData.records[*it].toggleBit(joy,bit); } - greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_CHANGE, *selectionFrames.begin(), *selectionFrames.rbegin())); + greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_CHANGE, *selection.CurrentSelection().begin(), *selection.CurrentSelection().rbegin())); } else { //update one row @@ -456,7 +430,7 @@ void SingleClick(LPNMITEMACTIVATE info) if(column_index == COLUMN_ICONS) { // click on the "icons" column - jump to the frame - ClearSelection(); + selection.ClearSelection(); playback.jump(row_index); } else if(column_index == COLUMN_FRAMENUM || column_index == COLUMN_FRAMENUM2) { @@ -491,7 +465,7 @@ void DoubleClick(LPNMITEMACTIVATE info) if(column_index == COLUMN_ICONS || column_index == COLUMN_FRAMENUM || column_index == COLUMN_FRAMENUM2) { // double click sends playback to the frame - ClearSelection(); + selection.ClearSelection(); playback.jump(row_index); } else if(column_index >= COLUMN_JOYPAD1_A && column_index <= COLUMN_JOYPAD4_R) { @@ -501,18 +475,19 @@ void DoubleClick(LPNMITEMACTIVATE info) void CloneFrames() { - int frames = selectionFrames.size(); + int frames = selection.CurrentSelection().size(); + if (!frames) return; currMovieData.records.reserve(currMovieData.getNumRecords() + frames); //insert frames before each selection, but consecutive selection lines are accounted as single region frames = 1; - TSelectionFrames::reverse_iterator next_it; - for(TSelectionFrames::reverse_iterator it(selectionFrames.rbegin()); it != selectionFrames.rend(); it++) + SelectionFrames::reverse_iterator next_it; + for(SelectionFrames::reverse_iterator it(selection.CurrentSelection().rbegin()); it != selection.CurrentSelection().rend(); it++) { next_it = it; next_it++; - if (next_it == selectionFrames.rend() || (int)*next_it < ((int)*it - 1)) + if (next_it == selection.CurrentSelection().rend() || (int)*next_it < ((int)*it - 1)) { // end of current region currMovieData.cloneRegion(*it, frames); @@ -522,24 +497,25 @@ void CloneFrames() } else frames++; } UpdateList(); - greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_CLONE, *selectionFrames.begin())); + greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_CLONE, *selection.CurrentSelection().begin())); } void InsertFrames() { - int frames = selectionFrames.size(); + int frames = selection.CurrentSelection().size(); + if (!frames) return; //to keep this from being even slower than it would otherwise be, go ahead and reserve records currMovieData.records.reserve(currMovieData.getNumRecords() + frames); //insert frames before each selection, but consecutive selection lines are accounted as single region frames = 1; - TSelectionFrames::reverse_iterator next_it; - for(TSelectionFrames::reverse_iterator it(selectionFrames.rbegin()); it != selectionFrames.rend(); it++) + SelectionFrames::reverse_iterator next_it; + for(SelectionFrames::reverse_iterator it(selection.CurrentSelection().rbegin()); it != selection.CurrentSelection().rend(); it++) { next_it = it; next_it++; - if (next_it == selectionFrames.rend() || (int)*next_it < ((int)*it - 1)) + if (next_it == selection.CurrentSelection().rend() || (int)*next_it < ((int)*it - 1)) { // end of current region currMovieData.insertEmpty(*it,frames); @@ -549,15 +525,45 @@ void InsertFrames() } else frames++; } UpdateList(); - greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_INSERT, *selectionFrames.begin())); + greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_INSERT, *selection.CurrentSelection().begin())); +} + +void InsertNumFrames() +{ + int frames = selection.CurrentSelection().size(); + if(CWin32InputBox::GetInteger("Insert number of Frames", "How many frames?", frames, hwndTasEdit) == IDOK) + { + if (frames > 0) + { + int index; + if (selection.CurrentSelection().size()) + { + // insert at selection + index = *selection.CurrentSelection().begin(); + selection.ClearSelection(); + } else + { + // insert at playback cursor + index = currFrameCounter; + } + currMovieData.insertEmpty(index, frames); + if (TASEdit_bind_markers) + markers.insertEmpty(index, frames); + UpdateList(); + // select inserted rows + selection.SetRegionSelection(index, index + frames - 1); + greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_INSERT, index)); + } + } } void DeleteFrames() { - int start_index = *selectionFrames.begin(); - int end_index = *selectionFrames.rbegin(); + if (selection.CurrentSelection().size() == 0) return; + int start_index = *selection.CurrentSelection().begin(); + int end_index = *selection.CurrentSelection().rbegin(); //delete frames on each selection, going backwards - for(TSelectionFrames::reverse_iterator it(selectionFrames.rbegin()); it != selectionFrames.rend(); it++) + for(SelectionFrames::reverse_iterator it(selection.CurrentSelection().rbegin()); it != selection.CurrentSelection().rend(); it++) { currMovieData.records.erase(currMovieData.records.begin() + *it); if (TASEdit_bind_markers) @@ -581,24 +587,25 @@ void DeleteFrames() void ClearFrames(bool cut) { + if (selection.CurrentSelection().size() == 0) return; //clear input on each selection - for(TSelectionFrames::iterator it(selectionFrames.begin()); it != selectionFrames.end(); it++) + for(SelectionFrames::iterator it(selection.CurrentSelection().begin()); it != selection.CurrentSelection().end(); it++) { currMovieData.records[*it].clear(); } if (cut) - greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_CUT, *selectionFrames.begin(), *selectionFrames.rbegin())); + greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_CUT, *selection.CurrentSelection().begin(), *selection.CurrentSelection().rbegin())); else - greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_CLEAR, *selectionFrames.begin(), *selectionFrames.rbegin())); + greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_CLEAR, *selection.CurrentSelection().begin(), *selection.CurrentSelection().rbegin())); } void Truncate() { int frame = currFrameCounter; - if (selectionFrames.size()) + if (selection.CurrentSelection().size()) { - frame=*selectionFrames.begin(); - ClearSelection(); + frame = *selection.CurrentSelection().begin(); + selection.ClearSelection(); } if (currMovieData.getNumRecords() > frame+1) { @@ -625,7 +632,7 @@ void ColumnSet(int column) // Markers column //inspect the selected frames, if they are all set, then unset all, else set all bool unset_found = false; - for(TSelectionFrames::iterator it(selectionFrames.begin()); it != selectionFrames.end(); it++) + for(SelectionFrames::iterator it(selection.CurrentSelection().begin()); it != selection.CurrentSelection().end(); it++) { if(!(markers.markers_array[*it] & MARKER_FLAG_BIT)) { @@ -636,18 +643,18 @@ void ColumnSet(int column) if (unset_found) { // set all - for(TSelectionFrames::iterator it(selectionFrames.begin()); it != selectionFrames.end(); it++) + for(SelectionFrames::iterator it(selection.CurrentSelection().begin()); it != selection.CurrentSelection().end(); it++) markers.markers_array[*it] |= MARKER_FLAG_BIT; - history.RegisterChanges(MODTYPE_MARKER_SET, *selectionFrames.begin(), *selectionFrames.rbegin()); + history.RegisterChanges(MODTYPE_MARKER_SET, *selection.CurrentSelection().begin(), *selection.CurrentSelection().rbegin()); } else { // unset all - for(TSelectionFrames::iterator it(selectionFrames.begin()); it != selectionFrames.end(); it++) + for(SelectionFrames::iterator it(selection.CurrentSelection().begin()); it != selection.CurrentSelection().end(); it++) markers.markers_array[*it] &= ~MARKER_FLAG_BIT; - history.RegisterChanges(MODTYPE_MARKER_UNSET, *selectionFrames.begin(), *selectionFrames.rbegin()); + history.RegisterChanges(MODTYPE_MARKER_UNSET, *selection.CurrentSelection().begin(), *selection.CurrentSelection().rbegin()); } project.SetProjectChanged(); - ClearSelection(); + selection.ClearSelection(); // no need to RedrawList(); } else { @@ -657,7 +664,7 @@ void ColumnSet(int column) int button = (column - COLUMN_JOYPAD1_A) % NUM_JOYPAD_BUTTONS; //inspect the selected frames, if they are all set, then unset all, else set all bool newValue = false; - for(TSelectionFrames::iterator it(selectionFrames.begin()); it != selectionFrames.end(); it++) + for(SelectionFrames::iterator it(selection.CurrentSelection().begin()); it != selection.CurrentSelection().end(); it++) { if(!(currMovieData.records[*it].checkBit(joy,button))) { @@ -666,114 +673,30 @@ void ColumnSet(int column) } } // apply newValue - for(TSelectionFrames::iterator it(selectionFrames.begin()); it != selectionFrames.end(); it++) + for(SelectionFrames::iterator it(selection.CurrentSelection().begin()); it != selection.CurrentSelection().end(); it++) currMovieData.records[*it].setBitValue(joy,button,newValue); if (newValue) - greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_SET, *selectionFrames.begin(), *selectionFrames.rbegin())); + greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_SET, *selection.CurrentSelection().begin(), *selection.CurrentSelection().rbegin())); else - greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_UNSET, *selectionFrames.begin(), *selectionFrames.rbegin())); + greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_UNSET, *selection.CurrentSelection().begin(), *selection.CurrentSelection().rbegin())); } } -void ClearSelection() -{ - ListView_SetItemState(hwndList, -1, 0, LVIS_FOCUSED|LVIS_SELECTED); -} -void ClearRowSelection(int index) -{ - ListView_SetItemState(hwndList,index,0, LVIS_SELECTED); -} - -void SelectAll() -{ - ListView_SetItemState(hwndList,-1,LVIS_SELECTED, LVIS_SELECTED); - RedrawList(); -} -void SelectMidMarkers() -{ - int center, upper_border, lower_border; - int upper_marker, lower_marker; - int movie_size = currMovieData.getNumRecords(); - - // if selection size=0 then playback cursor is selected and serves as center - if (selectionFrames.size()) - { - upper_border = center = *selectionFrames.begin(); - lower_border = *selectionFrames.rbegin(); - } else lower_border = upper_border = center = currFrameCounter; - - // find markers - // searching up starting from center-0 - for (upper_marker = center; upper_marker >= 0; upper_marker--) - if (markers.markers_array[upper_marker] & MARKER_FLAG_BIT) break; - // searching down starting from center+1 - for (lower_marker = center+1; lower_marker < movie_size; ++lower_marker) - if (markers.markers_array[lower_marker] & MARKER_FLAG_BIT) break; - - if (upper_marker == -1 && lower_marker == movie_size) - { - SelectAll(); - return; - } - - //ClearSelection(); - need to clear without clearing focused, because otherwise there's strange bug when quickly pressing Ctrl+A right after clicking on already selected row - ListView_SetItemState(hwndList, -1, 0, LVIS_SELECTED); - // selecting circle: - if (upper_border > upper_marker+1 || lower_border < lower_marker-1 || lower_border > lower_marker) - { - // default: select all between markers - for (int i = upper_marker+1; i < lower_marker; ++i) - { - ListView_SetItemState(hwndList,i,LVIS_SELECTED,LVIS_SELECTED); - } - } else if (upper_border == upper_marker+1 && lower_border == lower_marker-1) - { - // already selected all between markers - now select both markers or at least select the marker that is not outside movie range - if (upper_marker < 0) upper_marker = 0; - if (lower_marker >= movie_size) lower_marker = movie_size - 1; - for (int i = upper_marker; i <= lower_marker; ++i) - { - ListView_SetItemState(hwndList,i,LVIS_SELECTED,LVIS_SELECTED); - } - } else if (upper_border <= upper_marker && lower_border >= lower_marker) - { - // selected all between markers and both markers selected too - now deselect lower marker - for (int i = upper_marker; i < lower_marker; ++i) - { - ListView_SetItemState(hwndList,i,LVIS_SELECTED,LVIS_SELECTED); - } - } else if (upper_border == upper_marker && lower_border == lower_marker-1) - { - // selected all between markers and upper marker selected too - now deselect upper marker and (if lower marker < movie_size) reselect lower marker - if (lower_marker >= movie_size) lower_marker = movie_size - 1; - for (int i = upper_marker+1; i <= lower_marker; ++i) - { - ListView_SetItemState(hwndList,i,LVIS_SELECTED,LVIS_SELECTED); - } - } else if (upper_border == upper_marker+1 && lower_border == lower_marker) - { - // selected all between markers and lower marker selected too - now deselect lower marker (return to "selected all between markers") - for (int i = upper_marker + 1; i < lower_marker; ++i) - { - ListView_SetItemState(hwndList,i,LVIS_SELECTED,LVIS_SELECTED); - } - } -} bool Copy() { - if (selectionFrames.size()==0) return false; + if (selection.CurrentSelection().size() == 0) return false; - int cframe=*selectionFrames.begin()-1; + int cframe = *selection.CurrentSelection().begin()-1; try { - int range = *selectionFrames.rbegin() - *selectionFrames.begin()+1; + int range = *selection.CurrentSelection().rbegin() - *selection.CurrentSelection().begin()+1; //std::string outbuf clipString("TAS"); std::stringstream clipString; clipString << "TAS " << range << std::endl; - for(TSelectionFrames::iterator it(selectionFrames.begin()); it != selectionFrames.end(); it++) + for(SelectionFrames::iterator it(selection.CurrentSelection().begin()); it != selection.CurrentSelection().end(); it++) { if (*it>cframe+1) { @@ -793,7 +716,7 @@ bool Copy() { if (currMovieData.records[*it].joysticks[joy] & (1<uOldState & LVIS_SELECTED) && (info->uNewState & LVIS_SELECTED); - bool OFF = (info->uOldState & LVIS_SELECTED) && !(info->uNewState & LVIS_SELECTED); - - if(ON) - for(int i=info->iFrom;i<=info->iTo;i++) - selectionFrames.insert(i); - else - for(int i=info->iFrom;i<=info->iTo;i++) - selectionFrames.erase(i); -} -void ItemChanged(NMLISTVIEW* info) -{ - int item = info->iItem; - - bool ON = !(info->uOldState & LVIS_SELECTED) && (info->uNewState & LVIS_SELECTED); - bool OFF = (info->uOldState & LVIS_SELECTED) && !(info->uNewState & LVIS_SELECTED); - - //if the item is -1, apply the change to all items - if(item == -1) - { - if(OFF) - { - // clear all - selectionFrames.clear(); - } else if (ON) - { - // select all - for(int i = currMovieData.getNumRecords() - 1; i >= 0; i--) - { - selectionFrames.insert(i); - } - } - } - else - { - if(ON) - selectionFrames.insert(item); - else if(OFF) - selectionFrames.erase(item); - } -} - BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) @@ -1391,7 +1266,6 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar TasEdit_wndy = wrect.top; WindowBoundsCheckNoResize(TasEdit_wndx,TasEdit_wndy,wrect.right); // also move screenshot bitmap if it's open - extern HWND hwndScrBmp; if (hwndScrBmp) SetWindowPos(hwndScrBmp, 0, TasEdit_wndx + bookmarks.scr_bmp_x, TasEdit_wndy + bookmarks.scr_bmp_y, 0, 0,SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE); } @@ -1421,10 +1295,10 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar RightClick((LPNMITEMACTIVATE)lParam); break; case LVN_ITEMCHANGED: - ItemChanged((LPNMLISTVIEW) lParam); + selection.ItemChanged((LPNMLISTVIEW) lParam); break; case LVN_ODSTATECHANGED: - ItemRangeChanged((LPNMLVODSTATECHANGE) lParam); + selection.ItemRangeChanged((LPNMLVODSTATECHANGE) lParam); break; /* case LVN_ENDSCROLL: @@ -1518,7 +1392,7 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar ExitTasEdit(); break; case ID_EDIT_SELECTALL: - SelectAll(); + selection.SelectAll(); break; case ACCEL_CTRL_X: case ID_TASEDIT_CUT: @@ -1535,7 +1409,7 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar case ACCEL_CTRL_DELETE: case ID_TASEDIT_DELETE: case ID_CONTEXT_SELECTED_DELETEFRAMES: - if (selectionFrames.size()) DeleteFrames(); + DeleteFrames(); break; case ACCEL_CTRL_T: case ID_EDIT_TRUNCATE: @@ -1551,41 +1425,17 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar case ID_EDIT_INSERT: case MENU_CONTEXT_STRAY_INSERTFRAMES: case ID_CONTEXT_SELECTED_INSERTFRAMES2: - { - int frames = selectionFrames.size(); - if(CWin32InputBox::GetInteger("Insert number of Frames", "How many frames?", frames, hwndDlg) == IDOK) - { - if (frames > 0) - { - if (selectionFrames.size()) - { - // insert at selection - int index = *selectionFrames.begin(); - currMovieData.insertEmpty(index, frames); - if (TASEdit_bind_markers) - markers.insertEmpty(index, frames); - greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_INSERT, index)); - } else - { - // insert at playback cursor - currMovieData.insertEmpty(currFrameCounter, frames); - if (TASEdit_bind_markers) - markers.insertEmpty(currFrameCounter, frames); - greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_INSERT, currFrameCounter)); - } - } - } - } + InsertNumFrames(); break; case ACCEL_CTRL_INSERT: case ID_EDIT_INSERTFRAMES: case ID_CONTEXT_SELECTED_INSERTFRAMES: - if (selectionFrames.size()) InsertFrames(); + InsertFrames(); break; case ACCEL_DEL: case ID_EDIT_CLEAR: case ID_CONTEXT_SELECTED_CLEARFRAMES: - if (selectionFrames.size()) ClearFrames(); + ClearFrames(); break; case TASEDIT_PLAYSTOP: playback.ToggleEmulationPause(); @@ -1598,7 +1448,7 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar // if switched off then jump to selection if (TASEdit_follow_playback) FollowPlayback(); - else if (selectionFrames.size()) + else if (selection.CurrentSelection().size()) FollowSelection(); else if (playback.pauseframe) FollowPauseframe(); @@ -1613,12 +1463,17 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar //switch "Show Markers" flag TASEdit_show_markers ^= 1; CheckMenuItem(hmenu, ID_VIEW_SHOW_MARKERS, TASEdit_show_markers?MF_CHECKED : MF_UNCHECKED); - RedrawList(); + RedrawList(); // no need to redraw Bookmarks, as Markers are only shown in main list break; - case ID_VIEW_SHOWDOTINEMPTYCELLS: - TASEdit_show_dot ^= 1; - CheckMenuItem(hmenu, ID_VIEW_SHOWDOTINEMPTYCELLS, TASEdit_show_dot?MF_CHECKED : MF_UNCHECKED); - RedrawList(); + case ID_VIEW_SHOWBRANCHSCREENSHOTS: + //switch "Show Branch Screenshots" flag + TASEdit_show_branch_screenshots ^= 1; + CheckMenuItem(hmenu, ID_VIEW_SHOWBRANCHSCREENSHOTS, TASEdit_show_branch_screenshots?MF_CHECKED : MF_UNCHECKED); + break; + case ID_VIEW_ENABLEHOTCHANGES: + TASEdit_enable_hot_changes ^= 1; + CheckMenuItem(hmenu, ID_VIEW_ENABLEHOTCHANGES, TASEdit_enable_hot_changes?MF_CHECKED : MF_UNCHECKED); + RedrawList(); // redraw buttons text break; case ID_VIEW_JUMPWHENMAKINGUNDO: TASEdit_jump_to_undo ^= 1; @@ -1660,6 +1515,7 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar { TasEdit_undo_levels = new_size; history.init(TasEdit_undo_levels); + selection.init(TasEdit_undo_levels); // hot changes were cleared, so update list RedrawList(); } @@ -1680,15 +1536,6 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar } break; } - case ID_CONFIG_MUTETURBO: - muteTurbo ^= 1; - CheckMenuItem(hmenu, ID_CONFIG_MUTETURBO, muteTurbo?MF_CHECKED : MF_UNCHECKED); - break; - case ID_CONFIG_BINDMARKERSTOINPUT: - //switch "Bind Markers to Input" flag - TASEdit_bind_markers ^= 1; - CheckMenuItem(hmenu, ID_CONFIG_BINDMARKERSTOINPUT, TASEdit_bind_markers?MF_CHECKED : MF_UNCHECKED); - break; case ID_CONFIG_BRANCHESRESTOREFULLMOVIE: //switch "Branches restore entire Movie" flag TASEdit_branch_full_movie ^= 1; @@ -1705,6 +1552,15 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar TASEdit_branch_scr_hud ^= 1; CheckMenuItem(hmenu, ID_CONFIG_HUDINBRANCHSCREENSHOTS, TASEdit_branch_scr_hud?MF_CHECKED : MF_UNCHECKED); break; + case ID_CONFIG_BINDMARKERSTOINPUT: + //switch "Bind Markers to Input" flag + TASEdit_bind_markers ^= 1; + CheckMenuItem(hmenu, ID_CONFIG_BINDMARKERSTOINPUT, TASEdit_bind_markers?MF_CHECKED : MF_UNCHECKED); + break; + case ID_CONFIG_MUTETURBO: + muteTurbo ^= 1; + CheckMenuItem(hmenu, ID_CONFIG_MUTETURBO, muteTurbo?MF_CHECKED : MF_UNCHECKED); + break; case IDC_PROGRESS_BUTTON: // click on progressbar - stop seeking if (playback.pauseframe) playback.SeekingStop(); @@ -1746,12 +1602,12 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar case ACCEL_CTRL_A: case ID_EDIT_SELECTMIDMARKERS: case ID_SELECTED_SELECTMIDMARKERS: - SelectMidMarkers(); + selection.SelectMidMarkers(); break; case ACCEL_SHIFT_INS: case ID_EDIT_CLONEFRAMES: case ID_SELECTED_CLONE: - if (selectionFrames.size()) CloneFrames(); + CloneFrames(); break; case ACCEL_CTRL_Z: case ID_EDIT_UNDO: @@ -1777,6 +1633,27 @@ BOOL CALLBACK WndprocTasEdit(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lPar } break; } + case ID_EDIT_SELECTIONUNDO: + case ACCEL_CTRL_Q: + { + selection.undo(); + FollowSelection(); + break; + } + case ID_EDIT_SELECTIONREDO: + case ACCEL_CTRL_W: + { + selection.redo(); + FollowSelection(); + break; + } + case ID_EDIT_RESELECTCLIPBOARD: + case ACCEL_CTRL_B: + { + selection.ReselectClipboard(); + FollowSelection(); + break; + } } break; @@ -1831,10 +1708,12 @@ void FollowUndo() } void FollowSelection() { + if (selection.CurrentSelection().size() == 0) return; + int list_items = listItems; if (currMovieData.fourscore) list_items--; - int selection_start = *selectionFrames.begin(); - int selection_end = *selectionFrames.rbegin(); + int selection_start = *selection.CurrentSelection().begin(); + int selection_end = *selection.CurrentSelection().rbegin(); int selection_items = 1 + selection_end - selection_start; if (selection_items <= list_items) @@ -1917,22 +1796,23 @@ void EnterTasEdit() CheckDlgButton(hwndTasEdit, CHECK_FOLLOW_CURSOR, TASEdit_follow_playback?MF_CHECKED : MF_UNCHECKED); CheckMenuItem(hmenu, ID_VIEW_SHOW_LAG_FRAMES, TASEdit_show_lag_frames?MF_CHECKED : MF_UNCHECKED); CheckMenuItem(hmenu, ID_VIEW_SHOW_MARKERS, TASEdit_show_markers?MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hmenu, ID_VIEW_SHOWBRANCHSCREENSHOTS, TASEdit_show_branch_screenshots?MF_CHECKED : MF_UNCHECKED); CheckMenuItem(hmenu, ID_VIEW_JUMPWHENMAKINGUNDO, TASEdit_jump_to_undo?MF_CHECKED : MF_UNCHECKED); - CheckMenuItem(hmenu, ID_CONFIG_BINDMARKERSTOINPUT, TASEdit_bind_markers?MF_CHECKED : MF_UNCHECKED); CheckMenuItem(hmenu, ID_CONFIG_BRANCHESRESTOREFULLMOVIE, TASEdit_branch_full_movie?MF_CHECKED : MF_UNCHECKED); CheckMenuItem(hmenu, ID_CONFIG_BRANCHESWORKONLYWHENRECORDING, TASEdit_branch_only_when_rec?MF_CHECKED : MF_UNCHECKED); CheckMenuItem(hmenu, ID_CONFIG_HUDINBRANCHSCREENSHOTS, TASEdit_branch_scr_hud?MF_CHECKED : MF_UNCHECKED); - CheckDlgButton(hwndTasEdit,CHECK_AUTORESTORE_PLAYBACK,TASEdit_restore_position?BST_CHECKED:BST_UNCHECKED); + CheckMenuItem(hmenu, ID_CONFIG_BINDMARKERSTOINPUT, TASEdit_bind_markers?MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hmenu, ID_VIEW_ENABLEHOTCHANGES, TASEdit_enable_hot_changes?MF_CHECKED : MF_UNCHECKED); CheckMenuItem(hmenu, ID_CONFIG_MUTETURBO, muteTurbo?MF_CHECKED : MF_UNCHECKED); - CheckMenuItem(hmenu, ID_VIEW_SHOWDOTINEMPTYCELLS, TASEdit_show_dot?MF_CHECKED : MF_UNCHECKED); + CheckDlgButton(hwndTasEdit,CHECK_AUTORESTORE_PLAYBACK,TASEdit_restore_position?BST_CHECKED:BST_UNCHECKED); - SetWindowPos(hwndTasEdit,HWND_TOP,0,0,0,0,SWP_NOSIZE|SWP_NOMOVE|SWP_NOOWNERZORDER); + SetWindowPos(hwndTasEdit, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE|SWP_NOMOVE|SWP_NOOWNERZORDER); // init modules greenzone.init(); playback.init(); // either start new movie or use current movie - if (movieMode == MOVIEMODE_INACTIVE) + if (FCEUMOV_Mode(MOVIEMODE_INACTIVE)) { FCEUI_StopMovie(); CreateCleanMovie(); @@ -1948,15 +1828,23 @@ void EnterTasEdit() movie_readonly = true; multitrack_recording_joypad = MULTITRACK_RECORDING_ALL; RecheckRecordingRadioButtons(); + + // switch to tasedit mode movieMode = MOVIEMODE_TASEDIT; - // create font for main listview - hMainListFont = CreateFont(13, 8, /*Height,Width*/ + // create fonts for main listview + hMainListFont = CreateFont(15, 10, /*Height,Width*/ 0, 0, /*escapement,orientation*/ - FW_REGULAR, FALSE, FALSE, FALSE, /*weight, italic, underline, strikeout*/ + FW_BOLD, FALSE, FALSE, FALSE, /*weight, italic, underline, strikeout*/ ANSI_CHARSET, OUT_DEVICE_PRECIS, CLIP_MASK, /*charset, precision, clipping*/ DEFAULT_QUALITY, DEFAULT_PITCH, /*quality, and pitch*/ "Courier"); /*font name*/ + hMainListSelectFont = CreateFont(14, 7, /*Height,Width*/ + 0, 0, /*escapement,orientation*/ + FW_BOLD, FALSE, FALSE, FALSE, /*weight, italic, underline, strikeout*/ + ANSI_CHARSET, OUT_DEVICE_PRECIS, CLIP_MASK, /*charset, precision, clipping*/ + DEFAULT_QUALITY, DEFAULT_PITCH, /*quality, and pitch*/ + "Arial"); /*font name*/ // prepare the main listview ListView_SetExtendedListViewStyleEx(hwndList, LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES, LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES); @@ -2041,7 +1929,7 @@ void EnterTasEdit() // frame number column lvc.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_FMT; lvc.fmt = LVCFMT_CENTER; - lvc.cx = 74; + lvc.cx = 75; lvc.pszText = "Frame#"; ListView_InsertColumn(hwndList, colidx++, &lvc); // pads columns @@ -2079,12 +1967,15 @@ void EnterTasEdit() // jump_frame column lvc.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_FMT; lvc.fmt = LVCFMT_CENTER; - lvc.cx = 72; + lvc.cx = 74; ListView_InsertColumn(hwndBookmarksList, 1, &lvc); // time column - lvc.cx = 82; + lvc.cx = 80; ListView_InsertColumn(hwndBookmarksList, 2, &lvc); + // subclass BranchesBitmap + hwndBranchesBitmap_oldWndProc = (WNDPROC)SetWindowLong(hwndBranchesBitmap, GWL_WNDPROC, (LONG)BranchesBitmapWndProc); + // prepare the history listview ListView_SetExtendedListViewStyleEx(hwndHistoryList, LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES, LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES); // subclass the listview @@ -2094,14 +1985,12 @@ void EnterTasEdit() lvc.fmt = LVCFMT_LEFT; ListView_InsertColumn(hwndHistoryList, 0, &lvc); - // subclass BranchesBitmap - hwndBranchesBitmap_oldWndProc = (WNDPROC)SetWindowLong(hwndBranchesBitmap, GWL_WNDPROC, (LONG)BranchesBitmapWndProc); - // init variables markers.init(); project.init(); bookmarks.init(); history.init(TasEdit_undo_levels); + selection.init(TasEdit_undo_levels); SetFocus(hwndHistoryList); // to show darkblue cursor SetFocus(hwndList); FCEU_DispMessage("Tasedit engaged",0); @@ -2132,6 +2021,7 @@ bool ExitTasEdit() bookmarks.free(); history.free(); playback.SeekingStop(); + selection.free(); movieMode = MOVIEMODE_INACTIVE; FCEU_DispMessage("Tasedit disengaged",0); diff --git a/src/drivers/win/tasedit.h b/src/drivers/win/tasedit.h index add457a4..dbd4e14b 100644 --- a/src/drivers/win/tasedit.h +++ b/src/drivers/win/tasedit.h @@ -65,6 +65,8 @@ #define DIGITS_IN_FRAMENUM 7 #define ARROW_IMAGE_ID 20 // listview colors +#define NORMAL_TEXT_COLOR 0x0 + #define NORMAL_FRAMENUM_COLOR 0xFFFFFF #define NORMAL_INPUT_COLOR1 0xEDEDED #define NORMAL_INPUT_COLOR2 0xDEDEDE @@ -94,7 +96,7 @@ #define UNDOHINT_INPUT_COLOR2 0xE5B7CC #define MARKED_FRAMENUM_COLOR 0xC0FCFF -#define CUR_MARKED_FRAMENUM_COLOR 0xDEF7F4 +#define CUR_MARKED_FRAMENUM_COLOR 0xDEF7F3 #define MARKED_UNDOHINT_FRAMENUM_COLOR 0xE1E7EC // greenzone cleaning masks @@ -103,6 +105,11 @@ #define EVERY4TH 0xFFFFFFFC #define EVERY2ND 0xFFFFFFFE // ----------------------------- +enum ECONTEXTMENU +{ + CONTEXTMENU_STRAY = 0, + CONTEXTMENU_SELECTED = 1, +}; void EnterTasEdit(); void InitDialog(); bool ExitTasEdit(); @@ -114,8 +121,6 @@ void FollowPlayback(); void FollowUndo(); void FollowSelection(); void FollowPauseframe(); -void ClearSelection(); -void ClearRowSelection(int index); void AddFourscore(); void RemoveFourscore(); void RedrawWindowCaption(); @@ -132,10 +137,9 @@ void OpenProject(); bool SaveProject(); bool SaveProjectAs(); bool AskSaveProject(); -void SelectAll(); -void SelectMidMarkers(); void CloneFrames(); void InsertFrames(); +void InsertNumFrames(); void DeleteFrames(); void ClearFrames(bool cut = false); void Truncate(); diff --git a/src/drivers/win/taseditlib/bookmark.cpp b/src/drivers/win/taseditlib/bookmark.cpp index f11f9ff1..d553a13f 100644 --- a/src/drivers/win/taseditlib/bookmark.cpp +++ b/src/drivers/win/taseditlib/bookmark.cpp @@ -7,8 +7,11 @@ #include "zlib.h" extern GREENZONE greenzone; +extern INPUT_HISTORY history; extern bool TASEdit_branch_scr_hud; +extern bool TASEdit_enable_hot_changes; + extern uint8 *XBuf; extern uint8 *XBackBuf; @@ -27,8 +30,12 @@ void BOOKMARK::init() void BOOKMARK::set() { - snapshot.init(currMovieData, false); + // copy input and hotchanges + snapshot.init(currMovieData, TASEdit_enable_hot_changes); snapshot.jump_frame = currFrameCounter; + if (TASEdit_enable_hot_changes) + snapshot.copyHotChanges(&history.GetCurrentSnapshot()); + // copy savestate savestate = greenzone.savestates[currFrameCounter]; // save screenshot uLongf comprlen = (SCREENSHOT_SIZE>>9)+12 + SCREENSHOT_SIZE; diff --git a/src/drivers/win/taseditlib/bookmarks.cpp b/src/drivers/win/taseditlib/bookmarks.cpp index 541375c4..3106c565 100644 --- a/src/drivers/win/taseditlib/bookmarks.cpp +++ b/src/drivers/win/taseditlib/bookmarks.cpp @@ -46,13 +46,14 @@ extern bool TASEdit_bind_markers; extern bool TASEdit_branch_full_movie; extern bool TASEdit_branch_only_when_rec; extern bool TASEdit_view_branches_tree; +extern bool TASEdit_show_branch_screenshots; BOOKMARKS::BOOKMARKS() { // create font - hBookmarksFont = CreateFont(13, 8, /*Height,Width*/ + hBookmarksFont = CreateFont(15, 10, /*Height,Width*/ 0, 0, /*escapement,orientation*/ - FW_REGULAR, FALSE, FALSE, FALSE, /*weight, italic, underline, strikeout*/ + FW_BOLD, FALSE, FALSE, FALSE, /*weight, italic, underline, strikeout*/ ANSI_CHARSET, OUT_DEVICE_PRECIS, CLIP_MASK, /*charset, precision, clipping*/ DEFAULT_QUALITY, DEFAULT_PITCH, /*quality, and pitch*/ "Courier"); /*font name*/ @@ -292,11 +293,11 @@ void BOOKMARKS::update() if (must_check_item_under_mouse) CheckMousePos(); // render branches_bitmap - if (must_redraw_branches_tree) + if (edit_mode == EDIT_MODE_BRANCHES && must_redraw_branches_tree) RedrawBranchesTree(); // change screenshot_bitmap alpha if needed - if (item_under_mouse >= 0 && item_under_mouse < TOTAL_BOOKMARKS) + if (item_under_mouse >= 0 && item_under_mouse < TOTAL_BOOKMARKS && TASEdit_show_branch_screenshots) { if (!hwndScrBmp) { @@ -315,18 +316,24 @@ void BOOKMARKS::update() { scr_bmp_phase++; // update alpha - SetLayeredWindowAttributes(hwndScrBmp, 0, (255 * scr_bmp_phase) / SCR_BMP_PHASE_MAX, LWA_ALPHA); + int phase_alpha = scr_bmp_phase; + if (phase_alpha > SCR_BMP_PHASE_ALPHA_MAX) phase_alpha = SCR_BMP_PHASE_ALPHA_MAX; + SetLayeredWindowAttributes(hwndScrBmp, 0, (255 * phase_alpha) / SCR_BMP_PHASE_ALPHA_MAX, LWA_ALPHA); UpdateLayeredWindow(hwndScrBmp, 0, 0, 0, 0, 0, 0, &blend, ULW_ALPHA); } } else { + // fade and finally hide screenshot + if (scr_bmp_phase > 0) + scr_bmp_phase--; if (scr_bmp_phase > 0) { - scr_bmp_phase--; if (hwndScrBmp) { // update alpha - SetLayeredWindowAttributes(hwndScrBmp, 0, (255 * scr_bmp_phase) / SCR_BMP_PHASE_MAX, LWA_ALPHA); + int phase_alpha = scr_bmp_phase; + if (phase_alpha > SCR_BMP_PHASE_ALPHA_MAX) phase_alpha = SCR_BMP_PHASE_ALPHA_MAX; + SetLayeredWindowAttributes(hwndScrBmp, 0, (255 * phase_alpha) / SCR_BMP_PHASE_ALPHA_MAX, LWA_ALPHA); UpdateLayeredWindow(hwndScrBmp, 0, 0, 0, 0, 0, 0, &blend, ULW_ALPHA); } } else @@ -426,11 +433,11 @@ void BOOKMARKS::unleash(int slot) if (!bookmarks_array[slot].not_empty) return; int jump_frame = bookmarks_array[slot].snapshot.jump_frame; - // revert movie to the input_snapshot state + bool markers_changed = false; + // revert current movie to the input_snapshot state if (TASEdit_branch_full_movie) { // update Markers - bool markers_changed = false; if (TASEdit_bind_markers) { if (bookmarks_array[slot].snapshot.checkMarkersDiff()) @@ -448,12 +455,12 @@ void BOOKMARKS::unleash(int slot) currMovieData.records.resize(bookmarks_array[slot].snapshot.size); bookmarks_array[slot].snapshot.toMovie(currMovieData, first_change); UpdateList(); - history.RegisterBranch(MODTYPE_BRANCH_0 + slot, first_change, bookmarks_array[slot].snapshot.description); + history.RegisterBranching(MODTYPE_BRANCH_0 + slot, first_change, slot); greenzone.Invalidate(first_change); bookmarks_array[slot].unleashed(); } else if (markers_changed) { - history.RegisterBranch(MODTYPE_BRANCH_MARKERS_0 + slot, first_change, bookmarks_array[slot].snapshot.description); + history.RegisterBranching(MODTYPE_BRANCH_MARKERS_0 + slot, first_change, slot); RedrawList(); bookmarks_array[slot].unleashed(); } else @@ -464,7 +471,6 @@ void BOOKMARKS::unleash(int slot) } else { // update Markers - bool markers_changed = false; if (TASEdit_bind_markers) { if (bookmarks_array[slot].snapshot.checkMarkersDiff(jump_frame)) @@ -482,12 +488,12 @@ void BOOKMARKS::unleash(int slot) if (currMovieData.getNumRecords() <= jump_frame) currMovieData.records.resize(jump_frame+1); // but if old movie is shorter, include last frame as blank frame bookmarks_array[slot].snapshot.toMovie(currMovieData, first_change, jump_frame-1); UpdateList(); - history.RegisterBranch(MODTYPE_BRANCH_0 + slot, first_change, bookmarks_array[slot].snapshot.description); + history.RegisterBranching(MODTYPE_BRANCH_0 + slot, first_change, slot); greenzone.Invalidate(first_change); bookmarks_array[slot].unleashed(); } else if (markers_changed) { - history.RegisterBranch(MODTYPE_BRANCH_MARKERS_0 + slot, first_change, bookmarks_array[slot].snapshot.description); + history.RegisterBranching(MODTYPE_BRANCH_MARKERS_0 + slot, first_change, slot); RedrawList(); bookmarks_array[slot].unleashed(); } else @@ -746,15 +752,25 @@ void BOOKMARKS::RedrawBranchesTree() BitBlt(hBitmapDC, branch_x, branch_y, DIGIT_BITMAP_WIDTH, DIGIT_BITMAP_HEIGHT, hSpritesheetDC, i * DIGIT_BITMAP_WIDTH, 0, SRCCOPY); } } + // jump_frame of item under cursor (except cloud - it doesn't have particular frame) + if (item_under_mouse > ITEM_UNDER_MOUSE_CLOUD) + { + char framenum_string[DIGITS_IN_FRAMENUM+1] = {0}; + if (item_under_mouse < TOTAL_BOOKMARKS) + U32ToDecStr(framenum_string, bookmarks_array[item_under_mouse].snapshot.jump_frame, DIGITS_IN_FRAMENUM); + else + U32ToDecStr(framenum_string, currFrameCounter, DIGITS_IN_FRAMENUM); + TextOut(hBitmapDC, BRANCHES_BITMAP_FRAMENUM_X, BRANCHES_BITMAP_FRAMENUM_Y, (LPCSTR)framenum_string, DIGITS_IN_FRAMENUM); + } // time of item under cursor if (item_under_mouse > ITEM_UNDER_MOUSE_NONE) { if (item_under_mouse == ITEM_UNDER_MOUSE_CLOUD) - TextOut(hBitmapDC, 0, 0, (LPCSTR)cloud_time, 8); + TextOut(hBitmapDC, BRANCHES_BITMAP_TIME_X, BRANCHES_BITMAP_TIME_Y, (LPCSTR)cloud_time, TIME_DESC_LENGTH-1); else if (item_under_mouse < TOTAL_BOOKMARKS) - TextOut(hBitmapDC, 0, 0, (LPCSTR)bookmarks_array[item_under_mouse].snapshot.description, 8); + TextOut(hBitmapDC, BRANCHES_BITMAP_TIME_X, BRANCHES_BITMAP_TIME_Y, (LPCSTR)bookmarks_array[item_under_mouse].snapshot.description, TIME_DESC_LENGTH-1); else - TextOut(hBitmapDC, 0, 0, (LPCSTR)current_pos_time, 8); + TextOut(hBitmapDC, BRANCHES_BITMAP_TIME_X, BRANCHES_BITMAP_TIME_Y, (LPCSTR)current_pos_time, TIME_DESC_LENGTH-1); } // finished must_redraw_branches_tree = false; diff --git a/src/drivers/win/taseditlib/bookmarks.h b/src/drivers/win/taseditlib/bookmarks.h index 215ffda0..71787dd4 100644 --- a/src/drivers/win/taseditlib/bookmarks.h +++ b/src/drivers/win/taseditlib/bookmarks.h @@ -19,6 +19,10 @@ #define BRANCHES_BITMAP_WIDTH 170 #define BRANCHES_BITMAP_HEIGHT 145 #define BRANCHES_ANIMATION_FRAMES 12 +#define BRANCHES_BITMAP_FRAMENUM_X 0 +#define BRANCHES_BITMAP_FRAMENUM_Y 0 +#define BRANCHES_BITMAP_TIME_X 0 +#define BRANCHES_BITMAP_TIME_Y BRANCHES_BITMAP_HEIGHT - 17 // constants for drawing branches tree #define BRANCHES_CANVAS_WIDTH 146 #define BRANCHES_CANVAS_HEIGHT 130 @@ -82,8 +86,8 @@ #define TIME_DESC_LENGTH 9 // "HH:MM:SS" // screenshot bitmap -#define SCR_BMP_PHASE_MAX 10 - +#define SCR_BMP_PHASE_MAX 11 +#define SCR_BMP_PHASE_ALPHA_MAX 8 class BOOKMARKS { diff --git a/src/drivers/win/taseditlib/greenzone.cpp b/src/drivers/win/taseditlib/greenzone.cpp index 0a3e6f15..91e7bc99 100644 --- a/src/drivers/win/taseditlib/greenzone.cpp +++ b/src/drivers/win/taseditlib/greenzone.cpp @@ -231,7 +231,7 @@ bool GREENZONE::load(EMUFILE *is) if ((int)is->fread(save_id, GREENZONE_ID_LEN) < GREENZONE_ID_LEN) goto error; if (strcmp(greenzone_save_id, save_id)) goto error; // string is not valid // read size - if (read32le((uint32 *)&size, is) && size >= 0 && size <= currMovieData.getNumRecords()) + if (read32le(&size, is) && size >= 0 && size <= currMovieData.getNumRecords()) { greenZoneCount = size; savestates.resize(greenZoneCount); @@ -246,7 +246,7 @@ bool GREENZONE::load(EMUFILE *is) int e = uncompress(&lag_history[0], &destlen, &cbuf[0], comprlen); if (e != Z_OK && e != Z_BUF_ERROR) goto error; // read playback position - if (read32le((uint32 *)&frame, is)) + if (read32le(&frame, is)) { currFrameCounter = frame; int greenzone_tail_frame = currFrameCounter - TASEdit_greenzone_capacity; @@ -257,7 +257,7 @@ bool GREENZONE::load(EMUFILE *is) // read savestates while(1) { - if (!read32le((uint32 *)&frame, is)) break; + if (!read32le(&frame, is)) break; if (frame < 0) break; // -1 = eof // update TASEditor progressbar from time to time if (frame / PROGRESSBAR_UPDATE_RATE > last_tick) @@ -268,7 +268,7 @@ bool GREENZONE::load(EMUFILE *is) // read lua_colorings // read monitorings // read savestate - if (!read32le((uint32 *)&size, is)) break; + if (!read32le(&size, is)) break; if (size < 0) break; if (frame <= greenzone_tail_frame16 || (frame <= greenzone_tail_frame8 && (frame & 0xF)) diff --git a/src/drivers/win/taseditlib/inputhistory.cpp b/src/drivers/win/taseditlib/inputhistory.cpp index 95d956b6..a98a6a0c 100644 --- a/src/drivers/win/taseditlib/inputhistory.cpp +++ b/src/drivers/win/taseditlib/inputhistory.cpp @@ -1,14 +1,14 @@ //Implementation file of Input History class (Undo feature) -#include "movie.h" -#include "../common.h" -#include "../tasedit.h" #include "taseditproj.h" +#include "../tasedit.h" // just for mainlist functions, later this should be deleted extern void FCEU_printf(char *format, ...); extern HWND hwndHistoryList; extern bool TASEdit_bind_markers; +extern bool TASEdit_enable_hot_changes; +extern bool TASEdit_branch_full_movie; extern MARKERS markers; extern BOOKMARKS bookmarks; @@ -68,13 +68,13 @@ void INPUT_HISTORY::init(int new_size) undo_hint_pos = old_undo_hint_pos = undo_hint_time = -1; old_show_undo_hint = show_undo_hint = false; // clear snapshots history - history_total_items = 0; + free(); input_snapshots.resize(history_size); history_start_pos = 0; history_cursor_pos = -1; // create initial snapshot INPUT_SNAPSHOT inp; - inp.init(currMovieData, true); + inp.init(currMovieData, TASEdit_enable_hot_changes); strcat(inp.description, modCaptions[0]); inp.jump_frame = -1; AddInputSnapshotToHistory(inp); @@ -85,6 +85,7 @@ void INPUT_HISTORY::init(int new_size) void INPUT_HISTORY::free() { input_snapshots.resize(0); + history_total_items = 0; } void INPUT_HISTORY::update() @@ -149,11 +150,16 @@ int INPUT_HISTORY::jump(int new_pos) currMovieData.records.resize(input_snapshots[real_pos].size); input_snapshots[real_pos].toMovie(currMovieData, first_change); bookmarks.ChangesMadeSinceBranch(); + // list will be redrawn by greenzone invalidation } else if (markers_changed) { markers.update(); bookmarks.ChangesMadeSinceBranch(); RedrawList(); + } else if (TASEdit_enable_hot_changes) + { + // when using Hot Changes, list should be always redrawn, because old changes become less hot + RedrawList(); } return first_change; @@ -173,7 +179,7 @@ void INPUT_HISTORY::AddInputSnapshotToHistory(INPUT_SNAPSHOT &inp) int real_pos; if (history_cursor_pos+1 >= history_size) { - // reached the end of available history_size - move history_start_pos (thus deleting older snapshot) + // reached the end of available history_size - move history_start_pos (thus deleting oldest snapshot) history_cursor_pos = history_size-1; history_start_pos = (history_start_pos + 1) % history_size; } else @@ -211,13 +217,12 @@ void INPUT_HISTORY::AddInputSnapshotToHistory(INPUT_SNAPSHOT &inp) RedrawHistoryList(); } - // returns frame of first actual change int INPUT_HISTORY::RegisterChanges(int mod_type, int start, int end) { // create new input shanshot INPUT_SNAPSHOT inp; - inp.init(currMovieData, true); + inp.init(currMovieData, TASEdit_enable_hot_changes); if (mod_type == MODTYPE_MARKER_SET || mod_type == MODTYPE_MARKER_UNSET) { // special case: changed markers, but input didn't change @@ -235,6 +240,8 @@ int INPUT_HISTORY::RegisterChanges(int mod_type, int start, int end) strcat(inp.description, "-"); strcat(inp.description, framenum); } + if (TASEdit_enable_hot_changes) + inp.copyHotChanges(&GetCurrentSnapshot()); AddInputSnapshotToHistory(inp); bookmarks.ChangesMadeSinceBranch(); return -1; @@ -247,10 +254,6 @@ int INPUT_HISTORY::RegisterChanges(int mod_type, int start, int end) if (first_changes >= 0) { // differences found - // fade old hot_changes by 1 - - // highlight new hot changes - // fill description: strcat(inp.description, modCaptions[mod_type]); switch (mod_type) @@ -278,14 +281,14 @@ int INPUT_HISTORY::RegisterChanges(int mod_type, int start, int end) } case MODTYPE_RECORD: { - // add info which joypads were affected + inp.jump_frame = start; + // also add info which joypads were affected int num = (inp.input_type + 1) * 2; // hacky, only for distingushing between normal2p and fourscore for (int i = 0; i < num; ++i) { if (inp.checkJoypadDiff(input_snapshots[real_pos], first_changes, i)) strcat(inp.description, joypadCaptions[i]); } - inp.jump_frame = start; } } // add upper and lower frame to description @@ -299,21 +302,75 @@ int INPUT_HISTORY::RegisterChanges(int mod_type, int start, int end) strcat(inp.description, "-"); strcat(inp.description, framenum); } + // set hotchanges + if (TASEdit_enable_hot_changes) + { + // inherit previous hotchanges and set new changes + switch (mod_type) + { + case MODTYPE_DELETE: + inp.inheritHotChanges_DeleteSelection(&input_snapshots[real_pos]); + break; + case MODTYPE_INSERT: + case MODTYPE_PASTEINSERT: + case MODTYPE_CLONE: + inp.inheritHotChanges_InsertSelection(&input_snapshots[real_pos]); + break; + case MODTYPE_CHANGE: + case MODTYPE_SET: + case MODTYPE_UNSET: + case MODTYPE_CLEAR: + case MODTYPE_CUT: + case MODTYPE_PASTE: + case MODTYPE_RECORD: + inp.inheritHotChanges(&input_snapshots[real_pos]); + inp.fillHotChanges(input_snapshots[real_pos], first_changes, end); + break; + case MODTYPE_TRUNCATE: + inp.copyHotChanges(&input_snapshots[real_pos]); + // do not add new hotchanges and do not fade old hotchanges, because there was nothing added + break; + case MODTYPE_IMPORT: + // do not inherit old hotchanges, because imported input (most likely) doesn't have direct connection with recent edits, so old hotchanges are irrelevant and should not be copied + inp.fillHotChanges(input_snapshots[real_pos], first_changes, end); + break; + } + } AddInputSnapshotToHistory(inp); bookmarks.ChangesMadeSinceBranch(); } return first_changes; } } -void INPUT_HISTORY::RegisterBranch(int mod_type, int first_change, char* branch_creation_time) +void INPUT_HISTORY::RegisterBranching(int mod_type, int first_change, int slot) { - // create new input shanshot + // create new input snapshot INPUT_SNAPSHOT inp; - inp.init(currMovieData, true); - // fill description: + inp.init(currMovieData, TASEdit_enable_hot_changes); + // fill description: modification type + time of the Branch strcat(inp.description, modCaptions[mod_type]); - strcat(inp.description, branch_creation_time); + strcat(inp.description, bookmarks.bookmarks_array[slot].snapshot.description); inp.jump_frame = first_change; + if (TASEdit_enable_hot_changes) + { + if (mod_type < MODTYPE_BRANCH_MARKERS_0) + { + // input was changed + // copy hotchanges of the Branch + if (TASEdit_branch_full_movie) + { + inp.copyHotChanges(&bookmarks.bookmarks_array[slot].snapshot); + } else + { + // input was branched partially, so copy hotchanges only up to and not including jump_frame of the Branch + inp.copyHotChanges(&bookmarks.bookmarks_array[slot].snapshot, bookmarks.bookmarks_array[slot].snapshot.jump_frame); + } + } else + { + // input was not changed, only Markers were changed + inp.copyHotChanges(&GetCurrentSnapshot()); + } + } AddInputSnapshotToHistory(inp); } @@ -345,8 +402,8 @@ bool INPUT_HISTORY::load(EMUFILE *is) if ((int)is->fread(save_id, HISTORY_ID_LEN) < HISTORY_ID_LEN) goto error; if (strcmp(history_save_id, save_id)) goto error; // string is not valid // read vars - if (!read32le((uint32 *)&history_cursor_pos, is)) goto error; - if (!read32le((uint32 *)&history_total_items, is)) goto error; + if (!read32le(&history_cursor_pos, is)) goto error; + if (!read32le(&history_total_items, is)) goto error; if (history_cursor_pos > history_total_items) goto error; history_start_pos = 0; // read snapshots @@ -373,7 +430,6 @@ bool INPUT_HISTORY::load(EMUFILE *is) // load snapshots for (i = 0; i < history_total_items; ++i) { - // skip snapshots if current history_size is less then history_total_items if (input_snapshots[i].load(is)) goto error; playback.SetProgressbar(i, history_total_items); } diff --git a/src/drivers/win/taseditlib/inputhistory.h b/src/drivers/win/taseditlib/inputhistory.h index 3e1deaae..0ac1728e 100644 --- a/src/drivers/win/taseditlib/inputhistory.h +++ b/src/drivers/win/taseditlib/inputhistory.h @@ -63,11 +63,7 @@ public: void AddInputSnapshotToHistory(INPUT_SNAPSHOT &inp); int RegisterChanges(int mod_type, int start = 0, int end =-1); - void RegisterBranch(int mod_type, int first_change, char* branch_creation_time); - - int InputChanged(int start, int end); - int InputInserted(int start); - int InputDeleted(int start); + void RegisterBranching(int mod_type, int first_change, int slot); INPUT_SNAPSHOT& GetCurrentSnapshot(); INPUT_SNAPSHOT& GetNextToCurrentSnapshot(); diff --git a/src/drivers/win/taseditlib/inputsnapshot.cpp b/src/drivers/win/taseditlib/inputsnapshot.cpp index 16206125..d3a7f691 100644 --- a/src/drivers/win/taseditlib/inputsnapshot.cpp +++ b/src/drivers/win/taseditlib/inputsnapshot.cpp @@ -1,8 +1,6 @@ //Implementation file of Input Snapshot class (Undo feature) -#include "movie.h" -#include "inputsnapshot.h" -#include "markers.h" +#include "taseditproj.h" #include "zlib.h" const int bytes_per_frame[NUM_SUPPORTED_INPUT_TYPES] = {2, 4}; // 16bits for normal joypads, 32bits for fourscore @@ -10,10 +8,10 @@ const int bytes_per_frame[NUM_SUPPORTED_INPUT_TYPES] = {2, 4}; // 16bits for nor extern void FCEU_printf(char *format, ...); extern MARKERS markers; +extern TASEDIT_SELECTION selection; INPUT_SNAPSHOT::INPUT_SNAPSHOT() { - } void INPUT_SNAPSHOT::init(MovieData& md, bool hotchanges) @@ -64,6 +62,7 @@ void INPUT_SNAPSHOT::init(MovieData& md, bool hotchanges) struct tm * timeinfo = localtime(&raw_time); strftime(description, 10, "%H:%M:%S", timeinfo); } + // copy all stored markers to Markers void INPUT_SNAPSHOT::toMarkers() { @@ -72,7 +71,7 @@ void INPUT_SNAPSHOT::toMarkers() // copy some stored markers to Markers manually, from Frame 0 to end frame (including end frame) void INPUT_SNAPSHOT::copyToMarkers(int end) { - if (markers.markers_array.size() <= end) markers.markers_array.resize(end+1); + if ((int)markers.markers_array.size() <= end) markers.markers_array.resize(end+1); for (int i = end; i >= 0; i--) markers.markers_array[i] = markers_array[i]; } @@ -178,7 +177,6 @@ void INPUT_SNAPSHOT::save(EMUFILE *os) // returns true if couldn't load bool INPUT_SNAPSHOT::load(EMUFILE *is) { - int len; uint8 tmp; // read vars if (!read32le(&size, is)) return true; @@ -326,7 +324,7 @@ bool INPUT_SNAPSHOT::checkMarkersDiff() // return true only when difference is found before end frame (not including end frame) bool INPUT_SNAPSHOT::checkMarkersDiff(int end) { - if (markers_array.size() != markers.markers_array.size() && (markers_array.size()-1 < end || markers.markers_array.size()-1 < end)) return true; + if (markers_array.size() != markers.markers_array.size() && ((int)markers_array.size()-1 < end || (int)markers.markers_array.size()-1 < end)) return true; for (int i = end-1; i >= 0; i--) if ((markers_array[i] - markers.markers_array[i]) & MARKER_FLAG_BIT) return true; return false; @@ -338,7 +336,6 @@ int INPUT_SNAPSHOT::findFirstChange(INPUT_SNAPSHOT& inp, int start, int end) // search for differences to the specified end (or to the end of this snapshot) if (end < 0 || end >= size) end = size-1; int inp_end = inp.size; - switch(input_type) { case FOURSCORE: @@ -391,7 +388,7 @@ int INPUT_SNAPSHOT::findFirstChange(INPUT_SNAPSHOT& inp, int start, int end) } } // if current size is less then previous, return size-1 as the frame of difference - if (size < inp.size) return size-1; + if (size < inp_end) return size-1; // no changes were found return -1; } @@ -432,7 +429,214 @@ int INPUT_SNAPSHOT::findFirstChange(MovieData& md, int start, int end) return -1; // no changes were found } +// -------------------------------------------------------- +void INPUT_SNAPSHOT::copyHotChanges(INPUT_SNAPSHOT* source_of_hotchanges, int limit_frame_of_source) +{ + // copy hot changes from source snapshot + if (source_of_hotchanges && source_of_hotchanges->has_hot_changes) + { + int min = hot_changes.size(); + if (min > (int)source_of_hotchanges->hot_changes.size()) + min = source_of_hotchanges->hot_changes.size(); + // special case for Branches: if limit_frame if specified, then copy only hotchanges from 0 to limit_frame + if (limit_frame_of_source >= 0) + { + if (min > limit_frame_of_source * bytes_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY) + min = limit_frame_of_source * bytes_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY; + } + + memcpy(&hot_changes[0], &source_of_hotchanges->hot_changes[0], min); + } +} +void INPUT_SNAPSHOT::inheritHotChanges(INPUT_SNAPSHOT* source_of_hotchanges) +{ + // copy hot changes from source snapshot and fade them + if (source_of_hotchanges && source_of_hotchanges->has_hot_changes) + { + int min = hot_changes.size(); + if (min > (int)source_of_hotchanges->hot_changes.size()) + min = source_of_hotchanges->hot_changes.size(); + + memcpy(&hot_changes[0], &source_of_hotchanges->hot_changes[0], min); + FadeHotChanges(); + } +} +void INPUT_SNAPSHOT::inheritHotChanges_DeleteSelection(INPUT_SNAPSHOT* source_of_hotchanges) +{ + // copy hot changes from source snapshot, but omit deleted frames (which are represented by current selection) + if (source_of_hotchanges && source_of_hotchanges->has_hot_changes) + { + int bytes = bytes_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY; + int frame = 0, pos = 0, source_pos = 0; + int this_size = hot_changes.size(), source_size = source_of_hotchanges->hot_changes.size(); + SelectionFrames::iterator it(selection.CurrentSelection().begin()); + while (pos < this_size && source_pos < source_size) + { + if (it != selection.CurrentSelection().end() && frame == *it) + { + // this frame is selected + it++; + // omit the frame + source_pos += bytes; + } else + { + // copy hotchanges of this frame + memcpy(&hot_changes[pos], &source_of_hotchanges->hot_changes[source_pos], bytes); + pos += bytes; + source_pos += bytes; + } + frame++; + } + FadeHotChanges(); + } +} +void INPUT_SNAPSHOT::inheritHotChanges_InsertSelection(INPUT_SNAPSHOT* source_of_hotchanges) +{ + // copy hot changes from source snapshot, but insert filled lines for inserted frames (which are represented by current selection) + if (source_of_hotchanges && source_of_hotchanges->has_hot_changes) + { + int bytes = bytes_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY; + int frame = 0, region_len = 0, pos = 0, source_pos = 0; + int this_size = hot_changes.size(), source_size = source_of_hotchanges->hot_changes.size(); + SelectionFrames::iterator it(selection.CurrentSelection().begin()); + while (pos < this_size && source_pos < source_size) + { + if (it != selection.CurrentSelection().end() && frame == *it) + { + // this frame is selected + it++; + region_len++; + // set filled line to the frame + memset(&hot_changes[pos], 0xFF, bytes); + pos += bytes; + } else + { + // this frame is not selected + frame -= region_len; + region_len = 0; + // copy hotchanges of this frame + memcpy(&hot_changes[pos], &source_of_hotchanges->hot_changes[source_pos], bytes); + pos += bytes; + source_pos += bytes; + } + frame++; + } + FadeHotChanges(); + } else + { + // no old data, just fill selected lines + int bytes = bytes_per_frame[input_type] * HOTCHANGE_BYTES_PER_JOY; + int frame = 0, region_len = 0, pos = 0; + int this_size = hot_changes.size(); + SelectionFrames::iterator it(selection.CurrentSelection().begin()); + while (pos < this_size) + { + if (it != selection.CurrentSelection().end() && frame == *it) + { + // this frame is selected + it++; + region_len++; + // set filled line to the frame + memset(&hot_changes[pos], 0xFF, bytes); + pos += bytes; + // exit loop when all selection frames are handled + if (it == selection.CurrentSelection().end()) break; + } else + { + // this frame is not selected + frame -= region_len; + region_len = 0; + // leave zeros in this frame + pos += bytes; + } + frame++; + } + } +} +void INPUT_SNAPSHOT::fillHotChanges(INPUT_SNAPSHOT& inp, int start, int end) +{ + // search for differences to the specified end (or to the end of this snapshot) + if (end < 0 || end >= size) end = size-1; + int inp_end = inp.size; + switch(input_type) + { + case FOURSCORE: + { + for (int frame = start, pos = start * bytes_per_frame[input_type]; frame <= end; ++frame) + { + // set changed if found different byte, or found emptiness in inp when there's non-zero value here + if (frame < inp_end) + { + if (joysticks[pos] != inp.joysticks[pos]) + SetMaxHotChange_Bits(frame, 0, joysticks[pos] ^ inp.joysticks[pos]); + pos++; + if (joysticks[pos] != inp.joysticks[pos]) + SetMaxHotChange_Bits(frame, 1, joysticks[pos] ^ inp.joysticks[pos]); + pos++; + if (joysticks[pos] != inp.joysticks[pos]) + SetMaxHotChange_Bits(frame, 2, joysticks[pos] ^ inp.joysticks[pos]); + pos++; + if (joysticks[pos] != inp.joysticks[pos]) + SetMaxHotChange_Bits(frame, 3, joysticks[pos] ^ inp.joysticks[pos]); + pos++; + } else + { + if (joysticks[pos]) + SetMaxHotChange_Bits(frame, 0, joysticks[pos]); + pos++; + if (joysticks[pos]) + SetMaxHotChange_Bits(frame, 1, joysticks[pos]); + pos++; + if (joysticks[pos]) + SetMaxHotChange_Bits(frame, 2, joysticks[pos]); + pos++; + if (joysticks[pos]) + SetMaxHotChange_Bits(frame, 3, joysticks[pos]); + pos++; + } + } + break; + } + case NORMAL_2JOYPADS: + { + for (int frame = start, pos = start * bytes_per_frame[input_type]; frame <= end; ++frame) + { + // set changed if found different byte, or found emptiness in inp when there's non-zero value here + if (frame < inp_end) + { + if (joysticks[pos] != inp.joysticks[pos]) + SetMaxHotChange_Bits(frame, 0, joysticks[pos] ^ inp.joysticks[pos]); + pos++; + if (joysticks[pos] != inp.joysticks[pos]) + SetMaxHotChange_Bits(frame, 1, joysticks[pos] ^ inp.joysticks[pos]); + pos++; + } else + { + if (joysticks[pos]) + SetMaxHotChange_Bits(frame, 0, joysticks[pos]); + pos++; + if (joysticks[pos]) + SetMaxHotChange_Bits(frame, 1, joysticks[pos]); + pos++; + } + } + break; + } + } +} + +void INPUT_SNAPSHOT::SetMaxHotChange_Bits(int frame, int joypad, uint8 joy_bits) +{ + uint8 mask = 1; + // check all 8 buttons and set max hot_changes for bits that are set + for (int i = 0; i < 8; ++i) + { + if (joy_bits & mask) + SetMaxHotChange(frame, joypad * 8 + i); + mask <<= 1; + } +} void INPUT_SNAPSHOT::SetMaxHotChange(int frame, int absolute_button) { if (frame < 0 || frame >= size || !has_hot_changes) return; @@ -444,10 +648,10 @@ void INPUT_SNAPSHOT::SetMaxHotChange(int frame, int absolute_button) // 32 buttons = 16bytes if (absolute_button & 1) // odd buttons (B, T, D, R) - set upper 4 bits of the byte - hot_changes[(frame << 4) | (absolute_button >> 1)] &= 0xF0; + hot_changes[(frame << 4) | (absolute_button >> 1)] |= 0xF0; else // even buttons (A, S, U, L) - set lower 4 bits of the byte - hot_changes[(frame << 4) | (absolute_button >> 1)] &= 0x0F; + hot_changes[(frame << 4) | (absolute_button >> 1)] |= 0x0F; break; } case NORMAL_2JOYPADS: @@ -455,18 +659,35 @@ void INPUT_SNAPSHOT::SetMaxHotChange(int frame, int absolute_button) // 16 buttons = 8bytes if (absolute_button & 1) // odd buttons (B, T, D, R) - set upper 4 bits of the byte - hot_changes[(frame << 3) | (absolute_button >> 1)] &= 0xF0; + hot_changes[(frame << 3) | (absolute_button >> 1)] |= 0xF0; else // even buttons (A, S, U, L) - set lower 4 bits of the byte - hot_changes[(frame << 3) | (absolute_button >> 1)] &= 0x0F; + hot_changes[(frame << 3) | (absolute_button >> 1)] |= 0x0F; break; } } } + +void INPUT_SNAPSHOT::FadeHotChanges() +{ + uint8 hi_half, low_half; + for (int i = hot_changes.size() - 1; i >= 0; i--) + { + if (hot_changes[i]) + { + hi_half = hot_changes[i] >> 4; + low_half = hot_changes[i] & 15; + if (hi_half) hi_half--; + if (low_half) low_half--; + hot_changes[i] = (hi_half << 4) | low_half; + } + } +} + int INPUT_SNAPSHOT::GetHotChangeInfo(int frame, int absolute_button) { - if (frame < 0 || frame >= size || !has_hot_changes) return 0; - if (absolute_button < 0 || absolute_button > 31) return 0; + if (!has_hot_changes || frame < 0 || frame >= size || absolute_button < 0 || absolute_button > 31) + return 0; uint8 val; switch(input_type) @@ -493,4 +714,3 @@ int INPUT_SNAPSHOT::GetHotChangeInfo(int frame, int absolute_button) return val & 15; } - diff --git a/src/drivers/win/taseditlib/inputsnapshot.h b/src/drivers/win/taseditlib/inputsnapshot.h index 5012e406..d2dcfe39 100644 --- a/src/drivers/win/taseditlib/inputsnapshot.h +++ b/src/drivers/win/taseditlib/inputsnapshot.h @@ -33,11 +33,21 @@ public: int findFirstChange(INPUT_SNAPSHOT& inp, int start = 0, int end = -1); int findFirstChange(MovieData& md, int start = 0, int end = -1); + void copyHotChanges(INPUT_SNAPSHOT* source_of_hotchanges, int limit_frame_of_source = -1); + void inheritHotChanges(INPUT_SNAPSHOT* source_of_hotchanges); + void inheritHotChanges_DeleteSelection(INPUT_SNAPSHOT* source_of_hotchanges); + void inheritHotChanges_InsertSelection(INPUT_SNAPSHOT* source_of_hotchanges); + void fillHotChanges(INPUT_SNAPSHOT& inp, int start = 0, int end = -1); + + void SetMaxHotChange_Bits(int frame, int joypad, uint8 joy_bits); void SetMaxHotChange(int frame, int absolute_button); + + void FadeHotChanges(); + int GetHotChangeInfo(int frame, int absolute_button); int size; // in frames - int input_type; // 0=normal, 1=fourscore, in future may support other input types + int input_type; // 0=normal, 1=fourscore; theoretically TASEdit can support other input types, although some stuff may be unintentionally hardcoded std::vector joysticks; // Format: joy0-for-frame0, joy1-for-frame0, joy2-for-frame0, joy3-for-frame0, joy0-for-frame1, joy1-for-frame1, ... std::vector commands; // Format: commands-for-frame0, commands-for-frame1, ... std::vector hot_changes; // Format: buttons01joy0-for-frame0, buttons23joy0-for-frame0, buttons45joy0-for-frame0, buttons67joy0-for-frame0, buttons01joy1-for-frame0, ... @@ -46,6 +56,7 @@ public: bool coherent; // indicates whether this state was made right after previous state int jump_frame; // for jumping when making undo char description[SNAPSHOT_DESC_MAX_LENGTH]; + bool has_hot_changes; private: void compress_data(); @@ -56,6 +67,5 @@ private: std::vector hot_changes_compressed; std::vector markers_array_compressed; - bool has_hot_changes; }; diff --git a/src/drivers/win/taseditlib/tasedit_sel.cpp b/src/drivers/win/taseditlib/tasedit_sel.cpp new file mode 100644 index 00000000..3210d056 --- /dev/null +++ b/src/drivers/win/taseditlib/tasedit_sel.cpp @@ -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); + } + } +} + + + + + + + + diff --git a/src/drivers/win/taseditlib/tasedit_sel.h b/src/drivers/win/taseditlib/tasedit_sel.h new file mode 100644 index 00000000..c5636c7a --- /dev/null +++ b/src/drivers/win/taseditlib/tasedit_sel.h @@ -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 selections_history; + SelectionFrames clipboard_selection; + + int history_cursor_pos; + int history_start_pos; + int history_total_items; + int history_size; + + +}; diff --git a/src/drivers/win/taseditlib/taseditproj.cpp b/src/drivers/win/taseditlib/taseditproj.cpp index 1066fb8c..4c1ded27 100644 --- a/src/drivers/win/taseditlib/taseditproj.cpp +++ b/src/drivers/win/taseditlib/taseditproj.cpp @@ -9,6 +9,7 @@ extern BOOKMARKS bookmarks; extern GREENZONE greenzone; extern PLAYBACK playback; extern INPUT_HISTORY history; +extern TASEDIT_SELECTION selection; extern void FCEU_printf(char *format, ...); extern int TASEdit_autosave_period; @@ -57,6 +58,7 @@ bool TASEDIT_PROJECT::saveProject() bookmarks.save(ofs); greenzone.save(ofs); history.save(ofs); + selection.save(ofs); delete ofs; @@ -79,6 +81,7 @@ bool TASEDIT_PROJECT::LoadProject(std::string PFN) bool error; LoadFM2(currMovieData, &ifs, ifs.size(), false); LoadSubtitles(currMovieData); + UpdateList(); // try to load markers error = markers.load(&ifs); if (error) @@ -113,6 +116,15 @@ bool TASEDIT_PROJECT::LoadProject(std::string PFN) { FCEU_printf("Error loading history\n"); history.init(); + } else + { + // try to load selection + error = selection.load(&ifs); + } + if (error) + { + FCEU_printf("Error loading selection\n"); + selection.init(); } reset(); diff --git a/src/drivers/win/taseditlib/taseditproj.h b/src/drivers/win/taseditlib/taseditproj.h index 68fc4131..46d91719 100644 --- a/src/drivers/win/taseditlib/taseditproj.h +++ b/src/drivers/win/taseditlib/taseditproj.h @@ -1,12 +1,17 @@ //Specification file for the TASEdit Project class +#include +typedef std::set SelectionFrames; + #include "movie.h" +#include "../common.h" #include "inputsnapshot.h" #include "inputhistory.h" #include "playback.h" #include "greenzone.h" #include "markers.h" #include "bookmarks.h" +#include "tasedit_sel.h" class TASEDIT_PROJECT { diff --git a/vc/vc10_fceux.vcxproj b/vc/vc10_fceux.vcxproj index ce049208..7b84670f 100644 --- a/vc/vc10_fceux.vcxproj +++ b/vc/vc10_fceux.vcxproj @@ -426,6 +426,7 @@ + @@ -738,6 +739,7 @@ + diff --git a/vc/vc10_fceux.vcxproj.filters b/vc/vc10_fceux.vcxproj.filters index 0061fa0a..e5db2ea7 100644 --- a/vc/vc10_fceux.vcxproj.filters +++ b/vc/vc10_fceux.vcxproj.filters @@ -928,6 +928,7 @@ drivers\win\taseditlib + @@ -1375,6 +1376,7 @@ drivers\win\taseditlib +