From 66504dc11500d2360cda75f136476199d9ad7cb3 Mon Sep 17 00:00:00 2001 From: ansstuff Date: Thu, 22 Mar 2012 16:49:33 +0000 Subject: [PATCH] * Taseditor: Config->Combine consecutive Recordings/Draws * Taseditor: rightclick menu appears only after click on selected row * Taseditor: refactoring --- changelog.txt | 26 + src/drivers/win/config.cpp | 2 +- src/drivers/win/taseditor.cpp | 5 + src/drivers/win/taseditor/bookmark.cpp | 7 +- src/drivers/win/taseditor/bookmark.h | 3 +- src/drivers/win/taseditor/bookmarks.cpp | 967 ++---------------- src/drivers/win/taseditor/bookmarks.h | 133 +-- src/drivers/win/taseditor/branches.cpp | 944 +++++++++++++++++ src/drivers/win/taseditor/branches.h | 146 +++ src/drivers/win/taseditor/editor.cpp | 10 +- src/drivers/win/taseditor/editor.h | 4 +- src/drivers/win/taseditor/history.cpp | 204 ++-- src/drivers/win/taseditor/history.h | 2 +- src/drivers/win/taseditor/piano_roll.cpp | 64 +- src/drivers/win/taseditor/piano_roll.h | 3 +- src/drivers/win/taseditor/snapshot.cpp | 16 +- src/drivers/win/taseditor/snapshot.h | 6 +- .../win/taseditor/taseditor_config.cpp | 2 +- src/drivers/win/taseditor/taseditor_config.h | 2 +- src/drivers/win/taseditor/taseditor_lua.cpp | 3 +- src/drivers/win/taseditor/taseditor_project.h | 1 + .../win/taseditor/taseditor_window.cpp | 4 +- src/input.cpp | 4 +- vc/vc10_fceux.vcxproj | 2 + vc/vc10_fceux.vcxproj.filters | 12 +- 25 files changed, 1414 insertions(+), 1158 deletions(-) create mode 100644 src/drivers/win/taseditor/branches.cpp create mode 100644 src/drivers/win/taseditor/branches.h diff --git a/changelog.txt b/changelog.txt index d356a9ec..39199894 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,31 @@ + +22-Mar-2012 - AnS - Config->Combine consecutive Recordings/Draws +18-Mar-2012 - AnS - new hotkey "Cancel Seeking (TAS Editor)" (Esc) +18-Mar-2012 - AnS - Taseditor: Config->Autopause at the end of Movie +17-Mar-2012 - CaH4e3 - varous bugfixes, xstring trimming functions logic bugs, etc +17-Mar-2012 - AnS - Taseditor: showing row_last_clicked when Shift or Alt is held +17-Mar-2012 - AnS - Taseditor: no more "allow keys in Piano Roll"; new accelerators: Ctrl + arrows, Shift + arrows, Home/End/Page Up/Page Down, Ctrl + Home/End, Shift + Home/End +17-Mar-2012 - AnS - Taseditor: selection by dragging from Frame# +17-Mar-2012 - AnS - Taseditor: set/pick Markers by doubleclick, throw Markers away +14-Mar-2012 - AnS - Taseditor: drawing input; holding Shift when drawing +14-Mar-2012 - AnS - Taseditor: fixed known WinXP bug with scrollbar arrows +14-Mar-2012 - AnS - Taseditor: moving Markers by drag'n'drop, "Marker Drag" and "Marker Swap" operations +14-Mar-2012 - AnS - Taseditor: dragging blue arrow (moving Playback cursor) +14-Mar-2012 - AnS - Taseditor: observing Piano Roll by dragging cursor outside +11-Mar-2012 - AnS - Taseditor: double-tapping Shift/Ctrl scrolls Piano Roll to respective cursor +09-Mar-2012 - AnS - Taseditor: Config->Deselect on doubleclick +09-Mar-2012 - AnS - Taseditor: checking ROM checksum when saving/loading projects +09-Mar-2012 - AnS - Taseditor: TAS Editor can have mouse wheel input even when keyboard focus is on FCEUX window +09-Mar-2012 - AnS - disabled FCEUX context menu when TAS Editor is engaged 06-mar-2012 - prg - fceux no longer segfaults when opening gamepadconfig with GTK < 2.24 - a message is printed to the console 06-mar-2012 - prg - supports loading of configuration files in $FCEUXDIR/cfg.d/* +01-Mar-2012 - AnS - Taseditor: changing history size doesn't reset history +28-feb-2012 - AnS - Taseditor: "Deselect" option in menus +26-feb-2012 - AnS - Small save/load state speedup, noticeable only in TAS Editor or lua bots +21-feb-2012 - AnS - Taseditor: header lights on mouseover +21-feb-2012 - AnS - Taseditor: registering click at buttondown; Alt+click on input = set pattern +20-feb-2012 - AnS - Taseditor: Right button + wheel = Playback rewind/forward; Shift/Ctrl + wheel = jump via Markers with Playback/Selection cursor +20-feb-2012 - AnS - Taseditor: middle button pauses/unpauses emulation 18-feb-2012 - AnS - PATTERNS menu, loading data from "tools\taseditor_patterns.txt" 18-feb-2012 - AnS - "Use pattern" checkbox in Recorder; Config->ColumnSet Pattern skips Lag 18-feb-2012 - AnS - Taseditor: "Frame#" lights when Alt key is being held, not entering menu by Alt diff --git a/src/drivers/win/config.cpp b/src/drivers/win/config.cpp index 3989edb1..2ee565e1 100644 --- a/src/drivers/win/config.cpp +++ b/src/drivers/win/config.cpp @@ -318,7 +318,7 @@ static CFGSTRUCT fceuconfig[] = { AC(taseditor_config.show_branch_descr), AC(taseditor_config.bind_markers), AC(taseditor_config.empty_marker_notes), - AC(taseditor_config.combine_consecutive_rec), + AC(taseditor_config.combine_consecutive), AC(taseditor_config.use_1p_rec), AC(taseditor_config.columnset_by_keys), AC(taseditor_config.superimpose_affects_paste), diff --git a/src/drivers/win/taseditor.cpp b/src/drivers/win/taseditor.cpp index 19c7bde6..c4804df6 100644 --- a/src/drivers/win/taseditor.cpp +++ b/src/drivers/win/taseditor.cpp @@ -39,6 +39,7 @@ RECORDER recorder; GREENZONE greenzone; MARKERS_MANAGER markers_manager; BOOKMARKS bookmarks; +BRANCHES branches; POPUP_DISPLAY popup_display; PIANO_ROLL piano_roll; TASEDITOR_LUA taseditor_lua; @@ -94,6 +95,7 @@ bool EnterTasEditor() markers_manager.init(); project.init(); bookmarks.init(); + branches.init(); popup_display.init(); history.init(); taseditor_lua.init(); @@ -150,6 +152,7 @@ bool ExitTasEditor() markers_manager.free(); greenzone.free(); bookmarks.free(); + branches.free(); popup_display.free(); history.free(); playback.SeekingStop(); @@ -194,6 +197,7 @@ void UpdateTasEditor() markers_manager.update(); playback.update(); bookmarks.update(); + branches.update(); popup_display.update(); selection.update(); splicer.update(); @@ -317,6 +321,7 @@ void NewProject() playback.reset(); playback.StartFromZero(); bookmarks.reset(); + branches.reset(); history.reset(); piano_roll.reset(); selection.reset(); diff --git a/src/drivers/win/taseditor/bookmark.cpp b/src/drivers/win/taseditor/bookmark.cpp index 7a3a21e6..ab1c0efe 100644 --- a/src/drivers/win/taseditor/bookmark.cpp +++ b/src/drivers/win/taseditor/bookmark.cpp @@ -9,7 +9,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI ------------------------------------------------------------------------------------ Bookmark - Single Bookmark data -* stores all info of one specific Bookmark-Branch: movie snapshot, a savestate of 1 frame, a screenshot of the frame, an info about relations of this Branch, a state of flashing for this Bookmark's row +* stores all info of one specific Bookmark: movie snapshot, a savestate of 1 frame, a screenshot of the frame, a state of flashing for this Bookmark's row * saves and loads the data from a project file. On error: sends warning to caller * implements procedure of "Bookmark set": creating movie snapshot, setting key frame on current Playback position, copying savestate from Greenzone, making and compressing screenshot, launching flashing animation * launches respective flashings for "Bookmark jump" and "Branch deploy" @@ -34,7 +34,6 @@ void BOOKMARK::init() not_empty = false; flash_type = flash_phase = 0; snapshot.jump_frame = -1; - parent_branch = -1; // -1 = root (cloud) } void BOOKMARK::set() @@ -78,8 +77,6 @@ void BOOKMARK::save(EMUFILE *os) if (not_empty) { write8le(1, os); - // write parent_branch - write32le(parent_branch, os); // write snapshot snapshot.save(os); // write savestate @@ -100,8 +97,6 @@ bool BOOKMARK::load(EMUFILE *is) not_empty = tmp != 0; if (not_empty) { - // read parent_branch - if (!read32le(&parent_branch, is)) return true; // read snapshot if (snapshot.load(is)) return true; // read savestate diff --git a/src/drivers/win/taseditor/bookmark.h b/src/drivers/win/taseditor/bookmark.h index ac9ec596..3fc676ab 100644 --- a/src/drivers/win/taseditor/bookmark.h +++ b/src/drivers/win/taseditor/bookmark.h @@ -30,7 +30,8 @@ public: SNAPSHOT snapshot; std::vector savestate; std::vector saved_screenshot; - int parent_branch; + + //int parent_branch; // not saved vars int flash_phase; diff --git a/src/drivers/win/taseditor/bookmarks.cpp b/src/drivers/win/taseditor/bookmarks.cpp index 851346e9..fcaf5c73 100644 --- a/src/drivers/win/taseditor/bookmarks.cpp +++ b/src/drivers/win/taseditor/bookmarks.cpp @@ -10,16 +10,13 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI Bookmarks/Branches - Manager of Bookmarks [Singleton] -* stores 10 Bookmarks, info about their relations and the position of current Branch -* also stores the time of the last modification (see fireball) and the time of the root of Branches Tree (see cloudlet) -* implements all operations with Bookmarks: initialization, setting Bookmarks, jumping to Bookmarks, loading Branches -* saves and loads the data from a project file. On error: clears all Bookmarks +* stores 10 Bookmarks +* implements all operations with Bookmarks: initialization, setting Bookmarks, jumping to Bookmarks, deploying Branches +* saves and loads the data from a project file. On error: resets all Bookmarks and Branches * implements the working of Bookmarks List: creating, redrawing, mouseover, clicks -* implements the working of Branches Tree: creating, recalculating relations, animating, redrawing, mouseover -* on demand: reacts on project changes and recalculates Branches Tree -* regularly updates flashings in Bookmarks List and animations in Branches Tree +* regularly updates flashings in Bookmarks List * on demand: updates colors of rows in Bookmarks List, reflecting conditions of respective Piano Roll rows -* stores resources: save id, ids of commands, captions for panel, gradients for flashings, coordinates for building Branches Tree, animation timings, id of default slot +* stores resources: save id, ids of commands, captions for panel, gradients for flashings, id of default slot ------------------------------------------------------------------------------------ */ #include "taseditor_project.h" @@ -29,8 +26,7 @@ Bookmarks/Branches - Manager of Bookmarks #pragma comment(lib, "msimg32.lib") LRESULT APIENTRY BookmarksListWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); -LRESULT APIENTRY BranchesBitmapWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); -WNDPROC hwndBookmarksList_oldWndProc, hwndBranchesBitmap_oldWndProc; +WNDPROC hwndBookmarksList_oldWndProc; extern TASEDITOR_CONFIG taseditor_config; extern TASEDITOR_WINDOW taseditor_window; @@ -42,13 +38,14 @@ extern TASEDITOR_PROJECT project; extern HISTORY history; extern PIANO_ROLL piano_roll; extern MARKERS_MANAGER markers_manager; +extern BRANCHES branches; // resources char bookmarks_save_id[BOOKMARKS_ID_LEN] = "BOOKMARKS"; char bookmarks_skipsave_id[BOOKMARKS_ID_LEN] = "BOOKMARKX"; char bookmarksCaption[3][23] = { " Bookmarks ", " Bookmarks / Branches ", " Branches " }; // color tables for flashing when saving/loading bookmarks -COLORREF bookmark_flash_colors[3][FLASH_PHASE_MAX+1] = { +COLORREF bookmark_flash_colors[TOTAL_COMMANDS][FLASH_PHASE_MAX+1] = { // set //0x122330, 0x1b3541, 0x254753, 0x2e5964, 0x376b75, 0x417e87, 0x4a8f97, 0x53a1a8, 0x5db3b9, 0x66c5cb, 0x70d7dc, 0x79e9ed, 0x0d1241, 0x111853, 0x161e64, 0x1a2575, 0x1f2b87, 0x233197, 0x2837a8, 0x2c3db9, 0x3144cb, 0x354adc, 0x3a50ed, 0x3f57ff, @@ -56,26 +53,24 @@ COLORREF bookmark_flash_colors[3][FLASH_PHASE_MAX+1] = { 0x14350f, 0x1c480f, 0x235a0f, 0x2a6c0f, 0x317f10, 0x38910f, 0x3fa30f, 0x46b50f, 0x4dc80f, 0x54da0f, 0x5bec0f, 0x63ff10, // deploy 0x43171d, 0x541d21, 0x652325, 0x762929, 0x872f2c, 0x983530, 0xa93b34, 0xba4137, 0xcb463b, 0xdc4c3f, 0xed5243, 0xff5947 }; -// corners cursor animation -int corners_cursor_shift[BRANCHES_ANIMATION_FRAMES] = {0, 1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 0 }; BOOKMARKS::BOOKMARKS() { // fill TrackMouseEvent struct tme.cbSize = sizeof(tme); tme.dwFlags = TME_LEAVE; - tme.hwndTrack = hwndBranchesBitmap; + tme.hwndTrack = NULL; list_tme.cbSize = sizeof(tme); list_tme.dwFlags = TME_LEAVE; - list_tme.hwndTrack = hwndBookmarksList; + list_tme.hwndTrack = NULL; } void BOOKMARKS::init() { free(); hwndBookmarksList = GetDlgItem(taseditor_window.hwndTasEditor, IDC_BOOKMARKSLIST); - hwndBookmarks = GetDlgItem(taseditor_window.hwndTasEditor, IDC_BOOKMARKS_BOX); hwndBranchesBitmap = GetDlgItem(taseditor_window.hwndTasEditor, IDC_BRANCHES_BITMAP); + hwndBookmarks = GetDlgItem(taseditor_window.hwndTasEditor, IDC_BOOKMARKS_BOX); // prepare bookmarks listview ListView_SetExtendedListViewStyleEx(hwndBookmarksList, LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES, LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES); @@ -97,100 +92,27 @@ void BOOKMARKS::init() // time column lvc.cx = 80; ListView_InsertColumn(hwndBookmarksList, 2, &lvc); - - // subclass BranchesBitmap - hwndBranchesBitmap_oldWndProc = (WNDPROC)SetWindowLong(hwndBranchesBitmap, GWL_WNDPROC, (LONG)BranchesBitmapWndProc); - - // init arrays - BranchX.resize(TOTAL_BOOKMARKS+1); - BranchY.resize(TOTAL_BOOKMARKS+1); - BranchPrevX.resize(TOTAL_BOOKMARKS+1); - BranchPrevY.resize(TOTAL_BOOKMARKS+1); - BranchCurrX.resize(TOTAL_BOOKMARKS+1); - BranchCurrY.resize(TOTAL_BOOKMARKS+1); - - reset(); ListView_SetItemCountEx(hwndBookmarksList, TOTAL_BOOKMARKS, LVSICF_NOSCROLL | LVSICF_NOINVALIDATEALL); + reset(); // find rows top/height (for mouseover hittest calculations) RECT temp_rect; if (ListView_GetSubItemRect(hwndBookmarksList, 0, 2, LVIR_BOUNDS, &temp_rect) && temp_rect.bottom != temp_rect.top) { - branch_row_top = temp_rect.top; - branch_row_left = temp_rect.left; - branch_row_height = temp_rect.bottom - temp_rect.top; + list_row_top = temp_rect.top; + list_row_left = temp_rect.left; + list_row_height = temp_rect.bottom - temp_rect.top; } else { // couldn't get rect, set default values - branch_row_top = 0; - branch_row_height = 14; + list_row_top = 0; + list_row_height = 14; } - // init GDI stuff - HDC win_hdc = GetWindowDC(hwndBookmarksList); - hBitmapDC = CreateCompatibleDC(win_hdc); - branches_hbitmap = CreateCompatibleBitmap(win_hdc, BRANCHES_BITMAP_WIDTH, BRANCHES_BITMAP_WIDTH); - hOldBitmap = (HBITMAP)SelectObject(hBitmapDC, branches_hbitmap); - hBufferDC = CreateCompatibleDC(win_hdc); - buffer_hbitmap = CreateCompatibleBitmap(win_hdc, BRANCHES_BITMAP_WIDTH, BRANCHES_BITMAP_WIDTH); - hOldBitmap1 = (HBITMAP)SelectObject(hBufferDC, buffer_hbitmap); - normal_brush = CreateSolidBrush(0x000000); - // prepare branches spritesheet - branchesSpritesheet = LoadBitmap(fceu_hInstance, MAKEINTRESOURCE(IDB_BRANCH_SPRITESHEET)); - hSpritesheetDC = CreateCompatibleDC(win_hdc); - hOldBitmap2 = (HBITMAP)SelectObject(hSpritesheetDC, branchesSpritesheet); - // create pens - normal_pen = CreatePen(PS_SOLID, 1, 0x0); - select_pen = CreatePen(PS_SOLID, 2, 0xFF9080); - RedrawBookmarksCaption(); - next_animation_time = 0; - update(); } void BOOKMARKS::free() { bookmarks_array.resize(0); - - BranchX.resize(0); - BranchY.resize(0); - BranchPrevX.resize(0); - BranchPrevY.resize(0); - BranchCurrX.resize(0); - BranchCurrY.resize(0); - - // free GDI stuff - if (hOldBitmap && hBitmapDC) - { - SelectObject(hBitmapDC, hOldBitmap); - DeleteDC(hBitmapDC); - hBitmapDC = NULL; - } - if (branches_hbitmap) - { - DeleteObject(branches_hbitmap); - branches_hbitmap = NULL; - } - if (hOldBitmap1 && hBufferDC) - { - SelectObject(hBufferDC, hOldBitmap1); - DeleteDC(hBufferDC); - hBufferDC = NULL; - } - if (buffer_hbitmap) - { - DeleteObject(buffer_hbitmap); - buffer_hbitmap = NULL; - } - if (hOldBitmap2 && hSpritesheetDC) - { - SelectObject(hSpritesheetDC, hOldBitmap2); - DeleteDC(hSpritesheetDC); - hSpritesheetDC = NULL; - } - if (branchesSpritesheet) - { - DeleteObject(branchesSpritesheet); - branchesSpritesheet = NULL; - } } void BOOKMARKS::reset() { @@ -201,32 +123,15 @@ void BOOKMARKS::reset() bookmarks_array.resize(TOTAL_BOOKMARKS); for (int i = 0; i < TOTAL_BOOKMARKS; ++i) bookmarks_array[i].init(); - - for (int i = TOTAL_BOOKMARKS; i >= 0; i--) - { - BranchX[i] = BranchPrevX[i] = BranchCurrX[i] = EMPTY_BRANCHES_X; - BranchY[i] = BranchPrevY[i] = BranchCurrY[i] = EMPTY_BRANCHES_Y_BASE + EMPTY_BRANCHES_Y_FACTOR * ((i + TOTAL_BOOKMARKS - 1) % TOTAL_BOOKMARKS); - } - CursorX = CursorPrevX = CloudX = CloudPrevX = BRANCHES_CLOUD_X; - CursorY = CursorPrevY = BRANCHES_CLOUD_Y; reset_vars(); - current_branch = -1; // -1 = root - changes_since_current_branch = false; - fireball_size = 0; - - // set cloud_time and current_pos_time - SetCurrentPosTime(); - strcpy(cloud_time, current_pos_time); } void BOOKMARKS::reset_vars() { - transition_phase = animation_frame = 0; mouse_x = mouse_y = -1; item_under_mouse = ITEM_UNDER_MOUSE_NONE; mouse_over_bitmap = false; - must_recalculate_branches_tree = must_redraw_branches_tree = must_check_item_under_mouse = true; + must_check_item_under_mouse = true; check_flash_shedule = clock() + BOOKMARKS_FLASH_TICK; - next_animation_time = clock() + BRANCHES_ANIMATION_TICK; } void BOOKMARKS::update() @@ -261,55 +166,29 @@ void BOOKMARKS::update() { bookmarks_array[i].flash_phase--; RedrawBookmarksRow((i + TOTAL_BOOKMARKS - 1) % TOTAL_BOOKMARKS); - must_redraw_branches_tree = true; // because borders of some branch digit has changed + branches.must_redraw_branches_tree = true; // because border of some branch digit has changed } } } - if (must_recalculate_branches_tree) - RecalculateBranchesTree(); - - // once per 50 milliseconds update branches_bitmap - if (clock() > next_animation_time) + // controls + if (must_check_item_under_mouse) { - // animate branches_bitmap - next_animation_time = clock() + BRANCHES_ANIMATION_TICK; - animation_frame = (animation_frame + 1) % BRANCHES_ANIMATION_FRAMES; if (edit_mode == EDIT_MODE_BRANCHES) - { - // grow or shrink fireball size - if (changes_since_current_branch) - { - fireball_size++; - if (fireball_size > BRANCHES_FIREBALL_MAX_SIZE) fireball_size = BRANCHES_FIREBALL_MAX_SIZE; - } else - { - fireball_size--; - if (fireball_size < 0) fireball_size = 0; - } - // also update transition from old to new tree - if (transition_phase) - { - transition_phase--; - must_check_item_under_mouse = must_redraw_branches_tree = true; - } else if (!must_redraw_branches_tree) - { - // just update sprites - InvalidateRect(hwndBranchesBitmap, 0, FALSE); - } - } - // controls - if (must_check_item_under_mouse) - CheckMousePos(); - // render branches_bitmap - if (edit_mode == EDIT_MODE_BRANCHES && must_redraw_branches_tree) - RedrawBranchesTree(); + branches.FindItemUnderMouse(mouse_x, mouse_y); + else if (edit_mode == EDIT_MODE_BOTH) + FindItemUnderMouse(); + else + item_under_mouse = ITEM_UNDER_MOUSE_NONE; + must_check_item_under_mouse = false; } } // stores commands in array for update() function void BOOKMARKS::command(int command_id, int slot) { + if (slot < 0) + slot = branches.GetCurrentBranch(); switch (command_id) { case COMMAND_SET: @@ -337,7 +216,7 @@ void BOOKMARKS::set(int slot) { if (slot < 0 || slot >= TOTAL_BOOKMARKS) return; - // First save edited note (in case it's being currently edited) + // First save changes in edited note (in case it's being currently edited) markers_manager.UpdateMarkerNote(); int previous_frame = bookmarks_array[slot].snapshot.jump_frame; @@ -351,115 +230,18 @@ void BOOKMARKS::set(int slot) { saved_time[0] = 0; } - + // write current MovieData to the slot bookmarks_array[slot].set(); - - // if this screenshot is currently shown - reinit and redraw it - if (popup_display.screenshot_currently_shown == slot) - popup_display.screenshot_currently_shown = ITEM_UNDER_MOUSE_NONE; - - int parent; - // inherit current branch - if (slot != current_branch) + // find its new place in Branches Tree + int old_current_branch = branches.GetCurrentBranch(); + branches.HandleBookmarkSet(slot, saved_time); + if (slot != old_current_branch && old_current_branch != ITEM_UNDER_MOUSE_CLOUD) { - parent = bookmarks_array[slot].parent_branch; - if (parent == -1 && saved_time[0]) - { - // check if this was the only child of cloud parent, if so then set cloud time to the saved_time - int i = 0; - for (; i < TOTAL_BOOKMARKS; ++i) - { - if (bookmarks_array[i].not_empty && bookmarks_array[i].parent_branch == -1 && i != slot) - break; - } - if (i >= TOTAL_BOOKMARKS) - // didn't find another child of cloud, change cloud's time, sice "root" has shifted - strcpy(cloud_time, saved_time); - } - // before disconnecting from old parent, connect all childs to the old parent - for (int i = 0; i < TOTAL_BOOKMARKS; ++i) - { - if (bookmarks_array[i].not_empty && bookmarks_array[i].parent_branch == slot) - bookmarks_array[i].parent_branch = parent; - } - bookmarks_array[slot].parent_branch = current_branch; + // current_branch was switched to slot, redraw Bookmarks List to change the color of digits + piano_roll.RedrawRow(bookmarks_array[old_current_branch].snapshot.jump_frame); + RedrawChangedBookmarks(bookmarks_array[old_current_branch].snapshot.jump_frame); } - - // if parent is invalid (first_change < parent.jump_frame) then find better parent - int factor; - // also if parent == cloud, then try to find better parent - parent = bookmarks_array[slot].parent_branch; - if (parent >= 0) - factor = bookmarks_array[slot].snapshot.findFirstChange(bookmarks_array[parent].snapshot); - if (parent < 0 || (factor >= 0 && factor < bookmarks_array[parent].snapshot.jump_frame)) - { - // find highest frame of change - std::vector DecisiveFactor(TOTAL_BOOKMARKS); - int best_branch = -1; - for (int i = TOTAL_BOOKMARKS-1; i >= 0; i--) - { - if (i != slot && i != parent && bookmarks_array[i].not_empty && bookmarks_array[slot].snapshot.size >= bookmarks_array[i].snapshot.jump_frame) - { - factor = bookmarks_array[slot].snapshot.findFirstChange(bookmarks_array[i].snapshot); - if (factor < 0) - { - // this branch is identical to this slot - DecisiveFactor[i] = 2 * bookmarks_array[i].snapshot.size; - } else if (factor >= bookmarks_array[i].snapshot.jump_frame) - { - // hey, this branch could be our new parent... - DecisiveFactor[i] = 2 * factor; - } else - DecisiveFactor[i] = 0; - } else - { - DecisiveFactor[i] = 0; - } - } - // add +1 as a bonus to current parents and grandparents (a bit of nepotism!) - while (parent >= 0) - { - if (DecisiveFactor[parent]) - DecisiveFactor[parent]++; - parent = bookmarks_array[parent].parent_branch; - } - // find max - factor = 0; - for (int i = TOTAL_BOOKMARKS-1; i >= 0; i--) - { - if (DecisiveFactor[i] && DecisiveFactor[i] > factor) - { - factor = DecisiveFactor[i]; - best_branch = i; - } - } - parent = bookmarks_array[slot].parent_branch; - if (parent != best_branch) - { - // before disconnecting from old parent, connect all childs to the old parent - for (int i = 0; i < TOTAL_BOOKMARKS; ++i) - { - if (bookmarks_array[i].not_empty && bookmarks_array[i].parent_branch == slot) - bookmarks_array[i].parent_branch = parent; - } - // found new parent - bookmarks_array[slot].parent_branch = best_branch; - must_recalculate_branches_tree = true; - } - } - - // switch current branch to this branch - if (slot != current_branch && current_branch >= 0) - { - piano_roll.RedrawRow(bookmarks_array[current_branch].snapshot.jump_frame); - RedrawChangedBookmarks(bookmarks_array[current_branch].snapshot.jump_frame); - } - if (slot != current_branch || changes_since_current_branch) - must_recalculate_branches_tree = true; - current_branch = slot; - changes_since_current_branch = false; - project.SetProjectChanged(); - + // also redraw List rows if (previous_frame >= 0 && previous_frame != currFrameCounter) { piano_roll.RedrawRow(previous_frame); @@ -467,7 +249,11 @@ void BOOKMARKS::set(int slot) } piano_roll.RedrawRow(currFrameCounter); RedrawChangedBookmarks(currFrameCounter); + // if screenshot of the slot is currently shown - reinit and redraw the picture + if (popup_display.screenshot_currently_shown == slot) + popup_display.screenshot_currently_shown = ITEM_UNDER_MOUSE_NONE; + project.SetProjectChanged(); FCEU_DispMessage("Branch %d saved.", 0, slot); } @@ -587,20 +373,18 @@ void BOOKMARKS::deploy(int slot) greenzone.update(); // switch current branch to this branch - if (slot != current_branch && current_branch >= 0) + int old_current_branch = branches.GetCurrentBranch(); + branches.HandleBookmarkDeploy(slot); + if (slot != old_current_branch && old_current_branch != ITEM_UNDER_MOUSE_CLOUD) { - piano_roll.RedrawRow(bookmarks_array[current_branch].snapshot.jump_frame); - RedrawChangedBookmarks(bookmarks_array[current_branch].snapshot.jump_frame); + piano_roll.RedrawRow(bookmarks_array[old_current_branch].snapshot.jump_frame); + RedrawChangedBookmarks(bookmarks_array[old_current_branch].snapshot.jump_frame); piano_roll.RedrawRow(bookmarks_array[slot].snapshot.jump_frame); RedrawChangedBookmarks(bookmarks_array[slot].snapshot.jump_frame); } - current_branch = slot; - changes_since_current_branch = false; - must_recalculate_branches_tree = true; // jump to the target (bookmarked frame) playback.jump(jump_frame); - FCEU_DispMessage("Branch %d loaded.", 0, slot); } @@ -610,21 +394,11 @@ void BOOKMARKS::save(EMUFILE *os, bool really_save) { // write "BOOKMARKS" string os->fwrite(bookmarks_save_id, BOOKMARKS_ID_LEN); - // write cloud time - os->fwrite(cloud_time, TIME_DESC_LENGTH); - // write current branch and flag of changes since it - write32le(current_branch, os); - if (changes_since_current_branch) - write8le((uint8)1, os); - else - write8le((uint8)0, os); - // write current_position time - os->fwrite(current_pos_time, TIME_DESC_LENGTH); // write all 10 bookmarks for (int i = 0; i < TOTAL_BOOKMARKS; ++i) - { bookmarks_array[i].save(os); - } + // write branches + branches.save(os); } else { // write "BOOKMARKX" string @@ -640,25 +414,17 @@ bool BOOKMARKS::load(EMUFILE *is) if (!strcmp(bookmarks_skipsave_id, save_id)) { // string says to skip loading Bookmarks - FCEU_printf("No bookmarks in the file\n"); + FCEU_printf("No Bookmarks in the file\n"); reset(); + branches.reset(); return false; } if (strcmp(bookmarks_save_id, save_id)) goto error; // string is not valid - // read cloud time - if ((int)is->fread(cloud_time, TIME_DESC_LENGTH) < TIME_DESC_LENGTH) goto error; - // read current branch and flag of changes since it - uint8 tmp; - if (!read32le(¤t_branch, is)) goto error; - if (!read8le(&tmp, is)) goto error; - changes_since_current_branch = (tmp != 0); - // read current_position time - if ((int)is->fread(current_pos_time, TIME_DESC_LENGTH) < TIME_DESC_LENGTH) goto error; // read all 10 bookmarks for (int i = 0; i < TOTAL_BOOKMARKS; ++i) - { if (bookmarks_array[i].load(is)) goto error; - } + // read branches + if (branches.load(is)) goto error; // all ok reset_vars(); RedrawBookmarksCaption(); @@ -666,6 +432,7 @@ bool BOOKMARKS::load(EMUFILE *is) error: FCEU_printf("Error loading bookmarks\n"); reset(); + branches.reset(); return true; } // ---------------------------------------------------------- @@ -712,266 +479,24 @@ void BOOKMARKS::RedrawBookmarksRow(int index) ListView_RedrawItems(hwndBookmarksList, index, index); } -void BOOKMARKS::RedrawBranchesTree() -{ - // draw background gradient - TRIVERTEX vertex[2] ; - vertex[0].x = 0; - vertex[0].y = 0; - vertex[0].Red = 0xC700; - vertex[0].Green = 0xE700; - vertex[0].Blue = 0xF300; - vertex[0].Alpha = 0x0000; - vertex[1].x = BRANCHES_BITMAP_WIDTH; - vertex[1].y = BRANCHES_BITMAP_HEIGHT; - vertex[1].Red = 0xEB00; - vertex[1].Green = 0xFA00; - vertex[1].Blue = 0xF800; - vertex[1].Alpha = 0x0000; - GRADIENT_RECT gRect; - gRect.UpperLeft = 0; - gRect.LowerRight = 1; - GradientFill(hBitmapDC, vertex, 2, &gRect, 1, GRADIENT_FILL_RECT_H); - - // lines - int branch_x, branch_y, parent_x, parent_y, child_id; - SelectObject(hBitmapDC, normal_pen); - for (int t = Children.size() - 1; t >= 0; t--) - { - if (t > 0) - { - parent_x = BranchCurrX[t-1]; - parent_y = BranchCurrY[t-1]; - } else - { - parent_x = cloud_x; - parent_y = BRANCHES_CLOUD_Y; - } - for (int i = Children[t].size() - 1; i >= 0; i--) - { - child_id = Children[t][i]; - if (child_id < TOTAL_BOOKMARKS) - { - MoveToEx(hBitmapDC, parent_x, parent_y, 0); - LineTo(hBitmapDC, BranchCurrX[child_id], BranchCurrY[child_id]); - } - } - } - // lines for item under mouse - SelectObject(hBitmapDC, select_pen); - int branch = item_under_mouse; - if (item_under_mouse == TOTAL_BOOKMARKS) - branch = current_branch; - while (branch >= 0) - { - branch_x = BranchCurrX[branch]; - branch_y = BranchCurrY[branch]; - MoveToEx(hBitmapDC, branch_x, branch_y, 0); - branch = bookmarks_array[branch].parent_branch; - if (branch >= 0) - { - branch_x = BranchCurrX[branch]; - branch_y = BranchCurrY[branch]; - } else - { - branch_x = cloud_x; - branch_y = BRANCHES_CLOUD_Y; - } - LineTo(hBitmapDC, branch_x, branch_y); - } - if (changes_since_current_branch) - { - if (item_under_mouse != TOTAL_BOOKMARKS) - SelectObject(hBitmapDC, normal_pen); - if (current_branch >= 0) - { - parent_x = BranchCurrX[current_branch]; - parent_y = BranchCurrY[current_branch]; - } else - { - parent_x = cloud_x; - parent_y = BRANCHES_CLOUD_Y; - } - MoveToEx(hBitmapDC, parent_x, parent_y, 0); - branch_x = BranchCurrX[TOTAL_BOOKMARKS]; - branch_y = BranchCurrY[TOTAL_BOOKMARKS]; - LineTo(hBitmapDC, branch_x, branch_y); - } - - // cloud - TransparentBlt(hBitmapDC, cloud_x - BRANCHES_CLOUD_HALFWIDTH, BRANCHES_CLOUD_Y - BRANCHES_CLOUD_HALFHEIGHT, BRANCHES_CLOUD_WIDTH, BRANCHES_CLOUD_HEIGHT, hSpritesheetDC, BRANCHES_CLOUD_SPRITESHEET_X, BRANCHES_CLOUD_SPRITESHEET_Y, BRANCHES_CLOUD_WIDTH, BRANCHES_CLOUD_HEIGHT, 0x00FF00); - - // branches rectangles - for (int i = 0; i < TOTAL_BOOKMARKS; ++i) - { - temp_rect.left = BranchCurrX[i] - DIGIT_RECT_HALFWIDTH; - temp_rect.top = BranchCurrY[i] - DIGIT_RECT_HALFHEIGHT; - temp_rect.right = temp_rect.left + DIGIT_RECT_WIDTH; - temp_rect.bottom = temp_rect.top + DIGIT_RECT_HEIGHT; - if (bookmarks_array[i].flash_phase) - { - // draw colored rect - HBRUSH color_brush = CreateSolidBrush(bookmark_flash_colors[bookmarks_array[i].flash_type][bookmarks_array[i].flash_phase]); - FillRect(hBitmapDC, &temp_rect, color_brush); - DeleteObject(color_brush); - } else - { - // draw black rect - FillRect(hBitmapDC, &temp_rect, normal_brush); - } - } - // digits - for (int i = 0; i < TOTAL_BOOKMARKS; ++i) - { - branch_x = BranchCurrX[i] - DIGIT_BITMAP_HALFWIDTH; - branch_y = BranchCurrY[i] - DIGIT_BITMAP_HALFHEIGHT; - if(i == current_branch) - { - if (i == item_under_mouse) - BitBlt(hBitmapDC, branch_x, branch_y, DIGIT_BITMAP_WIDTH, DIGIT_BITMAP_HEIGHT, hSpritesheetDC, i * DIGIT_BITMAP_WIDTH + BLUE_DIGITS_SPRITESHEET_DX, MOUSEOVER_DIGITS_SPRITESHEET_DY, SRCCOPY); - else - BitBlt(hBitmapDC, branch_x, branch_y, DIGIT_BITMAP_WIDTH, DIGIT_BITMAP_HEIGHT, hSpritesheetDC, i * DIGIT_BITMAP_WIDTH + BLUE_DIGITS_SPRITESHEET_DX, 0, SRCCOPY); - } else - { - if (i == item_under_mouse) - BitBlt(hBitmapDC, branch_x, branch_y, DIGIT_BITMAP_WIDTH, DIGIT_BITMAP_HEIGHT, hSpritesheetDC, i * DIGIT_BITMAP_WIDTH, MOUSEOVER_DIGITS_SPRITESHEET_DY, SRCCOPY); - else - BitBlt(hBitmapDC, branch_x, branch_y, DIGIT_BITMAP_WIDTH, DIGIT_BITMAP_HEIGHT, hSpritesheetDC, i * DIGIT_BITMAP_WIDTH, 0, SRCCOPY); - } - } - SetBkMode(hBitmapDC, TRANSPARENT); - // 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); - SetTextColor(hBitmapDC, BRANCHES_TEXT_SHADOW_COLOR); - TextOut(hBitmapDC, BRANCHES_BITMAP_FRAMENUM_X + 1, BRANCHES_BITMAP_FRAMENUM_Y + 1, (LPCSTR)framenum_string, DIGITS_IN_FRAMENUM); - SetTextColor(hBitmapDC, BRANCHES_TEXT_COLOR); - 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) - { - // draw shadow of text - SetTextColor(hBitmapDC, BRANCHES_TEXT_SHADOW_COLOR); - TextOut(hBitmapDC, BRANCHES_BITMAP_TIME_X + 1, BRANCHES_BITMAP_TIME_Y + 1, (LPCSTR)cloud_time, TIME_DESC_LENGTH-1); - SetTextColor(hBitmapDC, BRANCHES_TEXT_COLOR); - TextOut(hBitmapDC, BRANCHES_BITMAP_TIME_X, BRANCHES_BITMAP_TIME_Y, (LPCSTR)cloud_time, TIME_DESC_LENGTH-1); - } else if (item_under_mouse < TOTAL_BOOKMARKS) - { - SetTextColor(hBitmapDC, BRANCHES_TEXT_SHADOW_COLOR); - TextOut(hBitmapDC, BRANCHES_BITMAP_TIME_X + 1, BRANCHES_BITMAP_TIME_Y + 1, (LPCSTR)bookmarks_array[item_under_mouse].snapshot.description, TIME_DESC_LENGTH-1); - SetTextColor(hBitmapDC, BRANCHES_TEXT_COLOR); - TextOut(hBitmapDC, BRANCHES_BITMAP_TIME_X, BRANCHES_BITMAP_TIME_Y, (LPCSTR)bookmarks_array[item_under_mouse].snapshot.description, TIME_DESC_LENGTH-1); - } else // fireball - current_pos_time - { - SetTextColor(hBitmapDC, BRANCHES_TEXT_SHADOW_COLOR); - TextOut(hBitmapDC, BRANCHES_BITMAP_TIME_X + 1, BRANCHES_BITMAP_TIME_Y + 1, (LPCSTR)current_pos_time, TIME_DESC_LENGTH-1); - SetTextColor(hBitmapDC, BRANCHES_TEXT_COLOR); - TextOut(hBitmapDC, BRANCHES_BITMAP_TIME_X, BRANCHES_BITMAP_TIME_Y, (LPCSTR)current_pos_time, TIME_DESC_LENGTH-1); - } - } - // finished - must_redraw_branches_tree = false; - InvalidateRect(hwndBranchesBitmap, 0, FALSE); -} - -// this is called by wndproc on WM_PAINT -void BOOKMARKS::PaintBranchesBitmap(HDC hdc) -{ - int branch_x, branch_y; - // "bg" - BitBlt(hBufferDC, 0, 0, BRANCHES_BITMAP_WIDTH, BRANCHES_BITMAP_HEIGHT, hBitmapDC, 0, 0, SRCCOPY); - // "sprites" - // fireball - if (fireball_size) - { - branch_x = BranchCurrX[TOTAL_BOOKMARKS] - BRANCHES_FIREBALL_HALFWIDTH; - branch_y = BranchCurrY[TOTAL_BOOKMARKS] - BRANCHES_FIREBALL_HALFHEIGHT; - if (fireball_size >= BRANCHES_FIREBALL_MAX_SIZE) - { - TransparentBlt(hBufferDC, branch_x, branch_y, BRANCHES_FIREBALL_WIDTH, BRANCHES_FIREBALL_HEIGHT, hSpritesheetDC, animation_frame * BRANCHES_FIREBALL_WIDTH + BRANCHES_FIREBALL_SPRITESHEET_X, BRANCHES_FIREBALL_SPRITESHEET_Y, BRANCHES_FIREBALL_WIDTH, BRANCHES_FIREBALL_HEIGHT, 0x00FF00); - } else - { - TransparentBlt(hBufferDC, branch_x, branch_y, BRANCHES_FIREBALL_WIDTH, BRANCHES_FIREBALL_HEIGHT, hSpritesheetDC, BRANCHES_FIREBALL_SPRITESHEET_END_X - fireball_size * BRANCHES_FIREBALL_WIDTH, BRANCHES_FIREBALL_SPRITESHEET_Y, BRANCHES_FIREBALL_WIDTH, BRANCHES_FIREBALL_HEIGHT, 0x00FF00); - } - } - // corners cursor - branch_x = (CursorX * (BRANCHES_TRANSITION_MAX - transition_phase) + CursorPrevX * transition_phase) / BRANCHES_TRANSITION_MAX; - branch_y = (CursorY * (BRANCHES_TRANSITION_MAX - transition_phase) + CursorPrevY * transition_phase) / BRANCHES_TRANSITION_MAX; - int current_corners_cursor_shift = BRANCHES_CORNER_BASE_SHIFT + corners_cursor_shift[animation_frame]; - int corner_x, corner_y; - // upper left - corner_x = branch_x - current_corners_cursor_shift - BRANCHES_CORNER_HALFWIDTH; - corner_y = branch_y - current_corners_cursor_shift - BRANCHES_CORNER_HALFHEIGHT; - TransparentBlt(hBufferDC, corner_x, corner_y, BRANCHES_CORNER_WIDTH, BRANCHES_CORNER_HEIGHT, hSpritesheetDC, BRANCHES_CORNER1_SPRITESHEET_X, BRANCHES_CORNER1_SPRITESHEET_Y, BRANCHES_CORNER_WIDTH, BRANCHES_CORNER_HEIGHT, 0x00FF00); - // upper right - corner_x = branch_x + current_corners_cursor_shift - BRANCHES_CORNER_HALFWIDTH; - corner_y = branch_y - current_corners_cursor_shift - BRANCHES_CORNER_HALFHEIGHT; - TransparentBlt(hBufferDC, corner_x, corner_y, BRANCHES_CORNER_WIDTH, BRANCHES_CORNER_HEIGHT, hSpritesheetDC, BRANCHES_CORNER2_SPRITESHEET_X, BRANCHES_CORNER2_SPRITESHEET_Y, BRANCHES_CORNER_WIDTH, BRANCHES_CORNER_HEIGHT, 0x00FF00); - // lower left - corner_x = branch_x - current_corners_cursor_shift - BRANCHES_CORNER_HALFWIDTH; - corner_y = branch_y + current_corners_cursor_shift - BRANCHES_CORNER_HALFHEIGHT; - TransparentBlt(hBufferDC, corner_x, corner_y, BRANCHES_CORNER_WIDTH, BRANCHES_CORNER_HEIGHT, hSpritesheetDC, BRANCHES_CORNER3_SPRITESHEET_X, BRANCHES_CORNER3_SPRITESHEET_Y, BRANCHES_CORNER_WIDTH, BRANCHES_CORNER_HEIGHT, 0x00FF00); - // lower right - corner_x = branch_x + current_corners_cursor_shift - BRANCHES_CORNER_HALFWIDTH; - corner_y = branch_y + current_corners_cursor_shift - BRANCHES_CORNER_HALFHEIGHT; - TransparentBlt(hBufferDC, corner_x, corner_y, BRANCHES_CORNER_WIDTH, BRANCHES_CORNER_HEIGHT, hSpritesheetDC, BRANCHES_CORNER4_SPRITESHEET_X, BRANCHES_CORNER4_SPRITESHEET_Y, BRANCHES_CORNER_WIDTH, BRANCHES_CORNER_HEIGHT, 0x00FF00); - // finish - paste buffer bitmap to screen - BitBlt(hdc, 0, 0, BRANCHES_BITMAP_WIDTH, BRANCHES_BITMAP_HEIGHT, hBufferDC, 0, 0, SRCCOPY); -} - void BOOKMARKS::MouseMove(int new_x, int new_y) { mouse_x = new_x; mouse_y = new_y; must_check_item_under_mouse = true; } -void BOOKMARKS::CheckMousePos() +void BOOKMARKS::FindItemUnderMouse() { - int prev_item_under_mouse = item_under_mouse; - if (edit_mode == EDIT_MODE_BRANCHES) + if (mouse_x > list_row_left) { - // Mouse over Branches bitmap - // first calculate current positions of branch items - for (int i = 0; i <= TOTAL_BOOKMARKS; ++i) + item_under_mouse = (mouse_y - list_row_top) / list_row_height; + if (item_under_mouse >= 0 && item_under_mouse < TOTAL_BOOKMARKS) { - BranchCurrX[i] = (BranchX[i] * (BRANCHES_TRANSITION_MAX - transition_phase) + BranchPrevX[i] * transition_phase) / BRANCHES_TRANSITION_MAX; - BranchCurrY[i] = (BranchY[i] * (BRANCHES_TRANSITION_MAX - transition_phase) + BranchPrevY[i] * transition_phase) / BRANCHES_TRANSITION_MAX; + item_under_mouse = (item_under_mouse + 1) % TOTAL_BOOKMARKS; + if (!bookmarks_array[item_under_mouse].not_empty) + item_under_mouse = ITEM_UNDER_MOUSE_NONE; } - cloud_x = (CloudX * (BRANCHES_TRANSITION_MAX - transition_phase) + CloudPrevX * transition_phase) / BRANCHES_TRANSITION_MAX; - // find item under mouse - item_under_mouse = ITEM_UNDER_MOUSE_NONE; - for (int i = 0; i < TOTAL_BOOKMARKS; ++i) - if (item_under_mouse == ITEM_UNDER_MOUSE_NONE && mouse_x >= BranchCurrX[i] - DIGIT_RECT_HALFWIDTH_COLLISION && mouse_x < BranchCurrX[i] - DIGIT_RECT_HALFWIDTH_COLLISION + DIGIT_RECT_WIDTH_COLLISION && mouse_y >= BranchCurrY[i] - DIGIT_RECT_HALFHEIGHT_COLLISION && mouse_y < BranchCurrY[i] - DIGIT_RECT_HALFHEIGHT_COLLISION + DIGIT_RECT_HEIGHT_COLLISION) - item_under_mouse = i; - if (item_under_mouse == ITEM_UNDER_MOUSE_NONE && mouse_x >= cloud_x - BRANCHES_CLOUD_HALFWIDTH && mouse_x < cloud_x - BRANCHES_CLOUD_HALFWIDTH + BRANCHES_CLOUD_WIDTH && mouse_y >= BRANCHES_CLOUD_Y - BRANCHES_CLOUD_HALFHEIGHT && mouse_y < BRANCHES_CLOUD_Y - BRANCHES_CLOUD_HALFHEIGHT + BRANCHES_CLOUD_HEIGHT) - item_under_mouse = ITEM_UNDER_MOUSE_CLOUD; - if (item_under_mouse == ITEM_UNDER_MOUSE_NONE && changes_since_current_branch && mouse_x >= BranchCurrX[TOTAL_BOOKMARKS] - DIGIT_RECT_HALFWIDTH_COLLISION && mouse_x < BranchCurrX[TOTAL_BOOKMARKS] - DIGIT_RECT_HALFWIDTH_COLLISION + DIGIT_RECT_WIDTH_COLLISION && mouse_y >= BranchCurrY[TOTAL_BOOKMARKS] - DIGIT_RECT_HALFHEIGHT_COLLISION && mouse_y < BranchCurrY[TOTAL_BOOKMARKS] - DIGIT_RECT_HALFHEIGHT_COLLISION + DIGIT_RECT_HEIGHT_COLLISION) - item_under_mouse = TOTAL_BOOKMARKS; - if (prev_item_under_mouse != item_under_mouse) - must_redraw_branches_tree = true; - } else if (edit_mode == EDIT_MODE_BOTH) - { - // Mouse over Bookmarks list - if (mouse_x > branch_row_left) - { - item_under_mouse = (mouse_y - branch_row_top) / branch_row_height; - if (item_under_mouse >= 0 && item_under_mouse < TOTAL_BOOKMARKS) - { - item_under_mouse = (item_under_mouse + 1) % TOTAL_BOOKMARKS; - if (!bookmarks_array[item_under_mouse].not_empty) - item_under_mouse = ITEM_UNDER_MOUSE_NONE; - } - } else item_under_mouse = ITEM_UNDER_MOUSE_NONE; - } else item_under_mouse = ITEM_UNDER_MOUSE_NONE; - must_check_item_under_mouse = false; + } } // ---------------------------------------------------------------------------------------- void BOOKMARKS::GetDispInfo(NMLVDISPINFO* nmlvDispInfo) @@ -983,7 +508,7 @@ void BOOKMARKS::GetDispInfo(NMLVDISPINFO* nmlvDispInfo) { case BOOKMARKS_COLUMN_ICON: { - if ((item.iItem + 1) % TOTAL_BOOKMARKS == current_branch) + if ((item.iItem + 1) % TOTAL_BOOKMARKS == branches.GetCurrentBranch()) item.iImage = ((item.iItem + 1) % TOTAL_BOOKMARKS) + TOTAL_BOOKMARKS; else item.iImage = (item.iItem + 1) % TOTAL_BOOKMARKS; @@ -1109,316 +634,14 @@ void BOOKMARKS::RightClick(int column_index, int row_index) int BOOKMARKS::FindBookmarkAtFrame(int frame) { - if (current_branch >= 0 && bookmarks_array[current_branch].snapshot.jump_frame == frame) return current_branch + TOTAL_BOOKMARKS; // blue digit - + int current_branch = branches.GetCurrentBranch(); + if (current_branch >= 0 && bookmarks_array[current_branch].snapshot.jump_frame == frame) + return current_branch + TOTAL_BOOKMARKS; // blue digit has highest priority when drawing for (int i = 0; i < TOTAL_BOOKMARKS; ++i) { if (bookmarks_array[i].snapshot.jump_frame == frame) return i; // green digit } - return -1; -} -int BOOKMARKS::GetCurrentBranch() -{ - return current_branch; -} - -void BOOKMARKS::ChangesMadeSinceBranch() -{ - bool prev_changes_since_current_branch = changes_since_current_branch; - changes_since_current_branch = true; - SetCurrentPosTime(); - // recalculate branch tree if previous_changes = false - if (!prev_changes_since_current_branch) - must_recalculate_branches_tree = true; - else if (edit_mode == EDIT_MODE_BRANCHES && item_under_mouse == TOTAL_BOOKMARKS) - // redraw displayed time if it's currently shown - must_redraw_branches_tree = true; -} - -void BOOKMARKS::SetCurrentPosTime() -{ - time_t raw_time; - time(&raw_time); - struct tm * timeinfo = localtime(&raw_time); - strftime(current_pos_time, TIME_DESC_LENGTH, "%H:%M:%S", timeinfo); -} - -void BOOKMARKS::RecalculateBranchesTree() -{ - // save previous values - for (int i = TOTAL_BOOKMARKS; i >= 0; i--) - { - BranchPrevX[i] = (BranchX[i] * (BRANCHES_TRANSITION_MAX - transition_phase) + BranchPrevX[i] * transition_phase) / BRANCHES_TRANSITION_MAX; - BranchPrevY[i] = (BranchY[i] * (BRANCHES_TRANSITION_MAX - transition_phase) + BranchPrevY[i] * transition_phase) / BRANCHES_TRANSITION_MAX; - } - CloudPrevX = (CloudX * (BRANCHES_TRANSITION_MAX - transition_phase) + CloudPrevX * transition_phase) / BRANCHES_TRANSITION_MAX; - CursorPrevX = (CursorX * (BRANCHES_TRANSITION_MAX - transition_phase) + CursorPrevX * transition_phase) / BRANCHES_TRANSITION_MAX; - CursorPrevY = (CursorY * (BRANCHES_TRANSITION_MAX - transition_phase) + CursorPrevY * transition_phase) / BRANCHES_TRANSITION_MAX; - transition_phase = BRANCHES_TRANSITION_MAX; - - // 0. Prepare arrays - GridX.resize(0); - GridY.resize(0); - Children.resize(0); - GridHeight.resize(0); - GridX.resize(TOTAL_BOOKMARKS+1); - GridY.resize(TOTAL_BOOKMARKS+1); - Children.resize(TOTAL_BOOKMARKS+2); // 0th subvector is for cloud's children - GridHeight.resize(TOTAL_BOOKMARKS+1); - for (int i = TOTAL_BOOKMARKS; i >= 0; i--) - GridHeight[i] = 1; - - // 1. Define GridX of branches (distribute to levels) and GridHeight of branches - int current_grid_x = 0; - std::vector> BranchesLevels; - - std::vector UndistributedBranches; - UndistributedBranches.resize(TOTAL_BOOKMARKS); - for (int i = UndistributedBranches.size()-1; i >= 0; i--) - UndistributedBranches[i] = i; - // remove all empty branches - for (int i = UndistributedBranches.size()-1; i >= 0; i--) - { - if (!bookmarks_array[UndistributedBranches[i]].not_empty) - UndistributedBranches.erase(UndistributedBranches.begin() + i); - } - // highest level: cloud (id = -1) - BranchesLevels.resize(current_grid_x+1); - BranchesLevels[current_grid_x].resize(1); - BranchesLevels[current_grid_x][0] = -1; - // go lower until all branches are arranged to levels - int current_parent; - while(UndistributedBranches.size()) - { - current_grid_x++; - BranchesLevels.resize(current_grid_x+1); - BranchesLevels[current_grid_x].resize(0); - for (int t = BranchesLevels[current_grid_x-1].size()-1; t >= 0; t--) - { - current_parent = BranchesLevels[current_grid_x-1][t]; - for (int i = UndistributedBranches.size()-1; i >= 0; i--) - { - if (bookmarks_array[UndistributedBranches[i]].parent_branch == current_parent) - { - // assign this branch to current level - GridX[UndistributedBranches[i]] = current_grid_x; - BranchesLevels[current_grid_x].push_back(UndistributedBranches[i]); - // also add it to parent's Children array - Children[current_parent+1].push_back(UndistributedBranches[i]); - UndistributedBranches.erase(UndistributedBranches.begin() + i); - } - } - if (current_parent >= 0) - { - GridHeight[current_parent] = Children[current_parent+1].size(); - if (Children[current_parent+1].size() > 1) - RecursiveAddHeight(bookmarks_array[current_parent].parent_branch, GridHeight[current_parent] - 1); - else - GridHeight[current_parent] = 1; // its own height - } - } - } - if (changes_since_current_branch) - { - // also define "current_pos" GridX - if (current_branch >= 0) - { - if (Children[current_branch+1].size() < MAX_NUM_CHILDREN_ON_CANVAS_HEIGHT) - { - // "current_pos" becomes a child of current_branch - GridX[TOTAL_BOOKMARKS] = GridX[current_branch] + 1; - if ((int)BranchesLevels.size() <= GridX[TOTAL_BOOKMARKS]) - BranchesLevels.resize(GridX[TOTAL_BOOKMARKS] + 1); - BranchesLevels[GridX[TOTAL_BOOKMARKS]].push_back(TOTAL_BOOKMARKS); // ID of "current_pos" = number of bookmarks - Children[current_branch+1].push_back(TOTAL_BOOKMARKS); - if (Children[current_branch+1].size() > 1) - RecursiveAddHeight(current_branch, 1); - } else - { - // special case 0: if there's too many children on one level (more than canvas can show) - // then "current_pos" becomes special branch above current_branch - GridX[TOTAL_BOOKMARKS] = GridX[current_branch]; - GridY[TOTAL_BOOKMARKS] = GridY[current_branch] - 7; - } - } else - { - // special case 1: one and (presumably) only child of "cloud" - GridX[TOTAL_BOOKMARKS] = 1; - GridY[TOTAL_BOOKMARKS] = 0; - if ((int)BranchesLevels.size() <= GridX[TOTAL_BOOKMARKS]) - BranchesLevels.resize(GridX[TOTAL_BOOKMARKS] + 1); - BranchesLevels[GridX[TOTAL_BOOKMARKS]].push_back(TOTAL_BOOKMARKS); - } - } - // define grid_width - int grid_width, cloud_prefix = 0; - if (BranchesLevels.size()-1 > 0) - { - grid_width = BRANCHES_CANVAS_WIDTH / (BranchesLevels.size()-1); - if (grid_width < BRANCHES_GRID_MIN_WIDTH) - grid_width = BRANCHES_GRID_MIN_WIDTH; - else if (grid_width > BRANCHES_GRID_MAX_WIDTH) - grid_width = BRANCHES_GRID_MAX_WIDTH; - } else grid_width = BRANCHES_GRID_MAX_WIDTH; - if (grid_width < MIN_CLOUD_LINE_LENGTH) - cloud_prefix = MIN_CLOUD_LINE_LENGTH - grid_width; - - // 2. Define GridY of branches - RecursiveSetYPos(-1, 0); - // define grid_halfheight - int grid_halfheight; - int totalHeight = 0; - for (int i = Children[0].size()-1; i >= 0; i--) - totalHeight += GridHeight[Children[0][i]]; - if (totalHeight) - { - grid_halfheight = BRANCHES_CANVAS_HEIGHT / (2 * totalHeight); - if (grid_halfheight < BRANCHES_GRID_MIN_HALFHEIGHT) - grid_halfheight = BRANCHES_GRID_MIN_HALFHEIGHT; - else if (grid_halfheight > BRANCHES_GRID_MAX_HALFHEIGHT) - grid_halfheight = BRANCHES_GRID_MAX_HALFHEIGHT; - } else grid_halfheight = BRANCHES_GRID_MAX_HALFHEIGHT; - // special case 2: if chain of branches is too long, last item (normally it's fireball) goes up - if (changes_since_current_branch) - { - if (GridX[TOTAL_BOOKMARKS] > MAX_CHAIN_LEN) - { - GridX[TOTAL_BOOKMARKS] = MAX_CHAIN_LEN; - GridY[TOTAL_BOOKMARKS] -= 2; - } - } - // special case 3: if some branch crosses upper or lower border - int parent; - for (int t = TOTAL_BOOKMARKS; t >= 0; t--) - { - if (GridY[t] > MAX_GRID_Y_POS) - { - if (t < TOTAL_BOOKMARKS) - parent = bookmarks_array[t].parent_branch; - else - parent = current_branch; - int pos = MAX_GRID_Y_POS; - for (int i = 0; i < (int)Children[parent+1].size(); ++i) - { - GridY[Children[parent+1][i]] = pos; - pos -= 2; - } - } else if (GridY[t] < -MAX_GRID_Y_POS) - { - if (t < TOTAL_BOOKMARKS) - parent = bookmarks_array[t].parent_branch; - else - parent = current_branch; - int pos = -MAX_GRID_Y_POS; - for (int i = Children[parent+1].size()-1; i >= 0; i--) - { - GridY[Children[parent+1][i]] = pos; - pos += 2; - } - } - } - // special case 4: if cloud has all 10 children, then one child will be out of canvas - if (Children[0].size() == TOTAL_BOOKMARKS) - { - // find this child and move it to be visible - for (int t = TOTAL_BOOKMARKS - 1; t >= 0; t--) - { - if (GridY[t] > MAX_GRID_Y_POS) - { - GridY[t] = MAX_GRID_Y_POS; - GridX[t] -= 2; - // also move fireball to position near this branch - if (changes_since_current_branch && current_branch == t) - { - GridY[TOTAL_BOOKMARKS] = GridY[t]; - GridX[TOTAL_BOOKMARKS] = GridX[t] + 1; - } - break; - } else if (GridY[t] < -MAX_GRID_Y_POS) - { - GridY[t] = -MAX_GRID_Y_POS; - GridX[t] -= 2; - // also move fireball to position near this branch - if (changes_since_current_branch && current_branch == t) - { - GridY[TOTAL_BOOKMARKS] = GridY[t]; - GridX[TOTAL_BOOKMARKS] = GridX[t] + 1; - } - break; - } - } - } - - // 3. Set pixel positions of branches - int max_x = 0; - for (int i = TOTAL_BOOKMARKS-1; i >= 0; i--) - { - if (bookmarks_array[i].not_empty) - { - BranchX[i] = cloud_prefix + GridX[i] * grid_width; - BranchY[i] = BRANCHES_CLOUD_Y + GridY[i] * grid_halfheight; - } else - { - BranchX[i] = EMPTY_BRANCHES_X; - BranchY[i] = EMPTY_BRANCHES_Y_BASE + EMPTY_BRANCHES_Y_FACTOR * ((i + TOTAL_BOOKMARKS - 1) % TOTAL_BOOKMARKS); - } - if (max_x < BranchX[i]) max_x = BranchX[i]; - } - if (changes_since_current_branch) - { - // also set pixel position of "current_pos" - BranchX[TOTAL_BOOKMARKS] = cloud_prefix + GridX[TOTAL_BOOKMARKS] * grid_width; - BranchY[TOTAL_BOOKMARKS] = BRANCHES_CLOUD_Y + GridY[TOTAL_BOOKMARKS] * grid_halfheight; - } else if (current_branch >= 0) - { - BranchX[TOTAL_BOOKMARKS] = cloud_prefix + GridX[current_branch] * grid_width; - BranchY[TOTAL_BOOKMARKS] = BRANCHES_CLOUD_Y + GridY[current_branch] * grid_halfheight; - } else - { - BranchX[TOTAL_BOOKMARKS] = 0; - BranchY[TOTAL_BOOKMARKS] = BRANCHES_CLOUD_Y; - } - if (max_x < BranchX[TOTAL_BOOKMARKS]) max_x = BranchX[TOTAL_BOOKMARKS]; - // align whole tree horizontally - CloudX = (BRANCHES_BITMAP_WIDTH + 1 - max_x) / 2; - if (CloudX < 0) CloudX = 0; - for (int i = TOTAL_BOOKMARKS-1; i >= 0; i--) - if (bookmarks_array[i].not_empty) - BranchX[i] += CloudX; - BranchX[TOTAL_BOOKMARKS] += CloudX; - // target cursor - CursorX = BranchX[TOTAL_BOOKMARKS]; - CursorY = BranchY[TOTAL_BOOKMARKS]; - // finished recalculating - must_recalculate_branches_tree = false; - must_redraw_branches_tree = true; -} -void BOOKMARKS::RecursiveAddHeight(int branch_num, int amount) -{ - if (branch_num >= 0) - { - GridHeight[branch_num] += amount; - if (bookmarks_array[branch_num].parent_branch >= 0) - RecursiveAddHeight(bookmarks_array[branch_num].parent_branch, amount); - } -} -void BOOKMARKS::RecursiveSetYPos(int parent, int parentY) -{ - if (Children[parent+1].size()) - { - // find total height of children - int totalHeight = 0; - for (int i = Children[parent+1].size()-1; i >= 0; i--) - totalHeight += GridHeight[Children[parent+1][i]]; - // set Y of children and subchildren - for (int i = Children[parent+1].size()-1; i >= 0; i--) - { - int child_id = Children[parent+1][i]; - GridY[child_id] = parentY + GridHeight[child_id] - totalHeight; - RecursiveSetYPos(child_id, GridY[child_id]); - parentY += 2 * GridHeight[child_id]; - } - } + return -1; // no Bookmarks at the frame } // ---------------------------------------------------------------------------------------- LRESULT APIENTRY BookmarksListWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) @@ -1489,58 +712,4 @@ LRESULT APIENTRY BookmarksListWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM } return CallWindowProc(hwndBookmarksList_oldWndProc, hWnd, msg, wParam, lParam); } -LRESULT APIENTRY BranchesBitmapWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - extern BOOKMARKS bookmarks; - switch(msg) - { - case WM_MOUSEMOVE: - { - if (!bookmarks.mouse_over_bitmap) - { - bookmarks.mouse_over_bitmap = true; - bookmarks.tme.hwndTrack = hWnd; - TrackMouseEvent(&bookmarks.tme); - } - bookmarks.MouseMove(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); - break; - } - case WM_MOUSELEAVE: - { - bookmarks.mouse_over_bitmap = false; - bookmarks.MouseMove(-1, -1); - break; - } - case WM_PAINT: - { - PAINTSTRUCT ps; - HDC hdc = BeginPaint(hWnd, &ps); - bookmarks.PaintBranchesBitmap(hdc); - EndPaint(hWnd, &ps); - return 0; - } - case WM_LBUTTONDOWN: - case WM_LBUTTONDBLCLK: - case WM_RBUTTONDOWN: - case WM_RBUTTONDBLCLK: - { - if (GetFocus() != hWnd) - SetFocus(hWnd); - return 0; - } - case WM_MBUTTONDOWN: - case WM_MBUTTONDBLCLK: - { - if (GetFocus() != hWnd) - SetFocus(hWnd); - playback.MiddleButtonClick(); - return 0; - } - case WM_MOUSEWHEEL: - return SendMessage(piano_roll.hwndList, msg, wParam, lParam); - - } - return CallWindowProc(hwndBranchesBitmap_oldWndProc, hWnd, msg, wParam, lParam); -} - diff --git a/src/drivers/win/taseditor/bookmarks.h b/src/drivers/win/taseditor/bookmarks.h index e4a88b7c..ab8e0619 100644 --- a/src/drivers/win/taseditor/bookmarks.h +++ b/src/drivers/win/taseditor/bookmarks.h @@ -17,83 +17,15 @@ enum COMMANDS COMMAND_JUMP = 1, COMMAND_DEPLOY = 2, COMMAND_DELETE = 3, // not implemented, probably useless + + TOTAL_COMMANDS }; #define ITEM_UNDER_MOUSE_NONE -2 #define ITEM_UNDER_MOUSE_CLOUD -1 #define BOOKMARKS_FLASH_TICK 100 // in milliseconds -#define BRANCHES_ANIMATION_TICK 50 // animate at 20FPS -#define BRANCHES_TRANSITION_MAX 8 -// branches bitmap -#define BRANCHES_BITMAP_WIDTH 170 -#define BRANCHES_BITMAP_HEIGHT 145 -#define BRANCHES_ANIMATION_FRAMES 12 -#define BRANCHES_BITMAP_FRAMENUM_X 2 -#define BRANCHES_BITMAP_FRAMENUM_Y 1 -#define BRANCHES_BITMAP_TIME_X 2 -#define BRANCHES_BITMAP_TIME_Y BRANCHES_BITMAP_HEIGHT - 17 -#define BRANCHES_TEXT_SHADOW_COLOR 0xFFFFFF -#define BRANCHES_TEXT_COLOR 0x7F0000 -// constants for drawing branches tree -#define BRANCHES_CANVAS_WIDTH 146 -#define BRANCHES_CANVAS_HEIGHT 130 -#define BRANCHES_CLOUD_X 14 -#define BRANCHES_CLOUD_Y 72 -#define BRANCHES_GRID_MIN_WIDTH 14 -#define BRANCHES_GRID_MAX_WIDTH 30 -#define MIN_CLOUD_LINE_LENGTH 19 -#define BRANCHES_GRID_MIN_HALFHEIGHT 8 -#define BRANCHES_GRID_MAX_HALFHEIGHT 12 -#define EMPTY_BRANCHES_X -6 -#define EMPTY_BRANCHES_Y_BASE 8 -#define EMPTY_BRANCHES_Y_FACTOR 14 -#define MAX_NUM_CHILDREN_ON_CANVAS_HEIGHT 9 -#define MAX_CHAIN_LEN 10 -#define MAX_GRID_Y_POS 8 -// spritesheet -#define DIGIT_BITMAP_WIDTH 9 -#define DIGIT_BITMAP_HALFWIDTH DIGIT_BITMAP_WIDTH/2 -#define DIGIT_BITMAP_HEIGHT 13 -#define DIGIT_BITMAP_HALFHEIGHT DIGIT_BITMAP_HEIGHT/2 -#define BLUE_DIGITS_SPRITESHEET_DX DIGIT_BITMAP_WIDTH*TOTAL_BOOKMARKS -#define MOUSEOVER_DIGITS_SPRITESHEET_DY DIGIT_BITMAP_HEIGHT -#define DIGIT_RECT_WIDTH 11 -#define DIGIT_RECT_WIDTH_COLLISION (DIGIT_RECT_WIDTH + 2) -#define DIGIT_RECT_HALFWIDTH DIGIT_RECT_WIDTH/2 -#define DIGIT_RECT_HALFWIDTH_COLLISION (DIGIT_RECT_HALFWIDTH + 1) -#define DIGIT_RECT_HEIGHT 15 -#define DIGIT_RECT_HEIGHT_COLLISION (DIGIT_RECT_HEIGHT + 2) -#define DIGIT_RECT_HALFHEIGHT DIGIT_RECT_HEIGHT/2 -#define DIGIT_RECT_HALFHEIGHT_COLLISION (DIGIT_RECT_HALFHEIGHT + 1) -#define BRANCHES_CLOUD_WIDTH 26 -#define BRANCHES_CLOUD_HALFWIDTH BRANCHES_CLOUD_WIDTH/2 -#define BRANCHES_CLOUD_HEIGHT 15 -#define BRANCHES_CLOUD_HALFHEIGHT BRANCHES_CLOUD_HEIGHT/2 -#define BRANCHES_CLOUD_SPRITESHEET_X 180 -#define BRANCHES_CLOUD_SPRITESHEET_Y 0 -#define BRANCHES_FIREBALL_WIDTH 10 -#define BRANCHES_FIREBALL_HALFWIDTH BRANCHES_FIREBALL_WIDTH/2 -#define BRANCHES_FIREBALL_HEIGHT 10 -#define BRANCHES_FIREBALL_HALFHEIGHT BRANCHES_FIREBALL_HEIGHT/2 -#define BRANCHES_FIREBALL_SPRITESHEET_X 0 -#define BRANCHES_FIREBALL_SPRITESHEET_Y 26 -#define BRANCHES_FIREBALL_MAX_SIZE 5 -#define BRANCHES_FIREBALL_SPRITESHEET_END_X 160 -#define BRANCHES_CORNER_WIDTH 7 -#define BRANCHES_CORNER_HALFWIDTH BRANCHES_CORNER_WIDTH/2 -#define BRANCHES_CORNER_HEIGHT 7 -#define BRANCHES_CORNER_HALFHEIGHT BRANCHES_CORNER_HEIGHT/2 -#define BRANCHES_CORNER1_SPRITESHEET_X 206 -#define BRANCHES_CORNER1_SPRITESHEET_Y 0 -#define BRANCHES_CORNER2_SPRITESHEET_X 213 -#define BRANCHES_CORNER2_SPRITESHEET_Y 0 -#define BRANCHES_CORNER3_SPRITESHEET_X 206 -#define BRANCHES_CORNER3_SPRITESHEET_Y 7 -#define BRANCHES_CORNER4_SPRITESHEET_X 213 -#define BRANCHES_CORNER4_SPRITESHEET_Y 7 -#define BRANCHES_CORNER_BASE_SHIFT 6 // listview columns enum { @@ -120,7 +52,7 @@ public: void save(EMUFILE *os, bool really_save = true); bool load(EMUFILE *is); - void command(int command_id, int slot); + void command(int command_id, int slot = -1); void GetDispInfo(NMLVDISPINFO* nmlvDispInfo); LONG CustomDraw(NMLVCUSTOMDRAW* msg); @@ -128,85 +60,40 @@ public: void RightClick(int column_index, int row_index); int FindBookmarkAtFrame(int frame); - int GetCurrentBranch(); void RedrawBookmarksCaption(); void RedrawBookmarksList(); void RedrawChangedBookmarks(int frame); void RedrawBookmarksRow(int index); - void RedrawBranchesTree(); - void PaintBranchesBitmap(HDC hdc); - void MouseMove(int new_x, int new_y); - void CheckMousePos(); - - void ChangesMadeSinceBranch(); - - void RecalculateBranchesTree(); - void RecursiveAddHeight(int branch_num, int amount); - void RecursiveSetYPos(int parent, int parentY); + void FindItemUnderMouse(); // saved vars std::vector bookmarks_array; // not saved vars - int branch_row_top; - int branch_row_left; - int branch_row_height; + int edit_mode; + bool must_check_item_under_mouse; bool mouse_over_bitmap, mouse_over_bookmarkslist; int item_under_mouse; TRACKMOUSEEVENT tme, list_tme; + HWND hwndBookmarksList, hwndBranchesBitmap, hwndBookmarks; private: void set(int slot); void jump(int slot); void deploy(int slot); - void SetCurrentPosTime(); - - // also saved vars - int current_branch; - bool changes_since_current_branch; - char cloud_time[TIME_DESC_LENGTH]; - char current_pos_time[TIME_DESC_LENGTH]; - // not saved vars std::vector commands; int check_flash_shedule; - int edit_mode; - int animation_frame; - int next_animation_time; - bool must_check_item_under_mouse; - bool must_redraw_branches_tree; - bool must_recalculate_branches_tree; - std::vector BranchX; // in pixels - std::vector BranchY; - std::vector BranchPrevX; - std::vector BranchPrevY; - std::vector BranchCurrX; - std::vector BranchCurrY; - int CloudX, CloudPrevX, cloud_x; - int CursorX, CursorPrevX, CursorY, CursorPrevY; - int transition_phase; - int fireball_size; int mouse_x, mouse_y; - - HWND hwndBookmarksList, hwndBookmarks; - HWND hwndBranchesBitmap; + int list_row_top; + int list_row_left; + int list_row_height; // GDI stuff HFONT hBookmarksFont; - HBRUSH normal_brush; - RECT temp_rect; - HPEN normal_pen, select_pen; - HBITMAP branches_hbitmap, hOldBitmap, buffer_hbitmap, hOldBitmap1, branchesSpritesheet, hOldBitmap2; - HDC hBitmapDC, hBufferDC, hSpritesheetDC; - - // temps - std::vector GridX; // in grid units - std::vector GridY; - std::vector GridHeight; - std::vector> Children; }; diff --git a/src/drivers/win/taseditor/branches.cpp b/src/drivers/win/taseditor/branches.cpp new file mode 100644 index 00000000..4614e6df --- /dev/null +++ b/src/drivers/win/taseditor/branches.cpp @@ -0,0 +1,944 @@ +/* --------------------------------------------------------------------------------- +Implementation file of Branches class +Copyright (c) 2011-2012 AnS + +(The MIT License) +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------------ +Branches - Manager of Branches +[Singleton] + +* stores info about Branches (relations of Bookmarks) and the id of current Branch +* also stores the time of the last modification (see fireball) and the time of the root of Branches Tree (see cloudlet) +* finds best place for every new Bookmark in the Branches Tree +* saves and loads the data from a project file. On error: sends warning to caller +* implements the working of Branches Tree: creating, recalculating relations, animating, redrawing, mouseover +* on demand: reacts on project changes and recalculates Branches Tree +* regularly updates animations in Branches Tree +* stores resources: coordinates for building Branches Tree, animation timings +------------------------------------------------------------------------------------ */ + +#include "taseditor_project.h" +#include "utils/xstring.h" +#include "zlib.h" + +#pragma comment(lib, "msimg32.lib") + +LRESULT APIENTRY BranchesBitmapWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); +WNDPROC hwndBranchesBitmap_oldWndProc; + +extern TASEDITOR_CONFIG taseditor_config; +extern TASEDITOR_WINDOW taseditor_window; +extern POPUP_DISPLAY popup_display; +extern PLAYBACK playback; +extern SELECTION selection; +extern GREENZONE greenzone; +extern TASEDITOR_PROJECT project; +extern HISTORY history; +extern PIANO_ROLL piano_roll; +extern MARKERS_MANAGER markers_manager; +extern BOOKMARKS bookmarks; + +extern COLORREF bookmark_flash_colors[TOTAL_COMMANDS][FLASH_PHASE_MAX+1]; + +// resources +// corners cursor animation +int corners_cursor_shift[BRANCHES_ANIMATION_FRAMES] = {0, 1, 2, 3, 4, 5, 5, 4, 3, 2, 1, 0 }; + +BRANCHES::BRANCHES() +{ +} + +void BRANCHES::init() +{ + free(); + + // subclass BranchesBitmap + hwndBranchesBitmap_oldWndProc = (WNDPROC)SetWindowLong(bookmarks.hwndBranchesBitmap, GWL_WNDPROC, (LONG)BranchesBitmapWndProc); + + // init arrays + BranchX.resize(TOTAL_BOOKMARKS+1); + BranchY.resize(TOTAL_BOOKMARKS+1); + BranchPrevX.resize(TOTAL_BOOKMARKS+1); + BranchPrevY.resize(TOTAL_BOOKMARKS+1); + BranchCurrX.resize(TOTAL_BOOKMARKS+1); + BranchCurrY.resize(TOTAL_BOOKMARKS+1); + + // init GDI stuff + HDC win_hdc = GetWindowDC(bookmarks.hwndBranchesBitmap); + hBitmapDC = CreateCompatibleDC(win_hdc); + branches_hbitmap = CreateCompatibleBitmap(win_hdc, BRANCHES_BITMAP_WIDTH, BRANCHES_BITMAP_WIDTH); + hOldBitmap = (HBITMAP)SelectObject(hBitmapDC, branches_hbitmap); + hBufferDC = CreateCompatibleDC(win_hdc); + buffer_hbitmap = CreateCompatibleBitmap(win_hdc, BRANCHES_BITMAP_WIDTH, BRANCHES_BITMAP_WIDTH); + hOldBitmap1 = (HBITMAP)SelectObject(hBufferDC, buffer_hbitmap); + normal_brush = CreateSolidBrush(0x000000); + // prepare branches spritesheet + branchesSpritesheet = LoadBitmap(fceu_hInstance, MAKEINTRESOURCE(IDB_BRANCH_SPRITESHEET)); + hSpritesheetDC = CreateCompatibleDC(win_hdc); + hOldBitmap2 = (HBITMAP)SelectObject(hSpritesheetDC, branchesSpritesheet); + // create pens + normal_pen = CreatePen(PS_SOLID, 1, 0x0); + select_pen = CreatePen(PS_SOLID, 2, 0xFF9080); + + reset(); + next_animation_time = 0; + + update(); +} +void BRANCHES::free() +{ + parents.resize(0); + BranchX.resize(0); + BranchY.resize(0); + BranchPrevX.resize(0); + BranchPrevY.resize(0); + BranchCurrX.resize(0); + BranchCurrY.resize(0); + // free GDI stuff + if (hOldBitmap && hBitmapDC) + { + SelectObject(hBitmapDC, hOldBitmap); + DeleteDC(hBitmapDC); + hBitmapDC = NULL; + } + if (branches_hbitmap) + { + DeleteObject(branches_hbitmap); + branches_hbitmap = NULL; + } + if (hOldBitmap1 && hBufferDC) + { + SelectObject(hBufferDC, hOldBitmap1); + DeleteDC(hBufferDC); + hBufferDC = NULL; + } + if (buffer_hbitmap) + { + DeleteObject(buffer_hbitmap); + buffer_hbitmap = NULL; + } + if (hOldBitmap2 && hSpritesheetDC) + { + SelectObject(hSpritesheetDC, hOldBitmap2); + DeleteDC(hSpritesheetDC); + hSpritesheetDC = NULL; + } + if (branchesSpritesheet) + { + DeleteObject(branchesSpritesheet); + branchesSpritesheet = NULL; + } +} +void BRANCHES::reset() +{ + parents.resize(TOTAL_BOOKMARKS); + for (int i = TOTAL_BOOKMARKS-1; i >= 0; i--) + parents[i] = ITEM_UNDER_MOUSE_CLOUD; + + // set positions of slots to default coordinates + for (int i = TOTAL_BOOKMARKS; i >= 0; i--) + { + BranchX[i] = BranchPrevX[i] = BranchCurrX[i] = EMPTY_BRANCHES_X; + BranchY[i] = BranchPrevY[i] = BranchCurrY[i] = EMPTY_BRANCHES_Y_BASE + EMPTY_BRANCHES_Y_FACTOR * ((i + TOTAL_BOOKMARKS - 1) % TOTAL_BOOKMARKS); + } + CursorX = CursorPrevX = CloudX = CloudPrevX = BRANCHES_CLOUD_X; + CursorY = CursorPrevY = BRANCHES_CLOUD_Y; + + reset_vars(); + + current_branch = ITEM_UNDER_MOUSE_CLOUD; + changes_since_current_branch = false; + fireball_size = 0; + + // set cloud_time and current_pos_time + SetCurrentPosTime(); + strcpy(cloud_time, current_pos_time); +} +void BRANCHES::reset_vars() +{ + transition_phase = animation_frame = 0; + must_recalculate_branches_tree = must_redraw_branches_tree = true; + next_animation_time = clock() + BRANCHES_ANIMATION_TICK; +} + +void BRANCHES::update() +{ + if (must_recalculate_branches_tree) + RecalculateBranchesTree(); + + // once per 50 milliseconds update branches_bitmap + if (clock() > next_animation_time) + { + // animate branches_bitmap + next_animation_time = clock() + BRANCHES_ANIMATION_TICK; + animation_frame = (animation_frame + 1) % BRANCHES_ANIMATION_FRAMES; + if (bookmarks.edit_mode == EDIT_MODE_BRANCHES) + { + // grow or shrink fireball size + if (changes_since_current_branch) + { + fireball_size++; + if (fireball_size > BRANCHES_FIREBALL_MAX_SIZE) fireball_size = BRANCHES_FIREBALL_MAX_SIZE; + } else + { + fireball_size--; + if (fireball_size < 0) fireball_size = 0; + } + // also update transition from old to new tree + if (transition_phase) + { + transition_phase--; + // recalculate current positions of branch items + for (int i = 0; i <= TOTAL_BOOKMARKS; ++i) + { + BranchCurrX[i] = (BranchX[i] * (BRANCHES_TRANSITION_MAX - transition_phase) + BranchPrevX[i] * transition_phase) / BRANCHES_TRANSITION_MAX; + BranchCurrY[i] = (BranchY[i] * (BRANCHES_TRANSITION_MAX - transition_phase) + BranchPrevY[i] * transition_phase) / BRANCHES_TRANSITION_MAX; + } + cloud_x = (CloudX * (BRANCHES_TRANSITION_MAX - transition_phase) + CloudPrevX * transition_phase) / BRANCHES_TRANSITION_MAX; + must_redraw_branches_tree = true; + bookmarks.must_check_item_under_mouse = true; + } else if (!must_redraw_branches_tree) + { + // just update sprites + InvalidateRect(bookmarks.hwndBranchesBitmap, 0, FALSE); + } + if (must_redraw_branches_tree) + RedrawBranchesTree(); + } + } +} + +void BRANCHES::save(EMUFILE *os) +{ + // write cloud time + os->fwrite(cloud_time, TIME_DESC_LENGTH); + // write current branch and flag of changes since it + write32le(current_branch, os); + if (changes_since_current_branch) + write8le((uint8)1, os); + else + write8le((uint8)0, os); + // write current_position time + os->fwrite(current_pos_time, TIME_DESC_LENGTH); + // write all 10 parents + for (int i = 0; i < TOTAL_BOOKMARKS; ++i) + write32le(parents[i], os); +} +// returns true if couldn't load +bool BRANCHES::load(EMUFILE *is) +{ + // read cloud time + if ((int)is->fread(cloud_time, TIME_DESC_LENGTH) < TIME_DESC_LENGTH) goto error; + // read current branch and flag of changes since it + uint8 tmp; + if (!read32le(¤t_branch, is)) goto error; + if (!read8le(&tmp, is)) goto error; + changes_since_current_branch = (tmp != 0); + // read current_position time + if ((int)is->fread(current_pos_time, TIME_DESC_LENGTH) < TIME_DESC_LENGTH) goto error; + // read all 10 parents + for (int i = 0; i < TOTAL_BOOKMARKS; ++i) + if (!read32le(&parents[i], is)) goto error; + // all ok + reset_vars(); + return false; +error: + FCEU_printf("Error loading branches\n"); + return true; +} +// ---------------------------------------------------------- +void BRANCHES::RedrawBranchesTree() +{ + // draw background gradient + TRIVERTEX vertex[2] ; + vertex[0].x = 0; + vertex[0].y = 0; + vertex[0].Red = 0xC700; + vertex[0].Green = 0xE700; + vertex[0].Blue = 0xF300; + vertex[0].Alpha = 0x0000; + vertex[1].x = BRANCHES_BITMAP_WIDTH; + vertex[1].y = BRANCHES_BITMAP_HEIGHT; + vertex[1].Red = 0xEB00; + vertex[1].Green = 0xFA00; + vertex[1].Blue = 0xF800; + vertex[1].Alpha = 0x0000; + GRADIENT_RECT gRect; + gRect.UpperLeft = 0; + gRect.LowerRight = 1; + GradientFill(hBitmapDC, vertex, 2, &gRect, 1, GRADIENT_FILL_RECT_H); + + // lines + int branch_x, branch_y, parent_x, parent_y, child_id; + SelectObject(hBitmapDC, normal_pen); + for (int t = Children.size() - 1; t >= 0; t--) + { + if (t > 0) + { + parent_x = BranchCurrX[t-1]; + parent_y = BranchCurrY[t-1]; + } else + { + parent_x = cloud_x; + parent_y = BRANCHES_CLOUD_Y; + } + for (int i = Children[t].size() - 1; i >= 0; i--) + { + child_id = Children[t][i]; + if (child_id < TOTAL_BOOKMARKS) + { + MoveToEx(hBitmapDC, parent_x, parent_y, 0); + LineTo(hBitmapDC, BranchCurrX[child_id], BranchCurrY[child_id]); + } + } + } + // lines for item under mouse + SelectObject(hBitmapDC, select_pen); + int branch = bookmarks.item_under_mouse; + if (branch == TOTAL_BOOKMARKS) + branch = current_branch; + while (branch >= 0) + { + branch_x = BranchCurrX[branch]; + branch_y = BranchCurrY[branch]; + MoveToEx(hBitmapDC, branch_x, branch_y, 0); + branch = parents[branch]; + if (branch >= 0) + { + branch_x = BranchCurrX[branch]; + branch_y = BranchCurrY[branch]; + } else + { + branch_x = cloud_x; + branch_y = BRANCHES_CLOUD_Y; + } + LineTo(hBitmapDC, branch_x, branch_y); + } + if (changes_since_current_branch) + { + if (bookmarks.item_under_mouse != TOTAL_BOOKMARKS) + SelectObject(hBitmapDC, normal_pen); + if (current_branch >= 0) + { + parent_x = BranchCurrX[current_branch]; + parent_y = BranchCurrY[current_branch]; + } else + { + parent_x = cloud_x; + parent_y = BRANCHES_CLOUD_Y; + } + MoveToEx(hBitmapDC, parent_x, parent_y, 0); + branch_x = BranchCurrX[TOTAL_BOOKMARKS]; + branch_y = BranchCurrY[TOTAL_BOOKMARKS]; + LineTo(hBitmapDC, branch_x, branch_y); + } + + // cloud + TransparentBlt(hBitmapDC, cloud_x - BRANCHES_CLOUD_HALFWIDTH, BRANCHES_CLOUD_Y - BRANCHES_CLOUD_HALFHEIGHT, BRANCHES_CLOUD_WIDTH, BRANCHES_CLOUD_HEIGHT, hSpritesheetDC, BRANCHES_CLOUD_SPRITESHEET_X, BRANCHES_CLOUD_SPRITESHEET_Y, BRANCHES_CLOUD_WIDTH, BRANCHES_CLOUD_HEIGHT, 0x00FF00); + + // branches rectangles + for (int i = 0; i < TOTAL_BOOKMARKS; ++i) + { + temp_rect.left = BranchCurrX[i] - DIGIT_RECT_HALFWIDTH; + temp_rect.top = BranchCurrY[i] - DIGIT_RECT_HALFHEIGHT; + temp_rect.right = temp_rect.left + DIGIT_RECT_WIDTH; + temp_rect.bottom = temp_rect.top + DIGIT_RECT_HEIGHT; + if (bookmarks.bookmarks_array[i].flash_phase) + { + // draw colored rect + HBRUSH color_brush = CreateSolidBrush(bookmark_flash_colors[bookmarks.bookmarks_array[i].flash_type][bookmarks.bookmarks_array[i].flash_phase]); + FillRect(hBitmapDC, &temp_rect, color_brush); + DeleteObject(color_brush); + } else + { + // draw black rect + FillRect(hBitmapDC, &temp_rect, normal_brush); + } + } + // digits + for (int i = 0; i < TOTAL_BOOKMARKS; ++i) + { + branch_x = BranchCurrX[i] - DIGIT_BITMAP_HALFWIDTH; + branch_y = BranchCurrY[i] - DIGIT_BITMAP_HALFHEIGHT; + if(i == current_branch) + { + if (i == bookmarks.item_under_mouse) + BitBlt(hBitmapDC, branch_x, branch_y, DIGIT_BITMAP_WIDTH, DIGIT_BITMAP_HEIGHT, hSpritesheetDC, i * DIGIT_BITMAP_WIDTH + BLUE_DIGITS_SPRITESHEET_DX, MOUSEOVER_DIGITS_SPRITESHEET_DY, SRCCOPY); + else + BitBlt(hBitmapDC, branch_x, branch_y, DIGIT_BITMAP_WIDTH, DIGIT_BITMAP_HEIGHT, hSpritesheetDC, i * DIGIT_BITMAP_WIDTH + BLUE_DIGITS_SPRITESHEET_DX, 0, SRCCOPY); + } else + { + if (i == bookmarks.item_under_mouse) + BitBlt(hBitmapDC, branch_x, branch_y, DIGIT_BITMAP_WIDTH, DIGIT_BITMAP_HEIGHT, hSpritesheetDC, i * DIGIT_BITMAP_WIDTH, MOUSEOVER_DIGITS_SPRITESHEET_DY, SRCCOPY); + else + BitBlt(hBitmapDC, branch_x, branch_y, DIGIT_BITMAP_WIDTH, DIGIT_BITMAP_HEIGHT, hSpritesheetDC, i * DIGIT_BITMAP_WIDTH, 0, SRCCOPY); + } + } + SetBkMode(hBitmapDC, TRANSPARENT); + // jump_frame of item under cursor (except cloud - it doesn't have particular frame) + if (bookmarks.item_under_mouse > ITEM_UNDER_MOUSE_CLOUD) + { + char framenum_string[DIGITS_IN_FRAMENUM+1] = {0}; + if (bookmarks.item_under_mouse < TOTAL_BOOKMARKS) + U32ToDecStr(framenum_string, bookmarks.bookmarks_array[bookmarks.item_under_mouse].snapshot.jump_frame, DIGITS_IN_FRAMENUM); + else + U32ToDecStr(framenum_string, currFrameCounter, DIGITS_IN_FRAMENUM); + SetTextColor(hBitmapDC, BRANCHES_TEXT_SHADOW_COLOR); + TextOut(hBitmapDC, BRANCHES_BITMAP_FRAMENUM_X + 1, BRANCHES_BITMAP_FRAMENUM_Y + 1, (LPCSTR)framenum_string, DIGITS_IN_FRAMENUM); + SetTextColor(hBitmapDC, BRANCHES_TEXT_COLOR); + TextOut(hBitmapDC, BRANCHES_BITMAP_FRAMENUM_X, BRANCHES_BITMAP_FRAMENUM_Y, (LPCSTR)framenum_string, DIGITS_IN_FRAMENUM); + } + // time of item under cursor + if (bookmarks.item_under_mouse > ITEM_UNDER_MOUSE_NONE) + { + if (bookmarks.item_under_mouse == ITEM_UNDER_MOUSE_CLOUD) + { + // draw shadow of text + SetTextColor(hBitmapDC, BRANCHES_TEXT_SHADOW_COLOR); + TextOut(hBitmapDC, BRANCHES_BITMAP_TIME_X + 1, BRANCHES_BITMAP_TIME_Y + 1, (LPCSTR)cloud_time, TIME_DESC_LENGTH-1); + SetTextColor(hBitmapDC, BRANCHES_TEXT_COLOR); + TextOut(hBitmapDC, BRANCHES_BITMAP_TIME_X, BRANCHES_BITMAP_TIME_Y, (LPCSTR)cloud_time, TIME_DESC_LENGTH-1); + } else if (bookmarks.item_under_mouse < TOTAL_BOOKMARKS) + { + SetTextColor(hBitmapDC, BRANCHES_TEXT_SHADOW_COLOR); + TextOut(hBitmapDC, BRANCHES_BITMAP_TIME_X + 1, BRANCHES_BITMAP_TIME_Y + 1, (LPCSTR)bookmarks.bookmarks_array[bookmarks.item_under_mouse].snapshot.description, TIME_DESC_LENGTH-1); + SetTextColor(hBitmapDC, BRANCHES_TEXT_COLOR); + TextOut(hBitmapDC, BRANCHES_BITMAP_TIME_X, BRANCHES_BITMAP_TIME_Y, (LPCSTR)bookmarks.bookmarks_array[bookmarks.item_under_mouse].snapshot.description, TIME_DESC_LENGTH-1); + } else // fireball - current_pos_time + { + SetTextColor(hBitmapDC, BRANCHES_TEXT_SHADOW_COLOR); + TextOut(hBitmapDC, BRANCHES_BITMAP_TIME_X + 1, BRANCHES_BITMAP_TIME_Y + 1, (LPCSTR)current_pos_time, TIME_DESC_LENGTH-1); + SetTextColor(hBitmapDC, BRANCHES_TEXT_COLOR); + TextOut(hBitmapDC, BRANCHES_BITMAP_TIME_X, BRANCHES_BITMAP_TIME_Y, (LPCSTR)current_pos_time, TIME_DESC_LENGTH-1); + } + } + // finished + must_redraw_branches_tree = false; + InvalidateRect(bookmarks.hwndBranchesBitmap, 0, FALSE); +} + +// this is called by wndproc on WM_PAINT +void BRANCHES::PaintBranchesBitmap(HDC hdc) +{ + int branch_x, branch_y; + // "bg" + BitBlt(hBufferDC, 0, 0, BRANCHES_BITMAP_WIDTH, BRANCHES_BITMAP_HEIGHT, hBitmapDC, 0, 0, SRCCOPY); + // "sprites" + // fireball + if (fireball_size) + { + branch_x = BranchCurrX[TOTAL_BOOKMARKS] - BRANCHES_FIREBALL_HALFWIDTH; + branch_y = BranchCurrY[TOTAL_BOOKMARKS] - BRANCHES_FIREBALL_HALFHEIGHT; + if (fireball_size >= BRANCHES_FIREBALL_MAX_SIZE) + { + TransparentBlt(hBufferDC, branch_x, branch_y, BRANCHES_FIREBALL_WIDTH, BRANCHES_FIREBALL_HEIGHT, hSpritesheetDC, animation_frame * BRANCHES_FIREBALL_WIDTH + BRANCHES_FIREBALL_SPRITESHEET_X, BRANCHES_FIREBALL_SPRITESHEET_Y, BRANCHES_FIREBALL_WIDTH, BRANCHES_FIREBALL_HEIGHT, 0x00FF00); + } else + { + TransparentBlt(hBufferDC, branch_x, branch_y, BRANCHES_FIREBALL_WIDTH, BRANCHES_FIREBALL_HEIGHT, hSpritesheetDC, BRANCHES_FIREBALL_SPRITESHEET_END_X - fireball_size * BRANCHES_FIREBALL_WIDTH, BRANCHES_FIREBALL_SPRITESHEET_Y, BRANCHES_FIREBALL_WIDTH, BRANCHES_FIREBALL_HEIGHT, 0x00FF00); + } + } + // corners cursor + branch_x = (CursorX * (BRANCHES_TRANSITION_MAX - transition_phase) + CursorPrevX * transition_phase) / BRANCHES_TRANSITION_MAX; + branch_y = (CursorY * (BRANCHES_TRANSITION_MAX - transition_phase) + CursorPrevY * transition_phase) / BRANCHES_TRANSITION_MAX; + int current_corners_cursor_shift = BRANCHES_CORNER_BASE_SHIFT + corners_cursor_shift[animation_frame]; + int corner_x, corner_y; + // upper left + corner_x = branch_x - current_corners_cursor_shift - BRANCHES_CORNER_HALFWIDTH; + corner_y = branch_y - current_corners_cursor_shift - BRANCHES_CORNER_HALFHEIGHT; + TransparentBlt(hBufferDC, corner_x, corner_y, BRANCHES_CORNER_WIDTH, BRANCHES_CORNER_HEIGHT, hSpritesheetDC, BRANCHES_CORNER1_SPRITESHEET_X, BRANCHES_CORNER1_SPRITESHEET_Y, BRANCHES_CORNER_WIDTH, BRANCHES_CORNER_HEIGHT, 0x00FF00); + // upper right + corner_x = branch_x + current_corners_cursor_shift - BRANCHES_CORNER_HALFWIDTH; + corner_y = branch_y - current_corners_cursor_shift - BRANCHES_CORNER_HALFHEIGHT; + TransparentBlt(hBufferDC, corner_x, corner_y, BRANCHES_CORNER_WIDTH, BRANCHES_CORNER_HEIGHT, hSpritesheetDC, BRANCHES_CORNER2_SPRITESHEET_X, BRANCHES_CORNER2_SPRITESHEET_Y, BRANCHES_CORNER_WIDTH, BRANCHES_CORNER_HEIGHT, 0x00FF00); + // lower left + corner_x = branch_x - current_corners_cursor_shift - BRANCHES_CORNER_HALFWIDTH; + corner_y = branch_y + current_corners_cursor_shift - BRANCHES_CORNER_HALFHEIGHT; + TransparentBlt(hBufferDC, corner_x, corner_y, BRANCHES_CORNER_WIDTH, BRANCHES_CORNER_HEIGHT, hSpritesheetDC, BRANCHES_CORNER3_SPRITESHEET_X, BRANCHES_CORNER3_SPRITESHEET_Y, BRANCHES_CORNER_WIDTH, BRANCHES_CORNER_HEIGHT, 0x00FF00); + // lower right + corner_x = branch_x + current_corners_cursor_shift - BRANCHES_CORNER_HALFWIDTH; + corner_y = branch_y + current_corners_cursor_shift - BRANCHES_CORNER_HALFHEIGHT; + TransparentBlt(hBufferDC, corner_x, corner_y, BRANCHES_CORNER_WIDTH, BRANCHES_CORNER_HEIGHT, hSpritesheetDC, BRANCHES_CORNER4_SPRITESHEET_X, BRANCHES_CORNER4_SPRITESHEET_Y, BRANCHES_CORNER_WIDTH, BRANCHES_CORNER_HEIGHT, 0x00FF00); + // finish - paste buffer bitmap to screen + BitBlt(hdc, 0, 0, BRANCHES_BITMAP_WIDTH, BRANCHES_BITMAP_HEIGHT, hBufferDC, 0, 0, SRCCOPY); +} +// ---------------------------------------------------------------------------------------- +int BRANCHES::GetCurrentBranch() +{ + return current_branch; +} + +// this function finds best place for a new Bookmark in the Branches Tree +void BRANCHES::HandleBookmarkSet(int slot, char* slot_time) +{ + if (slot != current_branch) + { + // inherit current branch, forget previous parent + int parent = parents[slot]; + if (parent == ITEM_UNDER_MOUSE_CLOUD && slot_time[0]) + { + // check if this was the only child of cloud parent, if so then set cloud time to the saved_time + int i = 0; + for (; i < TOTAL_BOOKMARKS; ++i) + { + if (bookmarks.bookmarks_array[i].not_empty && parents[i] == ITEM_UNDER_MOUSE_CLOUD && i != slot) + break; + } + if (i >= TOTAL_BOOKMARKS) + // didn't find another child of cloud, so after this slot disconnects, cloud will have 0 children + // it will mean that old cloud disappears and new cloud is formed where the slot was + strcpy(cloud_time, slot_time); + } + // before disconnecting from old parent, connect all childs to the old parent + for (int i = 0; i < TOTAL_BOOKMARKS; ++i) + { + if (bookmarks.bookmarks_array[i].not_empty && parents[i] == slot) + parents[i] = parent; + } + parents[slot] = current_branch; + } + + // if parent is invalid (first_change < parent.jump_frame) then find better parent + int factor = 0; + // also if parent == cloud, then try to find better parent + int parent = parents[slot]; + if (parent != ITEM_UNDER_MOUSE_CLOUD) + factor = bookmarks.bookmarks_array[slot].snapshot.findFirstChange(bookmarks.bookmarks_array[parent].snapshot); + if (parent == ITEM_UNDER_MOUSE_CLOUD || (factor >= 0 && factor < bookmarks.bookmarks_array[parent].snapshot.jump_frame)) + { + // find highest frame of change + std::vector DecisiveFactor(TOTAL_BOOKMARKS); + int best_branch = ITEM_UNDER_MOUSE_CLOUD; + for (int i = TOTAL_BOOKMARKS-1; i >= 0; i--) + { + if (i != slot && i != parent && bookmarks.bookmarks_array[i].not_empty && bookmarks.bookmarks_array[slot].snapshot.size >= bookmarks.bookmarks_array[i].snapshot.jump_frame) + { + factor = bookmarks.bookmarks_array[slot].snapshot.findFirstChange(bookmarks.bookmarks_array[i].snapshot); + if (factor < 0) + { + // this branch is identical to this slot + DecisiveFactor[i] = 2 * bookmarks.bookmarks_array[i].snapshot.size; + } else if (factor >= bookmarks.bookmarks_array[i].snapshot.jump_frame) + { + // hey, this branch could be our new parent... + DecisiveFactor[i] = 2 * factor; + } else + DecisiveFactor[i] = 0; + } else + { + DecisiveFactor[i] = 0; + } + } + // add +1 as a bonus to current parents and grandparents (a bit of nepotism here) + while (parent >= 0) + { + if (DecisiveFactor[parent]) + DecisiveFactor[parent]++; + parent = parents[parent]; + } + // find max + factor = 0; + for (int i = TOTAL_BOOKMARKS-1; i >= 0; i--) + { + if (DecisiveFactor[i] && DecisiveFactor[i] > factor) + { + factor = DecisiveFactor[i]; + best_branch = i; + } + } + parent = parents[slot]; + if (parent != best_branch) + { + // before disconnecting from old parent, connect all childs to the old parent + for (int i = 0; i < TOTAL_BOOKMARKS; ++i) + { + if (bookmarks.bookmarks_array[i].not_empty && parents[i] == slot) + parents[i] = parent; + } + // found new parent + parents[slot] = best_branch; + must_recalculate_branches_tree = true; + } + } + // switch current branch to this branch + if (slot != current_branch || changes_since_current_branch) + must_recalculate_branches_tree = true; + current_branch = slot; + changes_since_current_branch = false; +} +void BRANCHES::HandleBookmarkDeploy(int slot) +{ + current_branch = slot; + changes_since_current_branch = false; + must_recalculate_branches_tree = true; +} + +void BRANCHES::ChangesMadeSinceBranch() +{ + bool prev_changes_since_current_branch = changes_since_current_branch; + changes_since_current_branch = true; + SetCurrentPosTime(); + // recalculate branch tree if previous_changes = false + if (!prev_changes_since_current_branch) + must_recalculate_branches_tree = true; + else if (bookmarks.item_under_mouse == TOTAL_BOOKMARKS) + must_redraw_branches_tree = true; // to redraw fireball's time +} + +void BRANCHES::FindItemUnderMouse(int mouse_x, int mouse_y) +{ + int prev_item_under_mouse = bookmarks.item_under_mouse; + bookmarks.item_under_mouse = ITEM_UNDER_MOUSE_NONE; + for (int i = 0; i < TOTAL_BOOKMARKS; ++i) + if (bookmarks.item_under_mouse == ITEM_UNDER_MOUSE_NONE && mouse_x >= BranchCurrX[i] - DIGIT_RECT_HALFWIDTH_COLLISION && mouse_x < BranchCurrX[i] - DIGIT_RECT_HALFWIDTH_COLLISION + DIGIT_RECT_WIDTH_COLLISION && mouse_y >= BranchCurrY[i] - DIGIT_RECT_HALFHEIGHT_COLLISION && mouse_y < BranchCurrY[i] - DIGIT_RECT_HALFHEIGHT_COLLISION + DIGIT_RECT_HEIGHT_COLLISION) + bookmarks.item_under_mouse = i; + if (bookmarks.item_under_mouse == ITEM_UNDER_MOUSE_NONE && mouse_x >= cloud_x - BRANCHES_CLOUD_HALFWIDTH && mouse_x < cloud_x - BRANCHES_CLOUD_HALFWIDTH + BRANCHES_CLOUD_WIDTH && mouse_y >= BRANCHES_CLOUD_Y - BRANCHES_CLOUD_HALFHEIGHT && mouse_y < BRANCHES_CLOUD_Y - BRANCHES_CLOUD_HALFHEIGHT + BRANCHES_CLOUD_HEIGHT) + bookmarks.item_under_mouse = ITEM_UNDER_MOUSE_CLOUD; + if (bookmarks.item_under_mouse == ITEM_UNDER_MOUSE_NONE && changes_since_current_branch && mouse_x >= BranchCurrX[TOTAL_BOOKMARKS] - DIGIT_RECT_HALFWIDTH_COLLISION && mouse_x < BranchCurrX[TOTAL_BOOKMARKS] - DIGIT_RECT_HALFWIDTH_COLLISION + DIGIT_RECT_WIDTH_COLLISION && mouse_y >= BranchCurrY[TOTAL_BOOKMARKS] - DIGIT_RECT_HALFHEIGHT_COLLISION && mouse_y < BranchCurrY[TOTAL_BOOKMARKS] - DIGIT_RECT_HALFHEIGHT_COLLISION + DIGIT_RECT_HEIGHT_COLLISION) + bookmarks.item_under_mouse = TOTAL_BOOKMARKS; + if (prev_item_under_mouse != bookmarks.item_under_mouse) + must_redraw_branches_tree = true; +} + +void BRANCHES::SetCurrentPosTime() +{ + time_t raw_time; + time(&raw_time); + struct tm * timeinfo = localtime(&raw_time); + strftime(current_pos_time, TIME_DESC_LENGTH, "%H:%M:%S", timeinfo); +} + +void BRANCHES::RecalculateBranchesTree() +{ + // save previous values + for (int i = TOTAL_BOOKMARKS; i >= 0; i--) + { + BranchPrevX[i] = (BranchX[i] * (BRANCHES_TRANSITION_MAX - transition_phase) + BranchPrevX[i] * transition_phase) / BRANCHES_TRANSITION_MAX; + BranchPrevY[i] = (BranchY[i] * (BRANCHES_TRANSITION_MAX - transition_phase) + BranchPrevY[i] * transition_phase) / BRANCHES_TRANSITION_MAX; + } + CloudPrevX = (CloudX * (BRANCHES_TRANSITION_MAX - transition_phase) + CloudPrevX * transition_phase) / BRANCHES_TRANSITION_MAX; + CursorPrevX = (CursorX * (BRANCHES_TRANSITION_MAX - transition_phase) + CursorPrevX * transition_phase) / BRANCHES_TRANSITION_MAX; + CursorPrevY = (CursorY * (BRANCHES_TRANSITION_MAX - transition_phase) + CursorPrevY * transition_phase) / BRANCHES_TRANSITION_MAX; + transition_phase = BRANCHES_TRANSITION_MAX; + + // 0. Prepare arrays + GridX.resize(0); + GridY.resize(0); + Children.resize(0); + GridHeight.resize(0); + GridX.resize(TOTAL_BOOKMARKS+1); + GridY.resize(TOTAL_BOOKMARKS+1); + Children.resize(TOTAL_BOOKMARKS+2); // 0th subvector is for cloud's children + GridHeight.resize(TOTAL_BOOKMARKS+1); + for (int i = TOTAL_BOOKMARKS; i >= 0; i--) + GridHeight[i] = 1; + + // 1. Define GridX of branches (distribute to levels) and GridHeight of branches + int current_grid_x = 0; + std::vector> BranchesLevels; + + std::vector UndistributedBranches; + UndistributedBranches.resize(TOTAL_BOOKMARKS); + for (int i = UndistributedBranches.size()-1; i >= 0; i--) + UndistributedBranches[i] = i; + // remove all empty branches + for (int i = UndistributedBranches.size()-1; i >= 0; i--) + { + if (!bookmarks.bookmarks_array[UndistributedBranches[i]].not_empty) + UndistributedBranches.erase(UndistributedBranches.begin() + i); + } + // highest level: cloud (id = -1) + BranchesLevels.resize(current_grid_x+1); + BranchesLevels[current_grid_x].resize(1); + BranchesLevels[current_grid_x][0] = ITEM_UNDER_MOUSE_CLOUD; + // go lower until all branches are arranged to levels + int current_parent; + while(UndistributedBranches.size()) + { + current_grid_x++; + BranchesLevels.resize(current_grid_x+1); + BranchesLevels[current_grid_x].resize(0); + for (int t = BranchesLevels[current_grid_x-1].size()-1; t >= 0; t--) + { + current_parent = BranchesLevels[current_grid_x-1][t]; + for (int i = UndistributedBranches.size()-1; i >= 0; i--) + { + if (parents[UndistributedBranches[i]] == current_parent) + { + // assign this branch to current level + GridX[UndistributedBranches[i]] = current_grid_x; + BranchesLevels[current_grid_x].push_back(UndistributedBranches[i]); + // also add it to parent's Children array + Children[current_parent+1].push_back(UndistributedBranches[i]); + UndistributedBranches.erase(UndistributedBranches.begin() + i); + } + } + if (current_parent >= 0) + { + GridHeight[current_parent] = Children[current_parent+1].size(); + if (Children[current_parent+1].size() > 1) + RecursiveAddHeight(parents[current_parent], GridHeight[current_parent] - 1); + else + GridHeight[current_parent] = 1; // its own height + } + } + } + if (changes_since_current_branch) + { + // also define "current_pos" GridX + if (current_branch >= 0) + { + if (Children[current_branch+1].size() < MAX_NUM_CHILDREN_ON_CANVAS_HEIGHT) + { + // "current_pos" becomes a child of current_branch + GridX[TOTAL_BOOKMARKS] = GridX[current_branch] + 1; + if ((int)BranchesLevels.size() <= GridX[TOTAL_BOOKMARKS]) + BranchesLevels.resize(GridX[TOTAL_BOOKMARKS] + 1); + BranchesLevels[GridX[TOTAL_BOOKMARKS]].push_back(TOTAL_BOOKMARKS); // ID of "current_pos" = number of bookmarks + Children[current_branch+1].push_back(TOTAL_BOOKMARKS); + if (Children[current_branch+1].size() > 1) + RecursiveAddHeight(current_branch, 1); + } else + { + // special case 0: if there's too many children on one level (more than canvas can show) + // then "current_pos" becomes special branch above current_branch + GridX[TOTAL_BOOKMARKS] = GridX[current_branch]; + GridY[TOTAL_BOOKMARKS] = GridY[current_branch] - 7; + } + } else + { + // special case 1: one and (presumably) only child of "cloud" + GridX[TOTAL_BOOKMARKS] = 1; + GridY[TOTAL_BOOKMARKS] = 0; + if ((int)BranchesLevels.size() <= GridX[TOTAL_BOOKMARKS]) + BranchesLevels.resize(GridX[TOTAL_BOOKMARKS] + 1); + BranchesLevels[GridX[TOTAL_BOOKMARKS]].push_back(TOTAL_BOOKMARKS); + } + } + // define grid_width + int grid_width, cloud_prefix = 0; + if (BranchesLevels.size()-1 > 0) + { + grid_width = BRANCHES_CANVAS_WIDTH / (BranchesLevels.size()-1); + if (grid_width < BRANCHES_GRID_MIN_WIDTH) + grid_width = BRANCHES_GRID_MIN_WIDTH; + else if (grid_width > BRANCHES_GRID_MAX_WIDTH) + grid_width = BRANCHES_GRID_MAX_WIDTH; + } else grid_width = BRANCHES_GRID_MAX_WIDTH; + if (grid_width < MIN_CLOUD_LINE_LENGTH) + cloud_prefix = MIN_CLOUD_LINE_LENGTH - grid_width; + + // 2. Define GridY of branches + RecursiveSetYPos(ITEM_UNDER_MOUSE_CLOUD, 0); + // define grid_halfheight + int grid_halfheight; + int totalHeight = 0; + for (int i = Children[0].size()-1; i >= 0; i--) + totalHeight += GridHeight[Children[0][i]]; + if (totalHeight) + { + grid_halfheight = BRANCHES_CANVAS_HEIGHT / (2 * totalHeight); + if (grid_halfheight < BRANCHES_GRID_MIN_HALFHEIGHT) + grid_halfheight = BRANCHES_GRID_MIN_HALFHEIGHT; + else if (grid_halfheight > BRANCHES_GRID_MAX_HALFHEIGHT) + grid_halfheight = BRANCHES_GRID_MAX_HALFHEIGHT; + } else grid_halfheight = BRANCHES_GRID_MAX_HALFHEIGHT; + // special case 2: if chain of branches is too long, last item (normally it's fireball) goes up + if (changes_since_current_branch) + { + if (GridX[TOTAL_BOOKMARKS] > MAX_CHAIN_LEN) + { + GridX[TOTAL_BOOKMARKS] = MAX_CHAIN_LEN; + GridY[TOTAL_BOOKMARKS] -= 2; + } + } + // special case 3: if some branch crosses upper or lower border + int parent; + for (int t = TOTAL_BOOKMARKS; t >= 0; t--) + { + if (GridY[t] > MAX_GRID_Y_POS) + { + if (t < TOTAL_BOOKMARKS) + parent = parents[t]; + else + parent = current_branch; + int pos = MAX_GRID_Y_POS; + for (int i = 0; i < (int)Children[parent+1].size(); ++i) + { + GridY[Children[parent+1][i]] = pos; + pos -= 2; + } + } else if (GridY[t] < -MAX_GRID_Y_POS) + { + if (t < TOTAL_BOOKMARKS) + parent = parents[t]; + else + parent = current_branch; + int pos = -MAX_GRID_Y_POS; + for (int i = Children[parent+1].size()-1; i >= 0; i--) + { + GridY[Children[parent+1][i]] = pos; + pos += 2; + } + } + } + // special case 4: if cloud has all 10 children, then one child will be out of canvas + if (Children[0].size() == TOTAL_BOOKMARKS) + { + // find this child and move it to be visible + for (int t = TOTAL_BOOKMARKS - 1; t >= 0; t--) + { + if (GridY[t] > MAX_GRID_Y_POS) + { + GridY[t] = MAX_GRID_Y_POS; + GridX[t] -= 2; + // also move fireball to position near this branch + if (changes_since_current_branch && current_branch == t) + { + GridY[TOTAL_BOOKMARKS] = GridY[t]; + GridX[TOTAL_BOOKMARKS] = GridX[t] + 1; + } + break; + } else if (GridY[t] < -MAX_GRID_Y_POS) + { + GridY[t] = -MAX_GRID_Y_POS; + GridX[t] -= 2; + // also move fireball to position near this branch + if (changes_since_current_branch && current_branch == t) + { + GridY[TOTAL_BOOKMARKS] = GridY[t]; + GridX[TOTAL_BOOKMARKS] = GridX[t] + 1; + } + break; + } + } + } + + // 3. Set pixel positions of branches + int max_x = 0; + for (int i = TOTAL_BOOKMARKS-1; i >= 0; i--) + { + if (bookmarks.bookmarks_array[i].not_empty) + { + BranchX[i] = cloud_prefix + GridX[i] * grid_width; + BranchY[i] = BRANCHES_CLOUD_Y + GridY[i] * grid_halfheight; + } else + { + BranchX[i] = EMPTY_BRANCHES_X; + BranchY[i] = EMPTY_BRANCHES_Y_BASE + EMPTY_BRANCHES_Y_FACTOR * ((i + TOTAL_BOOKMARKS - 1) % TOTAL_BOOKMARKS); + } + if (max_x < BranchX[i]) max_x = BranchX[i]; + } + if (changes_since_current_branch) + { + // also set pixel position of "current_pos" + BranchX[TOTAL_BOOKMARKS] = cloud_prefix + GridX[TOTAL_BOOKMARKS] * grid_width; + BranchY[TOTAL_BOOKMARKS] = BRANCHES_CLOUD_Y + GridY[TOTAL_BOOKMARKS] * grid_halfheight; + } else if (current_branch >= 0) + { + BranchX[TOTAL_BOOKMARKS] = cloud_prefix + GridX[current_branch] * grid_width; + BranchY[TOTAL_BOOKMARKS] = BRANCHES_CLOUD_Y + GridY[current_branch] * grid_halfheight; + } else + { + BranchX[TOTAL_BOOKMARKS] = 0; + BranchY[TOTAL_BOOKMARKS] = BRANCHES_CLOUD_Y; + } + if (max_x < BranchX[TOTAL_BOOKMARKS]) max_x = BranchX[TOTAL_BOOKMARKS]; + // align whole tree horizontally + CloudX = (BRANCHES_BITMAP_WIDTH + 1 - max_x) / 2; + if (CloudX < 0) CloudX = 0; + for (int i = TOTAL_BOOKMARKS-1; i >= 0; i--) + if (bookmarks.bookmarks_array[i].not_empty) + BranchX[i] += CloudX; + BranchX[TOTAL_BOOKMARKS] += CloudX; + // target cursor + CursorX = BranchX[TOTAL_BOOKMARKS]; + CursorY = BranchY[TOTAL_BOOKMARKS]; + // finished recalculating + must_recalculate_branches_tree = false; + must_redraw_branches_tree = true; +} +void BRANCHES::RecursiveAddHeight(int branch_num, int amount) +{ + if (branch_num >= 0) + { + GridHeight[branch_num] += amount; + if (parents[branch_num] >= 0) + RecursiveAddHeight(parents[branch_num], amount); + } +} +void BRANCHES::RecursiveSetYPos(int parent, int parentY) +{ + if (Children[parent+1].size()) + { + // find total height of children + int totalHeight = 0; + for (int i = Children[parent+1].size()-1; i >= 0; i--) + totalHeight += GridHeight[Children[parent+1][i]]; + // set Y of children and subchildren + for (int i = Children[parent+1].size()-1; i >= 0; i--) + { + int child_id = Children[parent+1][i]; + GridY[child_id] = parentY + GridHeight[child_id] - totalHeight; + RecursiveSetYPos(child_id, GridY[child_id]); + parentY += 2 * GridHeight[child_id]; + } + } +} +// ---------------------------------------------------------------------------------------- +LRESULT APIENTRY BranchesBitmapWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + extern BRANCHES branches; + switch(msg) + { + case WM_MOUSEMOVE: + { + if (!bookmarks.mouse_over_bitmap) + { + bookmarks.mouse_over_bitmap = true; + bookmarks.tme.hwndTrack = hWnd; + TrackMouseEvent(&bookmarks.tme); + } + bookmarks.MouseMove(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + break; + } + case WM_MOUSELEAVE: + { + bookmarks.mouse_over_bitmap = false; + bookmarks.MouseMove(-1, -1); + break; + } + case WM_PAINT: + { + PAINTSTRUCT ps; + branches.PaintBranchesBitmap(BeginPaint(hWnd, &ps)); + EndPaint(hWnd, &ps); + return 0; + } + case WM_LBUTTONDOWN: + case WM_LBUTTONDBLCLK: + case WM_RBUTTONDOWN: + case WM_RBUTTONDBLCLK: + { + if (GetFocus() != hWnd) + SetFocus(hWnd); + return 0; + } + case WM_MBUTTONDOWN: + case WM_MBUTTONDBLCLK: + { + if (GetFocus() != hWnd) + SetFocus(hWnd); + playback.MiddleButtonClick(); + return 0; + } + case WM_MOUSEWHEEL: + return SendMessage(piano_roll.hwndList, msg, wParam, lParam); + + } + return CallWindowProc(hwndBranchesBitmap_oldWndProc, hWnd, msg, wParam, lParam); +} + + diff --git a/src/drivers/win/taseditor/branches.h b/src/drivers/win/taseditor/branches.h new file mode 100644 index 00000000..29ba0db6 --- /dev/null +++ b/src/drivers/win/taseditor/branches.h @@ -0,0 +1,146 @@ +// Specification file for Branches class + +#define BRANCHES_ANIMATION_TICK 50 // animate at 20FPS +#define BRANCHES_TRANSITION_MAX 8 + +// branches bitmap +#define BRANCHES_BITMAP_WIDTH 170 +#define BRANCHES_BITMAP_HEIGHT 145 +#define BRANCHES_ANIMATION_FRAMES 12 +#define BRANCHES_BITMAP_FRAMENUM_X 2 +#define BRANCHES_BITMAP_FRAMENUM_Y 1 +#define BRANCHES_BITMAP_TIME_X 2 +#define BRANCHES_BITMAP_TIME_Y BRANCHES_BITMAP_HEIGHT - 17 +#define BRANCHES_TEXT_SHADOW_COLOR 0xFFFFFF +#define BRANCHES_TEXT_COLOR 0x7F0000 +// constants for drawing branches tree +#define BRANCHES_CANVAS_WIDTH 146 +#define BRANCHES_CANVAS_HEIGHT 130 +#define BRANCHES_CLOUD_X 14 +#define BRANCHES_CLOUD_Y 72 +#define BRANCHES_GRID_MIN_WIDTH 14 +#define BRANCHES_GRID_MAX_WIDTH 30 +#define MIN_CLOUD_LINE_LENGTH 19 +#define BRANCHES_GRID_MIN_HALFHEIGHT 8 +#define BRANCHES_GRID_MAX_HALFHEIGHT 12 +#define EMPTY_BRANCHES_X -6 +#define EMPTY_BRANCHES_Y_BASE 8 +#define EMPTY_BRANCHES_Y_FACTOR 14 +#define MAX_NUM_CHILDREN_ON_CANVAS_HEIGHT 9 +#define MAX_CHAIN_LEN 10 +#define MAX_GRID_Y_POS 8 +// spritesheet +#define DIGIT_BITMAP_WIDTH 9 +#define DIGIT_BITMAP_HALFWIDTH DIGIT_BITMAP_WIDTH/2 +#define DIGIT_BITMAP_HEIGHT 13 +#define DIGIT_BITMAP_HALFHEIGHT DIGIT_BITMAP_HEIGHT/2 +#define BLUE_DIGITS_SPRITESHEET_DX DIGIT_BITMAP_WIDTH*TOTAL_BOOKMARKS +#define MOUSEOVER_DIGITS_SPRITESHEET_DY DIGIT_BITMAP_HEIGHT +#define DIGIT_RECT_WIDTH 11 +#define DIGIT_RECT_WIDTH_COLLISION (DIGIT_RECT_WIDTH + 2) +#define DIGIT_RECT_HALFWIDTH DIGIT_RECT_WIDTH/2 +#define DIGIT_RECT_HALFWIDTH_COLLISION (DIGIT_RECT_HALFWIDTH + 1) +#define DIGIT_RECT_HEIGHT 15 +#define DIGIT_RECT_HEIGHT_COLLISION (DIGIT_RECT_HEIGHT + 2) +#define DIGIT_RECT_HALFHEIGHT DIGIT_RECT_HEIGHT/2 +#define DIGIT_RECT_HALFHEIGHT_COLLISION (DIGIT_RECT_HALFHEIGHT + 1) +#define BRANCHES_CLOUD_WIDTH 26 +#define BRANCHES_CLOUD_HALFWIDTH BRANCHES_CLOUD_WIDTH/2 +#define BRANCHES_CLOUD_HEIGHT 15 +#define BRANCHES_CLOUD_HALFHEIGHT BRANCHES_CLOUD_HEIGHT/2 +#define BRANCHES_CLOUD_SPRITESHEET_X 180 +#define BRANCHES_CLOUD_SPRITESHEET_Y 0 +#define BRANCHES_FIREBALL_WIDTH 10 +#define BRANCHES_FIREBALL_HALFWIDTH BRANCHES_FIREBALL_WIDTH/2 +#define BRANCHES_FIREBALL_HEIGHT 10 +#define BRANCHES_FIREBALL_HALFHEIGHT BRANCHES_FIREBALL_HEIGHT/2 +#define BRANCHES_FIREBALL_SPRITESHEET_X 0 +#define BRANCHES_FIREBALL_SPRITESHEET_Y 26 +#define BRANCHES_FIREBALL_MAX_SIZE 5 +#define BRANCHES_FIREBALL_SPRITESHEET_END_X 160 +#define BRANCHES_CORNER_WIDTH 7 +#define BRANCHES_CORNER_HALFWIDTH BRANCHES_CORNER_WIDTH/2 +#define BRANCHES_CORNER_HEIGHT 7 +#define BRANCHES_CORNER_HALFHEIGHT BRANCHES_CORNER_HEIGHT/2 +#define BRANCHES_CORNER1_SPRITESHEET_X 206 +#define BRANCHES_CORNER1_SPRITESHEET_Y 0 +#define BRANCHES_CORNER2_SPRITESHEET_X 213 +#define BRANCHES_CORNER2_SPRITESHEET_Y 0 +#define BRANCHES_CORNER3_SPRITESHEET_X 206 +#define BRANCHES_CORNER3_SPRITESHEET_Y 7 +#define BRANCHES_CORNER4_SPRITESHEET_X 213 +#define BRANCHES_CORNER4_SPRITESHEET_Y 7 +#define BRANCHES_CORNER_BASE_SHIFT 6 + +class BRANCHES +{ +public: + BRANCHES(); + void init(); + void free(); + void reset(); + void reset_vars(); + void update(); + + void save(EMUFILE *os); + bool load(EMUFILE *is); + + int GetCurrentBranch(); + + void RedrawBranchesTree(); + void PaintBranchesBitmap(HDC hdc); + + void HandleBookmarkSet(int slot, char* slot_time); + void HandleBookmarkDeploy(int slot); + void ChangesMadeSinceBranch(); + + void FindItemUnderMouse(int mouse_x, int mouse_y); + + void RecalculateBranchesTree(); + void RecursiveAddHeight(int branch_num, int amount); + void RecursiveSetYPos(int parent, int parentY); + + // saved vars + std::vector parents; + + // not saved vars + bool must_redraw_branches_tree; + bool must_recalculate_branches_tree; + +private: + void SetCurrentPosTime(); + + // also saved vars + int current_branch; + bool changes_since_current_branch; + char cloud_time[TIME_DESC_LENGTH]; + char current_pos_time[TIME_DESC_LENGTH]; + + // not saved vars + int animation_frame; + int next_animation_time; + std::vector BranchX; // in pixels + std::vector BranchY; + std::vector BranchPrevX; + std::vector BranchPrevY; + std::vector BranchCurrX; + std::vector BranchCurrY; + int CloudX, CloudPrevX, cloud_x; + int CursorX, CursorPrevX, CursorY, CursorPrevY; + int transition_phase; + int fireball_size; + + // GDI stuff + HBRUSH normal_brush; + RECT temp_rect; + HPEN normal_pen, select_pen; + HBITMAP branches_hbitmap, hOldBitmap, buffer_hbitmap, hOldBitmap1, branchesSpritesheet, hOldBitmap2; + HDC hBitmapDC, hBufferDC, hSpritesheetDC; + + // temps + std::vector GridX; // in grid units + std::vector GridY; + std::vector GridHeight; + std::vector> Children; + +}; diff --git a/src/drivers/win/taseditor/editor.cpp b/src/drivers/win/taseditor/editor.cpp index eeec83ec..b67f23eb 100644 --- a/src/drivers/win/taseditor/editor.cpp +++ b/src/drivers/win/taseditor/editor.cpp @@ -152,7 +152,7 @@ bool EDITOR::ReadString(EMUFILE *is, std::string& dest) } // ---------------------------------------------------------------------------------------------- // following functions use function parameters to determine range of frames -void EDITOR::InputToggle(int start, int end, int joy, int button) +void EDITOR::InputToggle(int start, int end, int joy, int button, int consecutive_tag) { if (joy < 0 || joy >= joysticks_per_frame[GetInputType(currMovieData)]) return; @@ -173,16 +173,16 @@ void EDITOR::InputToggle(int start, int end, int joy, int button) // clear range for (int i = start; i <= end; ++i) currMovieData.records[i].clearBit(joy, button); - greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_UNSET, start, end)); + greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_UNSET, start, end, NULL, consecutive_tag)); } else { // set range for (int i = start; i <= end; ++i) currMovieData.records[i].setBit(joy, button); - greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_SET, start, end)); + greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_SET, start, end, NULL, consecutive_tag)); } } -void EDITOR::InputSetPattern(int start, int end, int joy, int button) +void EDITOR::InputSetPattern(int start, int end, int joy, int button, int consecutive_tag) { if (joy < 0 || joy >= joysticks_per_frame[GetInputType(currMovieData)]) return; @@ -209,7 +209,7 @@ void EDITOR::InputSetPattern(int start, int end, int joy, int button) if (pattern_offset >= (int)autofire_patterns[current_pattern].size()) pattern_offset -= autofire_patterns[current_pattern].size(); } - greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_PATTERN, start, end, autofire_patterns_names[current_pattern].c_str())); + greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_PATTERN, start, end, autofire_patterns_names[current_pattern].c_str(), consecutive_tag)); } // following functions use current Selection to determine range of frames diff --git a/src/drivers/win/taseditor/editor.h b/src/drivers/win/taseditor/editor.h index 2d779018..2e284bb3 100644 --- a/src/drivers/win/taseditor/editor.h +++ b/src/drivers/win/taseditor/editor.h @@ -9,8 +9,8 @@ public: void reset(); void update(); - void InputToggle(int start, int end, int joy, int button); - void InputSetPattern(int start, int end, int joy, int button); + void InputToggle(int start, int end, int joy, int button, int consecutive_tag = 0); + void InputSetPattern(int start, int end, int joy, int button, int consecutive_tag = 0); bool FrameColumnSet(); bool FrameColumnSetPattern(); diff --git a/src/drivers/win/taseditor/history.cpp b/src/drivers/win/taseditor/history.cpp index 6543975b..5e6565e3 100644 --- a/src/drivers/win/taseditor/history.cpp +++ b/src/drivers/win/taseditor/history.cpp @@ -29,6 +29,7 @@ extern TASEDITOR_CONFIG taseditor_config; extern TASEDITOR_WINDOW taseditor_window; extern MARKERS_MANAGER markers_manager; extern BOOKMARKS bookmarks; +extern BRANCHES branches; extern PLAYBACK playback; extern SELECTION selection; extern GREENZONE greenzone; @@ -130,6 +131,8 @@ void HISTORY::reset() inp.init(currMovieData, taseditor_config.enable_hot_changes); strcat(inp.description, modCaptions[MODTYPE_INIT]); inp.jump_frame = -1; + inp.start_frame = 0; + inp.end_frame = inp.size - 1; AddSnapshotToHistory(inp); UpdateHistoryList(); RedrawHistoryList(); @@ -224,13 +227,13 @@ int HISTORY::jump(int new_pos) { snapshots[real_pos].toMovie(currMovieData, first_change); selection.must_find_current_marker = playback.must_find_current_marker = true; - bookmarks.ChangesMadeSinceBranch(); + branches.ChangesMadeSinceBranch(); // and Piano Roll will be redrawn by greenzone invalidation } else if (markers_changed) { markers_manager.update(); selection.must_find_current_marker = playback.must_find_current_marker = true; - bookmarks.ChangesMadeSinceBranch(); + branches.ChangesMadeSinceBranch(); piano_roll.RedrawList(); piano_roll.FollowUndo(); } else if (taseditor_config.enable_hot_changes) @@ -287,12 +290,11 @@ void HISTORY::AddSnapshotToHistory(SNAPSHOT &inp) } // returns frame of first actual change -int HISTORY::RegisterChanges(int mod_type, int start, int end, const char* comment) +int HISTORY::RegisterChanges(int mod_type, int start, int end, const char* comment, int consecutive_tag) { // create new shanshot SNAPSHOT inp; inp.init(currMovieData, taseditor_config.enable_hot_changes); - inp.mod_type = mod_type; // check if there are input differences from latest snapshot int real_pos = (history_start_pos + history_cursor_pos) % history_size; int first_changes = inp.findFirstChange(snapshots[real_pos], start, end); @@ -300,7 +302,8 @@ int HISTORY::RegisterChanges(int mod_type, int start, int end, const char* comme { // differences found // fill description: - strcat(inp.description, modCaptions[mod_type]); + inp.mod_type = mod_type; + strcat(inp.description, modCaptions[inp.mod_type]); switch (mod_type) { case MODTYPE_SET: @@ -323,53 +326,97 @@ int HISTORY::RegisterChanges(int mod_type, int start, int end, const char* comme break; } } - // add upper and lower frame to description - char framenum[11]; - _itoa(start, framenum, 10); - strcat(inp.description, " "); - strcat(inp.description, framenum); - if (end > start) - { - _itoa(end, framenum, 10); - strcat(inp.description, "-"); - strcat(inp.description, framenum); - } - // add comment if there is one specified - if (comment) + inp.start_frame = start; + inp.end_frame = end; + inp.consecutive_tag = consecutive_tag; + if (consecutive_tag && taseditor_config.combine_consecutive && snapshots[real_pos].mod_type == inp.mod_type && snapshots[real_pos].consecutive_tag == inp.consecutive_tag) { + // combine with previous snapshot + if (inp.jump_frame > snapshots[real_pos].jump_frame) + inp.jump_frame = snapshots[real_pos].jump_frame; + if (inp.start_frame > snapshots[real_pos].start_frame) + inp.start_frame = snapshots[real_pos].start_frame; + if (inp.end_frame < snapshots[real_pos].end_frame) + inp.end_frame = snapshots[real_pos].end_frame; + // add upper and lower frame to description + char framenum[11]; strcat(inp.description, " "); - strncat(inp.description, comment, SNAPSHOT_DESC_MAX_LENGTH - strlen(inp.description) - 1); - } - // set hotchanges - if (taseditor_config.enable_hot_changes) - { - // inherit previous hotchanges and set new changes - switch (mod_type) + _itoa(inp.start_frame, framenum, 10); + strcat(inp.description, framenum); + if (inp.end_frame > inp.start_frame) { - case MODTYPE_DELETE: - inp.inheritHotChanges_DeleteSelection(&snapshots[real_pos]); - break; - case MODTYPE_INSERT: - case MODTYPE_CLONE: - inp.inheritHotChanges_InsertSelection(&snapshots[real_pos]); - break; - case MODTYPE_SET: - case MODTYPE_UNSET: - case MODTYPE_CLEAR: - case MODTYPE_CUT: - case MODTYPE_PASTE: - case MODTYPE_PATTERN: - inp.inheritHotChanges(&snapshots[real_pos]); - inp.fillHotChanges(snapshots[real_pos], first_changes, end); - break; - case MODTYPE_TRUNCATE: - inp.copyHotChanges(&snapshots[real_pos]); - // do not add new hotchanges and do not fade old hotchanges, because there was nothing added - break; + strcat(inp.description, "-"); + _itoa(inp.end_frame, framenum, 10); + strcat(inp.description, framenum); } + // add comment if there is one specified + if (comment) + { + strcat(inp.description, " "); + strncat(inp.description, comment, SNAPSHOT_DESC_MAX_LENGTH - strlen(inp.description) - 1); + } + // set hotchanges + if (taseditor_config.enable_hot_changes) + { + inp.copyHotChanges(&snapshots[real_pos]); + inp.fillHotChanges(snapshots[real_pos], first_changes, end); + } + // replace current snapshot with this cloned snapshot and truncate history here + snapshots[real_pos] = inp; + history_total_items = history_cursor_pos+1; + UpdateHistoryList(); + RedrawHistoryList(); + } else + { + // don't combine + // add upper and lower frame to description + char framenum[11]; + strcat(inp.description, " "); + _itoa(inp.start_frame, framenum, 10); + strcat(inp.description, framenum); + if (inp.end_frame > inp.start_frame) + { + strcat(inp.description, "-"); + _itoa(inp.end_frame, framenum, 10); + strcat(inp.description, framenum); + } + // add comment if there is one specified + if (comment) + { + strcat(inp.description, " "); + strncat(inp.description, comment, SNAPSHOT_DESC_MAX_LENGTH - strlen(inp.description) - 1); + } + // set hotchanges + if (taseditor_config.enable_hot_changes) + { + // inherit previous hotchanges and set new changes + switch (mod_type) + { + case MODTYPE_DELETE: + inp.inheritHotChanges_DeleteSelection(&snapshots[real_pos]); + break; + case MODTYPE_INSERT: + case MODTYPE_CLONE: + inp.inheritHotChanges_InsertSelection(&snapshots[real_pos]); + break; + case MODTYPE_SET: + case MODTYPE_UNSET: + case MODTYPE_CLEAR: + case MODTYPE_CUT: + case MODTYPE_PASTE: + case MODTYPE_PATTERN: + inp.inheritHotChanges(&snapshots[real_pos]); + inp.fillHotChanges(snapshots[real_pos], first_changes, end); + break; + case MODTYPE_TRUNCATE: + inp.copyHotChanges(&snapshots[real_pos]); + // do not add new hotchanges and do not fade old hotchanges, because there was nothing added + break; + } + } + AddSnapshotToHistory(inp); } - AddSnapshotToHistory(inp); - bookmarks.ChangesMadeSinceBranch(); + branches.ChangesMadeSinceBranch(); } return first_changes; } @@ -378,7 +425,6 @@ int HISTORY::RegisterInsertNum(int start, int frames) // create new shanshot SNAPSHOT inp; inp.init(currMovieData, taseditor_config.enable_hot_changes); - inp.mod_type = MODTYPE_INSERTNUM; // check if there are input differences from latest snapshot int real_pos = (history_start_pos + history_cursor_pos) % history_size; int first_changes = inp.findFirstChange(snapshots[real_pos], start); @@ -386,21 +432,24 @@ int HISTORY::RegisterInsertNum(int start, int frames) { // differences found // fill description: + inp.mod_type = MODTYPE_INSERTNUM; strcat(inp.description, modCaptions[inp.mod_type]); inp.jump_frame = start; + inp.start_frame = start; + inp.end_frame = start + frames - 1; char framenum[11]; // add number of inserted frames to description _itoa(frames, framenum, 10); strcat(inp.description, framenum); // add upper frame to description - _itoa(start, framenum, 10); strcat(inp.description, " "); + _itoa(inp.start_frame, framenum, 10); strcat(inp.description, framenum); // set hotchanges if (taseditor_config.enable_hot_changes) inp.inheritHotChanges_InsertNum(&snapshots[real_pos], start, frames); AddSnapshotToHistory(inp); - bookmarks.ChangesMadeSinceBranch(); + branches.ChangesMadeSinceBranch(); } return first_changes; } @@ -409,7 +458,6 @@ int HISTORY::RegisterPasteInsert(int start, SelectionFrames& inserted_set) // create new shanshot SNAPSHOT inp; inp.init(currMovieData, taseditor_config.enable_hot_changes); - inp.mod_type = MODTYPE_PASTEINSERT; // check if there are input differences from latest snapshot int real_pos = (history_start_pos + history_cursor_pos) % history_size; int first_changes = inp.findFirstChange(snapshots[real_pos], start); @@ -417,19 +465,22 @@ int HISTORY::RegisterPasteInsert(int start, SelectionFrames& inserted_set) { // differences found // fill description: + inp.mod_type = MODTYPE_PASTEINSERT; strcat(inp.description, modCaptions[inp.mod_type]); // for PasteInsert user prefers to see frame of attempted change (selection beginning), not frame of actual differences inp.jump_frame = start; + inp.start_frame = start; + inp.end_frame = -1; // add upper frame to description char framenum[11]; - _itoa(start, framenum, 10); strcat(inp.description, " "); + _itoa(inp.start_frame, framenum, 10); strcat(inp.description, framenum); // set hotchanges if (taseditor_config.enable_hot_changes) inp.inheritHotChanges_PasteInsert(&snapshots[real_pos], inserted_set); AddSnapshotToHistory(inp); - bookmarks.ChangesMadeSinceBranch(); + branches.ChangesMadeSinceBranch(); } return first_changes; } @@ -438,16 +489,18 @@ void HISTORY::RegisterMarkersChange(int mod_type, int start, int end, const char // create new shanshot SNAPSHOT inp; inp.init(currMovieData, taseditor_config.enable_hot_changes); - inp.mod_type = mod_type; // fill description: + inp.mod_type = mod_type; strcat(inp.description, modCaptions[mod_type]); inp.jump_frame = start; + inp.start_frame = start; + inp.end_frame = end; // add the frame to description char framenum[11]; - _itoa(start, framenum, 10); strcat(inp.description, " "); + _itoa(inp.start_frame, framenum, 10); strcat(inp.description, framenum); - if (end > start || mod_type == MODTYPE_MARKER_DRAG || mod_type == MODTYPE_MARKER_SWAP) + if (inp.end_frame > inp.start_frame || mod_type == MODTYPE_MARKER_DRAG || mod_type == MODTYPE_MARKER_SWAP) { if (mod_type == MODTYPE_MARKER_DRAG) strcat(inp.description, "=>"); @@ -455,7 +508,7 @@ void HISTORY::RegisterMarkersChange(int mod_type, int start, int end, const char strcat(inp.description, "<=>"); else strcat(inp.description, "-"); - _itoa(end, framenum, 10); + _itoa(inp.end_frame, framenum, 10); strcat(inp.description, framenum); } // add comment if there is one specified @@ -468,7 +521,7 @@ void HISTORY::RegisterMarkersChange(int mod_type, int start, int end, const char if (taseditor_config.enable_hot_changes) inp.copyHotChanges(&GetCurrentSnapshot()); AddSnapshotToHistory(inp); - bookmarks.ChangesMadeSinceBranch(); + branches.ChangesMadeSinceBranch(); project.SetProjectChanged(); } void HISTORY::RegisterBranching(int mod_type, int first_change, int slot) @@ -481,6 +534,8 @@ void HISTORY::RegisterBranching(int mod_type, int first_change, int slot) strcat(inp.description, modCaptions[mod_type]); strcat(inp.description, bookmarks.bookmarks_array[slot].snapshot.description); inp.jump_frame = first_change; + inp.start_frame = first_change; + inp.end_frame = -1; if (taseditor_config.enable_hot_changes) { if (mod_type < MODTYPE_BRANCH_MARKERS_0) @@ -509,18 +564,21 @@ void HISTORY::RegisterRecording(int frame_of_change) SNAPSHOT inp; inp.init(currMovieData, taseditor_config.enable_hot_changes); inp.fillJoypadsDiff(snapshots[real_pos], frame_of_change); + // fill description: inp.mod_type = MODTYPE_RECORD; strcat(inp.description, modCaptions[MODTYPE_RECORD]); char framenum[11]; // check if current snapshot is also Recording and maybe it is consecutive recording - if (taseditor_config.combine_consecutive_rec + if (taseditor_config.combine_consecutive && snapshots[real_pos].mod_type == MODTYPE_RECORD // a) also Recording - && snapshots[real_pos].rec_end_frame+1 == frame_of_change // b) consecutive + && snapshots[real_pos].consecutive_tag == frame_of_change - 1 // b) consecutive (previous frame) && snapshots[real_pos].rec_joypad_diff_bits == inp.rec_joypad_diff_bits) // c) recorded same set of joysticks { // clone this snapshot and continue chain of recorded frames inp.jump_frame = snapshots[real_pos].jump_frame; - inp.rec_end_frame = frame_of_change; + inp.start_frame = snapshots[real_pos].jump_frame; + inp.end_frame = frame_of_change; + inp.consecutive_tag = frame_of_change; // add info which joypads were affected int num = joysticks_per_frame[inp.input_type]; uint32 current_mask = 1; @@ -531,11 +589,11 @@ void HISTORY::RegisterRecording(int frame_of_change) current_mask <<= 1; } // add upper and lower frame to description - _itoa(inp.jump_frame, framenum, 10); strcat(inp.description, " "); + _itoa(inp.start_frame, framenum, 10); strcat(inp.description, framenum); - _itoa(frame_of_change, framenum, 10); strcat(inp.description, "-"); + _itoa(inp.end_frame, framenum, 10); strcat(inp.description, framenum); // set hotchanges if (taseditor_config.enable_hot_changes) @@ -551,7 +609,7 @@ void HISTORY::RegisterRecording(int frame_of_change) } else { // not consecutive - add new snapshot to history - inp.jump_frame = inp.rec_end_frame = frame_of_change; + inp.jump_frame = inp.start_frame = inp.end_frame = inp.consecutive_tag = frame_of_change; // add info which joypads were affected int num = joysticks_per_frame[inp.input_type]; uint32 current_mask = 1; @@ -562,8 +620,8 @@ void HISTORY::RegisterRecording(int frame_of_change) current_mask <<= 1; } // add upper frame to description - _itoa(frame_of_change, framenum, 10); strcat(inp.description, " "); + _itoa(frame_of_change, framenum, 10); strcat(inp.description, framenum); // set hotchanges if (taseditor_config.enable_hot_changes) @@ -573,7 +631,7 @@ void HISTORY::RegisterRecording(int frame_of_change) } AddSnapshotToHistory(inp); } - bookmarks.ChangesMadeSinceBranch(); + branches.ChangesMadeSinceBranch(); } void HISTORY::RegisterImport(MovieData& md, char* filename) { @@ -586,9 +644,11 @@ void HISTORY::RegisterImport(MovieData& md, char* filename) if (first_changes >= 0) { // differences found - inp.mod_type = MODTYPE_IMPORT; inp.jump_frame = first_changes; + inp.start_frame = 0; + inp.end_frame = inp.size - 1; // fill description: + inp.mod_type = MODTYPE_IMPORT; strcat(inp.description, modCaptions[inp.mod_type]); // add filename to description strcat(inp.description, " "); @@ -601,7 +661,7 @@ void HISTORY::RegisterImport(MovieData& md, char* filename) AddSnapshotToHistory(inp); inp.toMovie(currMovieData); piano_roll.UpdateItemCount(); - bookmarks.ChangesMadeSinceBranch(); + branches.ChangesMadeSinceBranch(); project.SetProjectChanged(); greenzone.InvalidateAndCheck(first_changes); } else @@ -614,7 +674,6 @@ int HISTORY::RegisterLuaChanges(const char* name, int start, bool InsertionDelet // create new shanshot SNAPSHOT inp; inp.init(currMovieData, taseditor_config.enable_hot_changes); - inp.mod_type = MODTYPE_LUA_CHANGE; // check if there are input differences from latest snapshot int real_pos = (history_start_pos + history_cursor_pos) % history_size; int first_changes = inp.findFirstChange(snapshots[real_pos], start); @@ -622,6 +681,7 @@ int HISTORY::RegisterLuaChanges(const char* name, int start, bool InsertionDelet { // differences found // fill description: + inp.mod_type = MODTYPE_LUA_CHANGE; if (name[0]) { // user provided custom name of operation @@ -633,10 +693,12 @@ int HISTORY::RegisterLuaChanges(const char* name, int start, bool InsertionDelet strcat(inp.description, modCaptions[inp.mod_type]); } inp.jump_frame = first_changes; + inp.start_frame = start; + inp.end_frame = -1; // add upper frame to description char framenum[11]; - _itoa(first_changes, framenum, 10); strcat(inp.description, " "); + _itoa(first_changes, framenum, 10); strcat(inp.description, framenum); // set hotchanges if (taseditor_config.enable_hot_changes) @@ -669,7 +731,7 @@ int HISTORY::RegisterLuaChanges(const char* name, int start, bool InsertionDelet } } AddSnapshotToHistory(inp); - bookmarks.ChangesMadeSinceBranch(); + branches.ChangesMadeSinceBranch(); } return first_changes; } diff --git a/src/drivers/win/taseditor/history.h b/src/drivers/win/taseditor/history.h index 7228b640..ebe3ac25 100644 --- a/src/drivers/win/taseditor/history.h +++ b/src/drivers/win/taseditor/history.h @@ -79,7 +79,7 @@ public: void undo(); void redo(); - int RegisterChanges(int mod_type, int start = 0, int end =-1, const char* comment = 0); + int RegisterChanges(int mod_type, int start = 0, int end =-1, const char* comment = NULL, int consecutive_tag = 0); int RegisterInsertNum(int start, int frames); int RegisterPasteInsert(int start, SelectionFrames& inserted_set); void RegisterMarkersChange(int mod_type, int start = 0, int end =-1, const char* comment = 0); diff --git a/src/drivers/win/taseditor/piano_roll.cpp b/src/drivers/win/taseditor/piano_roll.cpp index 3757523e..5571c7a2 100644 --- a/src/drivers/win/taseditor/piano_roll.cpp +++ b/src/drivers/win/taseditor/piano_roll.cpp @@ -550,7 +550,8 @@ void PIANO_ROLL::update() int row_index, column_index, joy, bit; int min_row_index = currMovieData.getNumRecords(), max_row_index = -1; bool changes_made = false; - for (double len = 0; len < total_len; len += DRAWING_MIN_LINE_LEN) + int drawing_min_line_len = list_row_height; // = min(list_row_width, list_row_height) in pixels + for (double len = 0; len < total_len; len += drawing_min_line_len) { // perform hit test info.pt.x = p.x + (len / total_len) * total_dx; @@ -580,9 +581,9 @@ void PIANO_ROLL::update() if (changes_made) { if (drag_mode == DRAG_MODE_SET) - greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_SET, min_row_index, max_row_index)); + greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_SET, min_row_index, max_row_index, NULL, drawing_start_time)); else - greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_UNSET, min_row_index, max_row_index)); + greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_UNSET, min_row_index, max_row_index, NULL, drawing_start_time)); } drawing_last_x = drawing_current_x; drawing_last_y = drawing_current_y; @@ -1301,34 +1302,34 @@ LONG PIANO_ROLL::HeaderCustomDraw(NMLVCUSTOMDRAW* msg) // ---------------------------------------------------- void PIANO_ROLL::RightClick(LVHITTESTINFO& info) { - SelectionFrames* current_selection = selection.MakeStrobe(); - if (current_selection->size() == 0) - return; - - HMENU sub = GetSubMenu(hrmenu, 0); - // inspect current selection and disable inappropriate menu items - SelectionFrames::iterator current_selection_begin(current_selection->begin()); - SelectionFrames::iterator current_selection_end(current_selection->end()); - bool set_found = false, unset_found = false; - for(SelectionFrames::iterator it(current_selection_begin); it != current_selection_end; it++) + if (selection.CheckFrameSelected(info.iItem)) { - if(markers_manager.GetMarker(*it)) - set_found = true; - else - unset_found = true; - } - if (set_found) - EnableMenuItem(sub, ID_SELECTED_REMOVEMARKER, MF_BYCOMMAND | MF_ENABLED); - else - EnableMenuItem(sub, ID_SELECTED_REMOVEMARKER, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); - if (unset_found) - EnableMenuItem(sub, ID_SELECTED_SETMARKER, MF_BYCOMMAND | MF_ENABLED); - else - EnableMenuItem(sub, ID_SELECTED_SETMARKER, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); + SelectionFrames* current_selection = selection.MakeStrobe(); + HMENU sub = GetSubMenu(hrmenu, 0); + // inspect current selection and disable inappropriate menu items + SelectionFrames::iterator current_selection_begin(current_selection->begin()); + SelectionFrames::iterator current_selection_end(current_selection->end()); + bool set_found = false, unset_found = false; + for(SelectionFrames::iterator it(current_selection_begin); it != current_selection_end; it++) + { + if(markers_manager.GetMarker(*it)) + set_found = true; + else + unset_found = true; + } + if (set_found) + EnableMenuItem(sub, ID_SELECTED_REMOVEMARKER, MF_BYCOMMAND | MF_ENABLED); + else + EnableMenuItem(sub, ID_SELECTED_REMOVEMARKER, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); + if (unset_found) + EnableMenuItem(sub, ID_SELECTED_SETMARKER, MF_BYCOMMAND | MF_ENABLED); + else + EnableMenuItem(sub, ID_SELECTED_SETMARKER, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED); - POINT pt = info.pt; - ClientToScreen(hwndList, &pt); - TrackPopupMenu(sub, 0, pt.x, pt.y, 0, taseditor_window.hwndTasEditor, 0); + POINT pt = info.pt; + ClientToScreen(hwndList, &pt); + TrackPopupMenu(sub, 0, pt.x, pt.y, 0, taseditor_window.hwndTasEditor, 0); + } } // ------------------------------------------------------------------------- LRESULT APIENTRY HeaderWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) @@ -1510,12 +1511,13 @@ LRESULT APIENTRY ListWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) } } // toggle input + piano_roll.drawing_start_time = clock(); int joy = (column_index - COLUMN_JOYPAD1_A) / NUM_JOYPAD_BUTTONS; int button = (column_index - COLUMN_JOYPAD1_A) % NUM_JOYPAD_BUTTONS; if (alt_pressed) - editor.InputSetPattern(piano_roll.row_last_clicked, row_index, joy, button); + editor.InputSetPattern(piano_roll.row_last_clicked, row_index, joy, button, piano_roll.drawing_start_time); else - editor.InputToggle(piano_roll.row_last_clicked, row_index, joy, button); + editor.InputToggle(piano_roll.row_last_clicked, row_index, joy, button, piano_roll.drawing_start_time); // and start dragging/drawing if (piano_roll.drag_mode == DRAG_MODE_NONE) { diff --git a/src/drivers/win/taseditor/piano_roll.h b/src/drivers/win/taseditor/piano_roll.h index 42e8a439..01ab5f65 100644 --- a/src/drivers/win/taseditor/piano_roll.h +++ b/src/drivers/win/taseditor/piano_roll.h @@ -26,8 +26,6 @@ #define MARKER_DRAG_MAX_SPEED 70 #define MARKER_DRAG_GRAVITY 2 -#define DRAWING_MIN_LINE_LEN 14 // = min(list_row_width, list_row_height) in pixels - #define DRAG_SCROLLING_BORDER_SIZE 10 // in pixels #define DOUBLETAP_COUNT 3 // 1:quick press, 2 - quick release, 3 - quick press @@ -194,6 +192,7 @@ public: int marker_drag_countdown; int marker_drag_framenum; int drawing_last_x, drawing_last_y; + int drawing_start_time; int drag_selection_starting_frame; int drag_selection_ending_frame; diff --git a/src/drivers/win/taseditor/snapshot.cpp b/src/drivers/win/taseditor/snapshot.cpp index d60d2e09..7c7b85cd 100644 --- a/src/drivers/win/taseditor/snapshot.cpp +++ b/src/drivers/win/taseditor/snapshot.cpp @@ -9,8 +9,8 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI ------------------------------------------------------------------------------------ Snapshot - Snapshot of all edited data -* stores the data of specific snapshot of the movie: size, input data (commands and joysticks), Markers at the moment of creating the snapshot, keyframe, type and description of the snapshot (including the time of creation) -* also stores info about sequential recording of input +* stores the data of specific snapshot of the movie: size, input data (commands and joysticks), Markers at the moment of creating the snapshot, keyframe, start and end frame of operation, type of operation and description of the snapshot (including the time of creation) +* also stores info about sequential recording/drawing of input * optionally can store map of Hot Changes * implements snapshot creation: copying input, copying Hot Changes, copying Markers, setting time of creation * implements full/partial restoring of data from snapshot: input, Hot Changes, Markers @@ -189,7 +189,9 @@ void SNAPSHOT::save(EMUFILE *os) write32le(size, os); write8le(input_type, os); write32le(jump_frame, os); - write32le(rec_end_frame, os); + write32le(start_frame, os); + write32le(end_frame, os); + write32le(consecutive_tag, os); write32le(rec_joypad_diff_bits, os); write32le(mod_type, os); if (has_hot_changes) write8le((uint8)1, os); else write8le((uint8)0, os); @@ -224,7 +226,9 @@ bool SNAPSHOT::load(EMUFILE *is) if (!read8le(&tmp, is)) return true; input_type = tmp; if (!read32le(&jump_frame, is)) return true; - if (!read32le(&rec_end_frame, is)) return true; + if (!read32le(&start_frame, is)) return true; + if (!read32le(&end_frame, is)) return true; + if (!read32le(&consecutive_tag, is)) return true; if (!read32le(&rec_joypad_diff_bits, is)) return true; if (!read32le(&mod_type, is)) return true; if (!read8le(&tmp, is)) return true; @@ -283,7 +287,9 @@ bool SNAPSHOT::skipLoad(EMUFILE *is) if (is->fseek(sizeof(int) + // size sizeof(uint8) + // input_type sizeof(int) + // jump_frame - sizeof(int) + // rec_end_frame + sizeof(int) + // start_frame + sizeof(int) + // end_frame + sizeof(int) + // consecutive_tag sizeof(int) + // rec_joypad_diff_bits sizeof(int) + // mod_type sizeof(uint8) // has_hot_changes diff --git a/src/drivers/win/taseditor/snapshot.h b/src/drivers/win/taseditor/snapshot.h index af5b8df7..4d6eefa2 100644 --- a/src/drivers/win/taseditor/snapshot.h +++ b/src/drivers/win/taseditor/snapshot.h @@ -64,7 +64,11 @@ public: 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, ... int jump_frame; // for jumping when making undo - int rec_end_frame; // for consecutive Recordings + + int start_frame; // for consecutive Draws + int end_frame; // for consecutive Draws + + int consecutive_tag; // for consecutive Recordings and Draws uint32 rec_joypad_diff_bits; // for consecutive Recordings int mod_type; char description[SNAPSHOT_DESC_MAX_LENGTH]; diff --git a/src/drivers/win/taseditor/taseditor_config.cpp b/src/drivers/win/taseditor/taseditor_config.cpp index d0e58048..2f791a89 100644 --- a/src/drivers/win/taseditor/taseditor_config.cpp +++ b/src/drivers/win/taseditor/taseditor_config.cpp @@ -44,7 +44,7 @@ TASEDITOR_CONFIG::TASEDITOR_CONFIG() follow_note_context = true; bind_markers = true; empty_marker_notes = true; - combine_consecutive_rec = false; + combine_consecutive = false; use_1p_rec = true; columnset_by_keys = false; superimpose = 0; // SUPERIMPOSE_UNCHECKED diff --git a/src/drivers/win/taseditor/taseditor_config.h b/src/drivers/win/taseditor/taseditor_config.h index 8dcc01db..4928b50f 100644 --- a/src/drivers/win/taseditor/taseditor_config.h +++ b/src/drivers/win/taseditor/taseditor_config.h @@ -42,7 +42,7 @@ public: bool follow_note_context; bool bind_markers; bool empty_marker_notes; - bool combine_consecutive_rec; + bool combine_consecutive; bool use_1p_rec; bool columnset_by_keys; int superimpose; diff --git a/src/drivers/win/taseditor/taseditor_lua.cpp b/src/drivers/win/taseditor/taseditor_lua.cpp index 523d90e3..ed3e368a 100644 --- a/src/drivers/win/taseditor/taseditor_lua.cpp +++ b/src/drivers/win/taseditor/taseditor_lua.cpp @@ -23,6 +23,7 @@ extern TASEDITOR_WINDOW taseditor_window; extern HISTORY history; extern MARKERS_MANAGER markers_manager; extern BOOKMARKS bookmarks; +extern BRANCHES branches; extern RECORDER recorder; extern PLAYBACK playback; extern GREENZONE greenzone; @@ -191,7 +192,7 @@ void TASEDITOR_LUA::setnote(int index, const char* newtext) int TASEDITOR_LUA::getcurrentbranch() { if (FCEUMOV_Mode(MOVIEMODE_TASEDITOR)) - return bookmarks.GetCurrentBranch(); + return branches.GetCurrentBranch(); else return -1; } diff --git a/src/drivers/win/taseditor/taseditor_project.h b/src/drivers/win/taseditor/taseditor_project.h index 3339806a..72687037 100644 --- a/src/drivers/win/taseditor/taseditor_project.h +++ b/src/drivers/win/taseditor/taseditor_project.h @@ -13,6 +13,7 @@ #include "recorder.h" #include "greenzone.h" #include "bookmarks.h" +#include "branches.h" #include "piano_roll.h" #include "taseditor_lua.h" #include "splicer.h" diff --git a/src/drivers/win/taseditor/taseditor_window.cpp b/src/drivers/win/taseditor/taseditor_window.cpp index a0eabc1b..91771c6a 100644 --- a/src/drivers/win/taseditor/taseditor_window.cpp +++ b/src/drivers/win/taseditor/taseditor_window.cpp @@ -437,7 +437,7 @@ void TASEDITOR_WINDOW::UpdateCheckedItems() CheckMenuItem(hmenu, ID_CONFIG_HUDINBRANCHSCREENSHOTS, taseditor_config.branch_scr_hud?MF_CHECKED : MF_UNCHECKED); CheckMenuItem(hmenu, ID_CONFIG_BINDMARKERSTOINPUT, taseditor_config.bind_markers?MF_CHECKED : MF_UNCHECKED); CheckMenuItem(hmenu, ID_CONFIG_EMPTYNEWMARKERNOTES, taseditor_config.empty_marker_notes?MF_CHECKED : MF_UNCHECKED); - CheckMenuItem(hmenu, ID_CONFIG_COMBINECONSECUTIVERECORDINGS, taseditor_config.combine_consecutive_rec?MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(hmenu, ID_CONFIG_COMBINECONSECUTIVERECORDINGS, taseditor_config.combine_consecutive?MF_CHECKED : MF_UNCHECKED); CheckMenuItem(hmenu, ID_CONFIG_USE1PFORRECORDING, taseditor_config.use_1p_rec?MF_CHECKED : MF_UNCHECKED); CheckMenuItem(hmenu, ID_CONFIG_USEINPUTKEYSFORCOLUMNSET, taseditor_config.columnset_by_keys?MF_CHECKED : MF_UNCHECKED); CheckMenuItem(hmenu, ID_CONFIG_SUPERIMPOSE_AFFECTS_PASTE, taseditor_config.superimpose_affects_paste?MF_CHECKED : MF_UNCHECKED); @@ -1032,7 +1032,7 @@ BOOL CALLBACK WndprocTasEditor(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara taseditor_window.UpdateCheckedItems(); break; case ID_CONFIG_COMBINECONSECUTIVERECORDINGS: - taseditor_config.combine_consecutive_rec ^= 1; + taseditor_config.combine_consecutive ^= 1; taseditor_window.UpdateCheckedItems(); break; case ID_CONFIG_USE1PFORRECORDING: diff --git a/src/input.cpp b/src/input.cpp index 1c8c578d..2d9cb18d 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -876,7 +876,7 @@ static void CommandStateSave(void) { #ifdef WIN32 if (execcmd == EMUCMD_SAVE_STATE) - bookmarks.command(COMMAND_SET, bookmarks.GetCurrentBranch()); + bookmarks.command(COMMAND_SET); else if(execcmd >= EMUCMD_SAVE_STATE_SLOT_0 && execcmd <= EMUCMD_SAVE_STATE_SLOT_9) bookmarks.command(COMMAND_SET, execcmd - EMUCMD_SAVE_STATE_SLOT_0); #endif @@ -900,7 +900,7 @@ static void CommandStateLoad(void) { #ifdef WIN32 if (execcmd == EMUCMD_LOAD_STATE) - bookmarks.command(COMMAND_DEPLOY, bookmarks.GetCurrentBranch()); + bookmarks.command(COMMAND_DEPLOY); else if(execcmd >= EMUCMD_LOAD_STATE_SLOT_0 && execcmd <= EMUCMD_LOAD_STATE_SLOT_9) bookmarks.command(COMMAND_DEPLOY, execcmd - EMUCMD_LOAD_STATE_SLOT_0); #endif diff --git a/vc/vc10_fceux.vcxproj b/vc/vc10_fceux.vcxproj index 99e278d5..b356af13 100644 --- a/vc/vc10_fceux.vcxproj +++ b/vc/vc10_fceux.vcxproj @@ -422,6 +422,7 @@ + @@ -749,6 +750,7 @@ + diff --git a/vc/vc10_fceux.vcxproj.filters b/vc/vc10_fceux.vcxproj.filters index 775d22c0..37af602f 100644 --- a/vc/vc10_fceux.vcxproj.filters +++ b/vc/vc10_fceux.vcxproj.filters @@ -958,6 +958,9 @@ drivers\win\taseditor + + drivers\win\taseditor + @@ -1408,9 +1411,6 @@ drivers\win\taseditor - - drivers\win\taseditor - drivers\win\taseditor @@ -1450,6 +1450,12 @@ drivers\win\taseditor + + drivers\win\taseditor + + + drivers\win\taseditor +