* observing Piano Roll by dragging cursor outside

* dragging blue arrow (moving Playback cursor)
* moving Markers by drag'n'drop, "Marker Drag" and "Marker Swap" operations
* Config->Doubleclick on Frame# affects Playback
* Config->Draw Input by dragging
This commit is contained in:
ansstuff 2012-03-13 20:03:37 +00:00
parent a484b4eb15
commit 1af2f936ff
19 changed files with 728 additions and 110 deletions

View File

@ -348,6 +348,8 @@ static CFGSTRUCT fceuconfig[] = {
AC(taseditor_config.findnote_matchcase),
AC(taseditor_config.findnote_search_up),
AC(taseditor_config.deselect_on_doubleclick),
AC(taseditor_config.doubleclick_affects_playback),
AC(taseditor_config.draw_input),
AC(taseditor_config.silent_autosave),
AC(taseditor_config.tooltips),
AC(taseditor_config.current_pattern),

View File

@ -71,6 +71,10 @@
#include "utils/xstring.h"
#include <string.h>
#include "taseditor/taseditor_window.h"
#include "taseditor/piano_roll.h"
extern TASEDITOR_WINDOW taseditor_window;
extern PIANO_ROLL piano_roll;
//---------------------------
//mbg merge 6/29/06 - new aboutbox
@ -308,7 +312,6 @@ int BlockingCheck()
{
//other accelerator capable dialogs could be added here
extern HWND hwndMemWatch;
extern TASEDITOR_WINDOW taseditor_window;
int handled = 0;
@ -336,12 +339,14 @@ int BlockingCheck()
}
}
if(!handled && taseditor_window.hwndTasEditor)
{
if(IsChild(taseditor_window.hwndTasEditor, msg.hwnd))
{
handled = TranslateAccelerator(taseditor_window.hwndTasEditor, fceu_hAccel, &msg);
if (handled)
piano_roll.AcceleratorDispatched();
}
}
if(!handled && taseditor_window.hwndFindNote)
{

View File

@ -292,6 +292,8 @@ BEGIN
MENUITEM MFT_SEPARATOR
MENUITEM "Autofire Pattern skips Lag", ID_CONFIG_COLUMNSETPATTERNSKIPSLAG,MFT_STRING,MFS_ENABLED
MENUITEM "Deselect on doubleclick", ID_CONFIG_DESELECTONDOUBLECLICK,MFT_STRING,MFS_ENABLED
MENUITEM "Doubleclick on Frame# affects Playback", ID_CONFIG_DOUBLECLICKONFRAME,MFT_STRING,MFS_ENABLED
MENUITEM "Draw Input by dragging", ID_CONFIG_DRAWINPUTBYDRAGGING,MFT_STRING,MFS_ENABLED
MENUITEM MFT_SEPARATOR
MENUITEM "Silent Autosave", ID_CONFIG_SILENTAUTOSAVE,MFT_STRING,MFS_ENABLED
MENUITEM "Mute Turbo", ID_CONFIG_MUTETURBO,MFT_STRING,MFS_ENABLED
@ -1391,56 +1393,56 @@ BEGIN
EDITTEXT IDC_LABEL_NEWPPUUSED,76,166,155,12,ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP
END
TASEDITOR DIALOGEX 0, 0, 323, 351
TASEDITOR DIALOGEX 0, 0, 325, 353
STYLE DS_SETFONT | DS_SETFOREGROUND | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "TAS Editor"
MENU TASEDITORMENU
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
CONTROL "",IDC_PROGRESS_BUTTON,"Button",BS_OWNERDRAW,199,36,116,12
CONTROL "",IDC_BRANCHES_BUTTON,"Button",BS_OWNERDRAW,206,167,104,11
CONTROL "",IDC_LIST1,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_OWNERDATA | LVS_NOSORTHEADER | WS_BORDER,4,13,187,320
GROUPBOX " Playback ",IDC_PLAYBACK_BOX,196,0,123,62,BS_CENTER,WS_EX_RIGHT
GROUPBOX " Recorder ",IDC_RECORDER_BOX,196,63,123,46,BS_CENTER,WS_EX_RIGHT
GROUPBOX " Splicer ",IDC_SPLICER_BOX,196,110,123,30,BS_CENTER,WS_EX_RIGHT
GROUPBOX " Lua ",IDC_LUA_BOX,196,141,123,26,BS_CENTER,WS_EX_RIGHT
GROUPBOX " Bookmarks ",IDC_BOOKMARKS_BOX,196,169,123,102,BS_CENTER,WS_EX_RIGHT
GROUPBOX " History ",IDC_HISTORY_BOX,196,272,123,56,BS_CENTER,WS_EX_RIGHT
PUSHBUTTON "<<",TASEDITOR_REWIND_FULL,200,9,23,14,NOT WS_TABSTOP
PUSHBUTTON "<",TASEDITOR_REWIND,223,9,23,14,NOT WS_TABSTOP
PUSHBUTTON "||",TASEDITOR_PLAYSTOP,246,9,23,14,NOT WS_TABSTOP
PUSHBUTTON ">",TASEDITOR_FORWARD,269,9,23,14,NOT WS_TABSTOP
PUSHBUTTON ">>",TASEDITOR_FORWARD_FULL,292,9,23,14,NOT WS_TABSTOP
CONTROL "",IDC_PROGRESS1,"msctls_progress32",PBS_SMOOTH | WS_BORDER,200,39,115,6
CONTROL " Follow cursor",CHECK_FOLLOW_CURSOR,"Button",BS_AUTOCHECKBOX,202,25,56,12
CONTROL "",IDC_PROGRESS_BUTTON,"Button",BS_OWNERDRAW,200,36,116,12
CONTROL "",IDC_BRANCHES_BUTTON,"Button",BS_OWNERDRAW,207,167,104,11
CONTROL "",IDC_LIST1,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_OWNERDATA | LVS_NOSORTHEADER | WS_BORDER,5,13,187,320
GROUPBOX " Playback ",IDC_PLAYBACK_BOX,197,0,123,62,BS_CENTER,WS_EX_RIGHT
GROUPBOX " Recorder ",IDC_RECORDER_BOX,197,63,123,46,BS_CENTER,WS_EX_RIGHT
GROUPBOX " Splicer ",IDC_SPLICER_BOX,197,110,123,30,BS_CENTER,WS_EX_RIGHT
GROUPBOX " Lua ",IDC_LUA_BOX,197,141,123,26,BS_CENTER,WS_EX_RIGHT
GROUPBOX " Bookmarks ",IDC_BOOKMARKS_BOX,197,169,123,102,BS_CENTER,WS_EX_RIGHT
GROUPBOX " History ",IDC_HISTORY_BOX,197,272,123,56,BS_CENTER,WS_EX_RIGHT
PUSHBUTTON "<<",TASEDITOR_REWIND_FULL,201,9,23,14,NOT WS_TABSTOP
PUSHBUTTON "<",TASEDITOR_REWIND,224,9,23,14,NOT WS_TABSTOP
PUSHBUTTON "||",TASEDITOR_PLAYSTOP,247,9,23,14,NOT WS_TABSTOP
PUSHBUTTON ">",TASEDITOR_FORWARD,270,9,23,14,NOT WS_TABSTOP
PUSHBUTTON ">>",TASEDITOR_FORWARD_FULL,293,9,23,14,NOT WS_TABSTOP
CONTROL "",IDC_PROGRESS1,"msctls_progress32",PBS_SMOOTH | WS_BORDER,201,39,115,6
CONTROL " Follow cursor",CHECK_FOLLOW_CURSOR,"Button",BS_AUTOCHECKBOX,203,25,56,12
CONTROL " Auto-restore last position",CHECK_AUTORESTORE_PLAYBACK,
"Button",BS_AUTOCHECKBOX,202,48,105,12
CONTROL "",IDC_BOOKMARKSLIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_ALIGNLEFT | LVS_OWNERDATA | LVS_NOSCROLL | LVS_NOCOLUMNHEADER | LVS_NOSORTHEADER | NOT WS_VISIBLE | WS_BORDER,201,178,113,89
CONTROL "",IDC_HISTORYLIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOLABELWRAP | LVS_ALIGNLEFT | LVS_OWNERDATA | LVS_NOCOLUMNHEADER | LVS_NOSORTHEADER | WS_BORDER,201,282,113,42
CONTROL " All",IDC_RADIO_ALL,"Button",BS_AUTORADIOBUTTON,290,71,24,10
CONTROL " 1P",IDC_RADIO_1P,"Button",BS_AUTORADIOBUTTON,202,83,25,10
CONTROL " 2P",IDC_RADIO_2P,"Button",BS_AUTORADIOBUTTON,231,83,25,10
CONTROL " 3P",IDC_RADIO_3P,"Button",BS_AUTORADIOBUTTON,261,83,24,10
CONTROL " 4P",IDC_RADIO_4P,"Button",BS_AUTORADIOBUTTON,290,83,24,10
CONTROL " Superimpose",IDC_SUPERIMPOSE,"Button",BS_AUTO3STATE,202,96,55,10
PUSHBUTTON "<<",TASEDITOR_PREV_MARKER,201,332,23,14,NOT WS_TABSTOP
PUSHBUTTON "Similar",TASEDITOR_FIND_BEST_SIMILAR_MARKER,224,332,34,14,NOT WS_TABSTOP
PUSHBUTTON "More",TASEDITOR_FIND_NEXT_SIMILAR_MARKER,258,332,34,14,NOT WS_TABSTOP
PUSHBUTTON ">>",TASEDITOR_NEXT_MARKER,291,332,23,14,NOT WS_TABSTOP
CONTROL "",IDC_JUMP_PLAYBACK_BUTTON,"Button",BS_OWNERDRAW,4,0,59,13
EDITTEXT IDC_PLAYBACK_MARKER_EDIT,64,0,127,13,ES_AUTOHSCROLL | ES_READONLY | NOT WS_TABSTOP
RTEXT "Marker 0",IDC_PLAYBACK_MARKER,3,2,58,10,0,WS_EX_RIGHT
CONTROL "",IDC_JUMP_SELECTION_BUTTON,"Button",BS_OWNERDRAW,4,333,59,13
EDITTEXT IDC_SELECTION_MARKER_EDIT,64,333,127,13,ES_AUTOHSCROLL | ES_READONLY | NOT WS_TABSTOP
RTEXT "Marker 99999",IDC_SELECTION_MARKER,3,335,58,10,0,WS_EX_RIGHT
CONTROL "",IDC_BRANCHES_BITMAP,"Static",SS_OWNERDRAW | SS_NOTIFY | SS_REALSIZEIMAGE | NOT WS_VISIBLE,201,178,113,89
CONTROL " Turbo seek",CHECK_TURBO_SEEK,"Button",BS_AUTOCHECKBOX,262,25,50,12
LTEXT "Selection: 0 rows, 16 columns",IDC_TEXT_SELECTION,203,118,112,10
LTEXT "Clipboard: 0 rows, 16 columns",IDC_TEXT_CLIPBOARD,202,128,114,10
CONTROL " Recording",IDC_RECORDING,"Button",BS_AUTO3STATE,202,71,64,10
PUSHBUTTON "Run function",TASEDITOR_RUN_MANUAL,201,150,54,14,WS_DISABLED | NOT WS_TABSTOP
CONTROL "Auto function",IDC_RUN_AUTO,"Button",BS_AUTOCHECKBOX,260,152,55,10
CONTROL " Use pattern",IDC_USEPATTERN,"Button",BS_AUTOCHECKBOX,261,96,53,10
"Button",BS_AUTOCHECKBOX,203,48,105,12
CONTROL "",IDC_BOOKMARKSLIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_ALIGNLEFT | LVS_OWNERDATA | LVS_NOSCROLL | LVS_NOCOLUMNHEADER | LVS_NOSORTHEADER | NOT WS_VISIBLE | WS_BORDER,202,178,113,89
CONTROL "",IDC_HISTORYLIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOLABELWRAP | LVS_ALIGNLEFT | LVS_OWNERDATA | LVS_NOCOLUMNHEADER | LVS_NOSORTHEADER | WS_BORDER,202,282,113,42
CONTROL " All",IDC_RADIO_ALL,"Button",BS_AUTORADIOBUTTON,291,71,24,10
CONTROL " 1P",IDC_RADIO_1P,"Button",BS_AUTORADIOBUTTON,203,83,25,10
CONTROL " 2P",IDC_RADIO_2P,"Button",BS_AUTORADIOBUTTON,232,83,25,10
CONTROL " 3P",IDC_RADIO_3P,"Button",BS_AUTORADIOBUTTON,262,83,24,10
CONTROL " 4P",IDC_RADIO_4P,"Button",BS_AUTORADIOBUTTON,291,83,24,10
CONTROL " Superimpose",IDC_SUPERIMPOSE,"Button",BS_AUTO3STATE,203,96,55,10
PUSHBUTTON "<<",TASEDITOR_PREV_MARKER,202,332,23,14,NOT WS_TABSTOP
PUSHBUTTON "Similar",TASEDITOR_FIND_BEST_SIMILAR_MARKER,225,332,34,14,NOT WS_TABSTOP
PUSHBUTTON "More",TASEDITOR_FIND_NEXT_SIMILAR_MARKER,259,332,34,14,NOT WS_TABSTOP
PUSHBUTTON ">>",TASEDITOR_NEXT_MARKER,292,332,23,14,NOT WS_TABSTOP
CONTROL "",IDC_JUMP_PLAYBACK_BUTTON,"Button",BS_OWNERDRAW,5,0,59,13
EDITTEXT IDC_PLAYBACK_MARKER_EDIT,65,0,127,13,ES_AUTOHSCROLL | ES_READONLY | NOT WS_TABSTOP
RTEXT "Marker 0",IDC_PLAYBACK_MARKER,4,2,58,10,0,WS_EX_RIGHT
CONTROL "",IDC_JUMP_SELECTION_BUTTON,"Button",BS_OWNERDRAW,5,333,59,13
EDITTEXT IDC_SELECTION_MARKER_EDIT,65,333,127,13,ES_AUTOHSCROLL | ES_READONLY | NOT WS_TABSTOP
RTEXT "Marker 99999",IDC_SELECTION_MARKER,4,335,58,10,0,WS_EX_RIGHT
CONTROL "",IDC_BRANCHES_BITMAP,"Static",SS_OWNERDRAW | SS_NOTIFY | SS_REALSIZEIMAGE | NOT WS_VISIBLE,202,178,113,89
CONTROL " Turbo seek",CHECK_TURBO_SEEK,"Button",BS_AUTOCHECKBOX,263,25,50,12
LTEXT "Selection: 0 rows, 16 columns",IDC_TEXT_SELECTION,204,118,112,10
LTEXT "Clipboard: 0 rows, 16 columns",IDC_TEXT_CLIPBOARD,203,128,114,10
CONTROL " Recording",IDC_RECORDING,"Button",BS_AUTO3STATE,203,71,64,10
PUSHBUTTON "Run function",TASEDITOR_RUN_MANUAL,202,150,54,14,WS_DISABLED | NOT WS_TABSTOP
CONTROL "Auto function",IDC_RUN_AUTO,"Button",BS_AUTOCHECKBOX,261,152,55,10
CONTROL " Use pattern",IDC_USEPATTERN,"Button",BS_AUTOCHECKBOX,262,96,53,10
END
IDD_TASEDITOR_ABOUT DIALOGEX 0, 0, 238, 78
@ -1982,6 +1984,8 @@ BEGIN
"TASEDITOR", DIALOG
BEGIN
RIGHTMARGIN, 323
BOTTOMMARGIN, 351
END
IDD_TASEDITOR_ABOUT, DIALOG

View File

@ -995,6 +995,8 @@
#define ID_EDIT_DESELECT 40536
#define ID_SELECTED_DESELECT 40537
#define ID_CONFIG_DESELECTONDOUBLECLICK 40538
#define ID_CONFIG_DRAWINPUTBYDRAGGING 40539
#define ID_CONFIG_DOUBLECLICKONFRAME 40540
#define IDC_DEBUGGER_ICONTRAY 55535
#define MW_ValueLabel2 65423
#define MW_ValueLabel1 65426
@ -1004,7 +1006,7 @@
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 206
#define _APS_NEXT_COMMAND_VALUE 40539
#define _APS_NEXT_COMMAND_VALUE 40541
#define _APS_NEXT_CONTROL_VALUE 1281
#define _APS_NEXT_SYMED_VALUE 101
#endif

View File

@ -290,10 +290,10 @@ void UpdateTasEditor()
// update all modules that need to be updated every frame
taseditor_window.update();
greenzone.update();
recorder.update();
piano_roll.update();
markers_manager.update();
greenzone.update();
playback.update();
bookmarks.update();
popup_display.update();

View File

@ -83,7 +83,9 @@ char modCaptions[MODTYPES_TOTAL][20] = {" Init Project",
" Marker Unset",
" Marker Pattern",
" Marker Rename",
" Marker Move",
" Marker Drag",
" Marker Swap",
" Marker Shift",
" LUA Marker Set",
" LUA Marker Unset",
" LUA Marker Rename",
@ -445,10 +447,15 @@ void HISTORY::RegisterMarkersChange(int mod_type, int start, int end, const char
_itoa(start, framenum, 10);
strcat(inp.description, " ");
strcat(inp.description, framenum);
if (end > start)
if (end > start || mod_type == MODTYPE_MARKER_DRAG || mod_type == MODTYPE_MARKER_SWAP)
{
_itoa(end, framenum, 10);
if (mod_type == MODTYPE_MARKER_DRAG)
strcat(inp.description, "=>");
else if (mod_type == MODTYPE_MARKER_SWAP)
strcat(inp.description, "<=>");
else
strcat(inp.description, "-");
_itoa(end, framenum, 10);
strcat(inp.description, framenum);
}
// add comment if there is one specified
@ -838,8 +845,13 @@ bool HISTORY::CursorOverHistoryList()
POINT p;
if (GetCursorPos(&p))
{
RECT wrect;
GetWindowRect(hwndHistoryList, &wrect);
ScreenToClient(hwndHistoryList, &p);
if (p.x >= 0 && p.y >= 0 && p.x < window_items[HISTORYLIST_IN_WINDOWITEMS].width && p.y < (taseditor_config.wndheight + window_items[HISTORYLIST_IN_WINDOWITEMS].height - window_items[HISTORYLIST_IN_WINDOWITEMS].y))
if (p.x >= 0
&& p.y >= 0
&& p.x < window_items[HISTORYLIST_IN_WINDOWITEMS].width
&& p.y < (wrect.bottom - wrect.top))
return true;
}
return false;

View File

@ -44,7 +44,9 @@ enum
MODTYPE_MARKER_UNSET,
MODTYPE_MARKER_PATTERN,
MODTYPE_MARKER_RENAME,
MODTYPE_MARKER_MOVE,
MODTYPE_MARKER_DRAG,
MODTYPE_MARKER_SWAP,
MODTYPE_MARKER_SHIFT,
MODTYPE_LUA_MARKER_SET,
MODTYPE_LUA_MARKER_UNSET,
MODTYPE_LUA_MARKER_RENAME,
@ -54,7 +56,6 @@ enum
};
#define HISTORY_NORMAL_COLOR 0x000000
#define HISTORYLIST_IN_WINDOWITEMS 18
#define WM_MOUSEWHEEL_RESENT WM_APP+123
#define HISTORY_ID_LEN 8

View File

@ -185,7 +185,7 @@ void MARKERS_MANAGER::ClearMarker(int frame)
{
// erase corresponding note
markers.notes.erase(markers.notes.begin() + markers.markers_array[frame]);
// erase marker
// clear marker
markers.markers_array[frame] = 0;
// decrease following markers' ids
int size = markers.markers_array.size();

View File

@ -10,7 +10,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
Piano Roll - Piano Roll interface
[Singleton]
* implements the working of Piano Roll List: creating, redrawing, scrolling, clicks
* implements the working of Piano Roll List: creating, redrawing, scrolling, mouseover, clicks, drag
* on demand: scrolls visible area of the List to any given item: to Playback Cursor, to Selection Cursor, to "undo pointer", to a Marker
* saves and loads current position of vertical scrolling from a project file. On error: scrolls the List to the beginning
* implements the working of Piano Roll List Header: creating, redrawing, animating, mouseover, clicks
@ -18,6 +18,7 @@ Piano Roll - Piano Roll interface
* regularly updates the size of the List according to current movie input, also updates lights in the List Header according to button presses data from Recorder and Alt key state
* implements the working of mouse wheel: List scrolling, Playback cursor movement, Selection cursor movement
* implements context menu on Right-click
* updates mouse cursor icon depending on item under cursor
* stores resources: save id, ids of columns, widths of columns, tables of colors, gradient of Hot Changes, gradient of Header flashings, timings of flashes, all fonts used in TAS Editor, images
------------------------------------------------------------------------------------ */
@ -43,21 +44,48 @@ extern HISTORY history;
extern MARKERS_MANAGER markers_manager;
extern SELECTION selection;
extern Window_items_struct window_items[];
extern int GetInputType(MovieData& md);
LRESULT APIENTRY HeaderWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
LRESULT APIENTRY ListWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
WNDPROC hwndList_oldWndProc = 0, hwndHeader_oldWndproc = 0;
LRESULT APIENTRY MarkerDragBoxWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
// resources
char piano_roll_save_id[PIANO_ROLL_ID_LEN] = "PIANO_ROLL";
char piano_roll_skipsave_id[PIANO_ROLL_ID_LEN] = "PIANO_ROLX";
COLORREF hot_changes_colors[16] = { 0x0, 0x5c4c44, 0x854604, 0xab2500, 0xc20006, 0xd6006f, 0xd40091, 0xba00a4, 0x9500ba, 0x7a00cc, 0x5800d4, 0x0045e2, 0x0063ea, 0x0079f4, 0x0092fa, 0x00aaff };
//COLORREF hot_changes_colors[16] = { 0x0, 0x661212, 0x842B4E, 0x652C73, 0x48247D, 0x383596, 0x2947AE, 0x1E53C1, 0x135DD2, 0x116EDA, 0x107EE3, 0x0F8EEB, 0x209FF4, 0x3DB1FD, 0x51C2FF, 0x4DCDFF };
COLORREF header_lights_colors[11] = { 0x0, 0x007313, 0x009100, 0x1daf00, 0x42c700, 0x65d900, 0x91e500, 0xb0f000, 0xdaf700, 0xf0fc7c, 0xfcffba };
char markerDragBoxClassName[] = "MarkerDragBox";
PIANO_ROLL::PIANO_ROLL()
{
hwndMarkerDragBox = 0;
// register MARKER_DRAG_BOX window class
wincl.hInstance = fceu_hInstance;
wincl.lpszClassName = markerDragBoxClassName;
wincl.lpfnWndProc = MarkerDragBoxWndProc;
wincl.style = CS_DBLCLKS;
wincl.cbSize = sizeof(WNDCLASSEX);
wincl.hIcon = 0;
wincl.hIconSm = 0;
wincl.hCursor = 0;
wincl.lpszMenuName = 0;
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 0;
wincl.hbrBackground = 0;
if(!RegisterClassEx(&wincl))
FCEU_printf("Error registering MARKER_DRAG_BOX window class\n");
// create blendfunction
blend.BlendOp = AC_SRC_OVER;
blend.BlendFlags = 0;
blend.AlphaFormat = 0;
blend.SourceConstantAlpha = 255;
}
void PIANO_ROLL::init()
@ -97,6 +125,8 @@ void PIANO_ROLL::init()
"Arial"); /*font name*/
bg_brush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
marker_drag_box_brush = CreateSolidBrush(MARKED_FRAMENUM_COLOR);
marker_drag_box_brush_bind = CreateSolidBrush(BINDMARKED_FRAMENUM_COLOR);
hwndList = GetDlgItem(taseditor_window.hwndTasEditor, IDC_LIST1);
// prepare the main listview
@ -183,6 +213,20 @@ void PIANO_ROLL::init()
lvc.mask = LVCF_WIDTH;
lvc.cx = COLUMN_ICONS_WIDTH;
ListView_InsertColumn(hwndList, 0, &lvc);
// find rows top/height (for mouseover hittest calculations)
ListView_SetItemCountEx(hwndList, 1, LVSICF_NOSCROLL|LVSICF_NOINVALIDATEALL);
RECT temp_rect;
if (ListView_GetSubItemRect(hwndList, 0, 0, LVIR_BOUNDS, &temp_rect) && temp_rect.bottom != temp_rect.top)
{
list_row_top = temp_rect.top;
list_row_height = temp_rect.bottom - temp_rect.top;
} else
{
// couldn't get rect, set default values
list_row_top = 20;
list_row_height = 14;
}
ListView_SetItemCountEx(hwndList, 0, LVSICF_NOSCROLL|LVSICF_NOINVALIDATEALL);
hrmenu = LoadMenu(fceu_hInstance,"TASEDITORCONTEXTMENUS");
header_colors.resize(TOTAL_COLUMNS);
@ -190,7 +234,7 @@ void PIANO_ROLL::init()
tme.cbSize = sizeof(tme);
tme.dwFlags = TME_LEAVE;
tme.hwndTrack = hwndHeader;
drag_mode = DRAG_MODE_NONE;
}
void PIANO_ROLL::free()
{
@ -224,6 +268,21 @@ void PIANO_ROLL::free()
DeleteObject(bg_brush);
bg_brush = 0;
}
if (marker_drag_box_brush)
{
DeleteObject(marker_drag_box_brush);
marker_drag_box_brush = 0;
}
if (marker_drag_box_brush_bind)
{
DeleteObject(marker_drag_box_brush_bind);
marker_drag_box_brush_bind = 0;
}
if (bg_brush)
{
DeleteObject(bg_brush);
bg_brush = 0;
}
if (himglist)
{
ImageList_Destroy(himglist);
@ -233,6 +292,7 @@ void PIANO_ROLL::free()
}
void PIANO_ROLL::reset()
{
must_check_item_under_mouse = true;
vk_shift_release_time = vk_control_release_time = 0;
next_header_update_time = header_item_under_mouse = 0;
// delete all columns except 0th
@ -274,7 +334,136 @@ void PIANO_ROLL::update()
if(currLVItemCount != movie_size)
ListView_SetItemCountEx(hwndList, movie_size, LVSICF_NOSCROLL|LVSICF_NOINVALIDATEALL);
// once per 40 milliseconds update colors alpha
// update dragging
if (drag_mode != DRAG_MODE_NONE)
{
// check if user released left button
if (GetAsyncKeyState(GetSystemMetrics(SM_SWAPBUTTON) ? VK_RBUTTON : VK_LBUTTON) >= 0)
FinishDrag();
}
// also scroll Piano Roll if user is dragging cursor outside
if (drag_mode != DRAG_MODE_NONE)
{
POINT p;
if (GetCursorPos(&p))
{
int scroll_dx = 0, scroll_dy = 0;
RECT wrect;
GetWindowRect(hwndList, &wrect);
ScreenToClient(hwndList, &p);
if (p.x < 0)
scroll_dx = p.x;
else if (p.x > (wrect.right - wrect.left))
scroll_dx = p.x - (wrect.right - wrect.left);
if (p.y < 0)
scroll_dy = p.y;
else if (p.y > (wrect.bottom - wrect.top))
scroll_dy = p.y - (wrect.bottom - wrect.top);
if (scroll_dx || scroll_dy)
ListView_Scroll(hwndList, scroll_dx, scroll_dy);
}
}
// perform drag
switch (drag_mode)
{
case DRAG_MODE_PLAYBACK:
{
if (!playback.pause_frame)
DragPlaybackCursor();
break;
}
case DRAG_MODE_MARKER:
{
// if suddenly source frame lost its Marker, abort drag
if (!markers_manager.GetMarker(marker_drag_framenum))
{
if (hwndMarkerDragBox)
{
DestroyWindow(hwndMarkerDragBox);
hwndMarkerDragBox = 0;
}
drag_mode = DRAG_MODE_NONE;
break;
}
// when dragging, always show semi-transparent yellow rectangle under mouse
POINT p = {0, 0};
GetCursorPos(&p);
int window_x = p.x - marker_drag_box_dx;
int window_y = p.y - marker_drag_box_dy;
if (!hwndMarkerDragBox)
{
hwndMarkerDragBox = CreateWindowEx(WS_EX_LAYERED | WS_EX_TRANSPARENT, markerDragBoxClassName, markerDragBoxClassName, WS_POPUP, window_x, window_y, COLUMN_FRAMENUM_WIDTH, list_row_height, taseditor_window.hwndTasEditor, NULL, fceu_hInstance, NULL);
ShowWindow(hwndMarkerDragBox, SW_SHOWNA);
SetLayeredWindowAttributes(hwndMarkerDragBox, 0, MARKER_DRAG_BOX_ALPHA, LWA_ALPHA);
UpdateLayeredWindow(hwndMarkerDragBox, 0, 0, 0, 0, 0, 0, &blend, ULW_ALPHA);
} else
{
SetWindowPos(hwndMarkerDragBox, 0, window_x, window_y, 0, 0, SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
}
// force dragging cursor icon
SetCursor(LoadCursor(0, IDC_ARROW));
must_check_item_under_mouse = false;
break;
}
case DRAG_MODE_SET:
case DRAG_MODE_UNSET:
{
POINT p;
if (GetCursorPos(&p))
{
RECT wrect;
GetWindowRect(hwndList, &wrect);
ScreenToClient(hwndList, &p);
int drawing_current_x = p.x + GetScrollPos(hwndList, SB_HORZ);
int drawing_current_y = p.y + GetScrollPos(hwndList, SB_VERT) * list_row_height;
// draw (or erase) line from [drawing_current_x, drawing_current_y] to (drawing_last_x, drawing_last_y)
int total_dx = drawing_last_x - drawing_current_x, total_dy = drawing_last_y - drawing_current_y;
double total_len = sqrt((double)(total_dx * total_dx + total_dy * total_dy));
LVHITTESTINFO info;
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)
{
// perform hit test
info.pt.x = p.x + (len / total_len) * total_dx;
info.pt.y = p.y + (len / total_len) * total_dy;
ListView_SubItemHitTest(hwndList, &info);
row_index = info.iItem;
column_index = info.iSubItem;
if (row_index >= 0 && column_index >= COLUMN_JOYPAD1_A && column_index <= COLUMN_JOYPAD4_R)
{
joy = (column_index - COLUMN_JOYPAD1_A) / NUM_JOYPAD_BUTTONS;
bit = (column_index - COLUMN_JOYPAD1_A) % NUM_JOYPAD_BUTTONS;
if (drag_mode == DRAG_MODE_SET && !currMovieData.records[row_index].checkBit(joy, bit))
{
currMovieData.records[row_index].setBit(joy, bit);
changes_made = true;
if (min_row_index > row_index) min_row_index = row_index;
if (max_row_index < row_index) max_row_index = row_index;
} else if (drag_mode == DRAG_MODE_UNSET && currMovieData.records[row_index].checkBit(joy, bit))
{
currMovieData.records[row_index].clearBit(joy, bit);
changes_made = true;
if (min_row_index > row_index) min_row_index = row_index;
if (max_row_index < row_index) max_row_index = row_index;
}
}
}
if (changes_made)
{
if (drag_mode == DRAG_MODE_SET)
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_SET, min_row_index, max_row_index));
else
greenzone.InvalidateAndCheck(history.RegisterChanges(MODTYPE_UNSET, min_row_index, max_row_index));
}
drawing_last_x = drawing_current_x;
drawing_last_y = drawing_current_y;
}
break;
}
}
// once per 40 milliseconds update colors alpha in the Header
if (clock() > next_header_update_time)
{
next_header_update_time = clock() + HEADER_LIGHT_UPDATE_TICK;
@ -283,7 +472,7 @@ void PIANO_ROLL::update()
// 1 - update Frame# columns' heads
if (GetAsyncKeyState(VK_MENU) & 0x8000)
light_value = HEADER_LIGHT_HOLD;
else if (header_item_under_mouse == COLUMN_FRAMENUM || header_item_under_mouse == COLUMN_FRAMENUM2)
else if (drag_mode == DRAG_MODE_NONE && (header_item_under_mouse == COLUMN_FRAMENUM || header_item_under_mouse == COLUMN_FRAMENUM2))
light_value = (selection.GetCurrentSelectionSize() > 0) ? HEADER_LIGHT_MOUSEOVER_SEL : HEADER_LIGHT_MOUSEOVER;
if (header_colors[COLUMN_FRAMENUM] < light_value)
{
@ -303,7 +492,7 @@ void PIANO_ROLL::update()
light_value = 0;
if (recorder.current_joy[(i - COLUMN_JOYPAD1_A) / NUM_JOYPAD_BUTTONS] & (1 << ((i - COLUMN_JOYPAD1_A) % NUM_JOYPAD_BUTTONS)))
light_value = HEADER_LIGHT_HOLD;
else if (header_item_under_mouse == i)
else if (drag_mode == DRAG_MODE_NONE && header_item_under_mouse == i)
light_value = (selection.GetCurrentSelectionSize() > 0) ? HEADER_LIGHT_MOUSEOVER_SEL : HEADER_LIGHT_MOUSEOVER;
if (header_colors[i] < light_value)
{
@ -319,6 +508,56 @@ void PIANO_ROLL::update()
if (changes_made)
RedrawHeader();
}
// change mouse cursor depending on what it points at
if (must_check_item_under_mouse)
{
LPCSTR cursor_icon = IDC_ARROW;
switch (drag_mode)
{
case DRAG_MODE_NONE:
{
// normal mouseover
POINT p;
if (GetCursorPos(&p))
{
RECT wrect;
GetWindowRect(hwndList, &wrect);
ScreenToClient(hwndList, &p);
if (p.x >= 0 && p.y >= 0 && p.x < (wrect.right - wrect.left) && p.y < (wrect.bottom - wrect.top))
{
// perform hit test
LVHITTESTINFO info;
info.pt.x = p.x;
info.pt.y = p.y;
ListView_SubItemHitTest(hwndList, &info);
int row_index = info.iItem;
int column_index = info.iSubItem;
if (row_index >= 0
&& (column_index == COLUMN_FRAMENUM || column_index == COLUMN_FRAMENUM2)
&& markers_manager.GetMarker(row_index))
cursor_icon = IDC_SIZEALL;
}
}
break;
}
case DRAG_MODE_PLAYBACK:
{
// dragging Playback cursor - show either normal arrow or arrow+wait
if (playback.pause_frame)
cursor_icon = IDC_APPSTARTING;
break;
}
case DRAG_MODE_MARKER:
case DRAG_MODE_OBSERVE:
case DRAG_MODE_SET:
case DRAG_MODE_UNSET:
// show normal arrow
break;
}
SetCursor(LoadCursor(0, cursor_icon));
must_check_item_under_mouse = false;
}
}
void PIANO_ROLL::save(EMUFILE *os, bool really_save)
@ -370,6 +609,7 @@ error:
void PIANO_ROLL::RedrawList()
{
InvalidateRect(hwndList, 0, FALSE);
must_check_item_under_mouse = true;
}
void PIANO_ROLL::RedrawRow(int index)
{
@ -483,6 +723,111 @@ void PIANO_ROLL::SetHeaderColumnLight(int column, int level)
}
}
void PIANO_ROLL::DragPlaybackCursor()
{
POINT p;
if (GetCursorPos(&p))
{
ScreenToClient(hwndList, &p);
// perform hit test
LVHITTESTINFO info;
info.pt.x = p.x;
info.pt.y = p.y;
ListView_SubItemHitTest(hwndList, &info);
int row_index = info.iItem;
if (row_index < 0)
row_index = ListView_GetTopIndex(hwndList) + (p.y - list_row_top) / list_row_height;
// send Playback there
if (currFrameCounter != row_index)
{
int lastCursor = currFrameCounter;
playback.jump(row_index);
if (lastCursor != currFrameCounter)
{
// redraw row where Playback cursor was (in case there's two or more drags before playback.update())
RedrawRow(lastCursor);
bookmarks.RedrawChangedBookmarks(lastCursor);
}
}
}
}
void PIANO_ROLL::FinishDrag()
{
switch (drag_mode)
{
case DRAG_MODE_MARKER:
{
// place Marker here
if (markers_manager.GetMarker(marker_drag_framenum))
{
POINT p;
if (GetCursorPos(&p))
{
ScreenToClient(hwndList, &p);
// perform hit test
LVHITTESTINFO info;
info.pt.x = p.x;
info.pt.y = p.y;
ListView_SubItemHitTest(hwndList, &info);
int row_index = info.iItem;
int column_index = info.iSubItem;
if (row_index >= 0 && row_index != marker_drag_framenum && (column_index <= COLUMN_FRAMENUM || column_index >= COLUMN_FRAMENUM2))
{
if (markers_manager.GetMarker(row_index))
{
int dragged_marker_id = markers_manager.GetMarker(marker_drag_framenum);
int destination_marker_id = markers_manager.GetMarker(row_index);
// swap Notes of these Markers
char dragged_marker_note[MAX_NOTE_LEN];
strcpy(dragged_marker_note, markers_manager.GetNote(dragged_marker_id).c_str());
if (strcmp(markers_manager.GetNote(destination_marker_id).c_str(), dragged_marker_note))
{
// notes are different, swap them
markers_manager.SetNote(dragged_marker_id, markers_manager.GetNote(destination_marker_id).c_str());
markers_manager.SetNote(destination_marker_id, dragged_marker_note);
history.RegisterMarkersChange(MODTYPE_MARKER_SWAP, marker_drag_framenum, row_index);
selection.must_find_current_marker = playback.must_find_current_marker = true;
SetHeaderColumnLight(COLUMN_FRAMENUM, HEADER_LIGHT_MAX);
}
} else
{
// move Marker
int new_marker_id = markers_manager.SetMarker(row_index);
if (new_marker_id)
{
markers_manager.SetNote(new_marker_id, markers_manager.GetNote(markers_manager.GetMarker(marker_drag_framenum)).c_str());
// and delete it from old frame
markers_manager.ClearMarker(marker_drag_framenum);
RedrawRow(marker_drag_framenum);
RedrawRow(row_index);
history.RegisterMarkersChange(MODTYPE_MARKER_DRAG, marker_drag_framenum, row_index, markers_manager.GetNote(markers_manager.GetMarker(row_index)).c_str());
selection.must_find_current_marker = playback.must_find_current_marker = true;
SetHeaderColumnLight(COLUMN_FRAMENUM, HEADER_LIGHT_MAX);
}
}
}
}
}
if (hwndMarkerDragBox)
{
DestroyWindow(hwndMarkerDragBox);
hwndMarkerDragBox = 0;
}
break;
}
}
drag_mode = DRAG_MODE_NONE;
must_check_item_under_mouse = true;
}
void PIANO_ROLL::AcceleratorDispatched()
{
// hack for tapping Ctrl twice - if first was accelerator, then it won't count as first tap
vk_control_release_time = -1;
}
void PIANO_ROLL::GetDispInfo(NMLVDISPINFO* nmlvDispInfo)
{
LVITEM& item = nmlvDispInfo->item;
@ -1063,6 +1408,18 @@ LRESULT APIENTRY ListWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
case WM_CHAR:
case WM_KILLFOCUS:
return 0;
case WM_SETCURSOR:
if (LOWORD(lParam) == HTCLIENT)
{
piano_roll.must_check_item_under_mouse = true;
return true;
}
break;
case WM_MOUSEMOVE:
{
piano_roll.must_check_item_under_mouse = true;
return 0;
}
case WM_NOTIFY:
{
if (((LPNMHDR)lParam)->hwndFrom == piano_roll.hwndHeader)
@ -1082,9 +1439,18 @@ LRESULT APIENTRY ListWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
case WM_KEYUP:
{
if (wParam == VK_SHIFT)
{
if (piano_roll.vk_shift_release_time >= 0)
piano_roll.vk_shift_release_time = clock();
else if (wParam == VK_CONTROL)
else // this was accelerator
piano_roll.vk_shift_release_time = 0;
} else if (wParam == VK_CONTROL)
{
if (piano_roll.vk_control_release_time >= 0)
piano_roll.vk_control_release_time = clock();
else // this was accelerator
piano_roll.vk_control_release_time = 0;
}
break;
}
case WM_KEYDOWN:
@ -1092,18 +1458,20 @@ LRESULT APIENTRY ListWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
if (wParam == VK_SHIFT)
{
// double-tap of Shift key
if (piano_roll.vk_shift_release_time + GetDoubleClickTime() > clock())
if ((int)(piano_roll.vk_shift_release_time + GetDoubleClickTime()) > clock())
piano_roll.FollowPlayback();
} else if (wParam == VK_CONTROL)
{
// double-tap of Ctrl key
if (piano_roll.vk_control_release_time + GetDoubleClickTime() > clock())
if ((int)(piano_roll.vk_control_release_time + GetDoubleClickTime()) > clock())
piano_roll.FollowSelection();
}
// only allow 8 keys
if (taseditor_config.keyboard_for_piano_roll && (wParam == VK_LEFT || wParam == VK_UP || wParam == VK_RIGHT || wParam == VK_DOWN || wParam == VK_END || wParam == VK_HOME || wParam == VK_PRIOR || wParam == VK_NEXT))
{
piano_roll.must_check_item_under_mouse = true;
break;
else
}
return 0;
}
case WM_TIMER:
@ -1118,15 +1486,15 @@ LRESULT APIENTRY ListWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
LVHITTESTINFO info;
info.pt.x = GET_X_LPARAM(lParam);
info.pt.y = GET_Y_LPARAM(lParam);
ListView_SubItemHitTest(hWnd, (LPARAM)&info);
ListView_SubItemHitTest(hWnd, &info);
int row_index = info.iItem;
int column_index = info.iSubItem;
if(row_index >= 0)
{
if(column_index == COLUMN_ICONS)
{
// click on the "icons" column - jump to the frame
playback.jump(row_index);
// click on the "icons" column
piano_roll.DragPlaybackCursor();
if (piano_roll.drag_mode == DRAG_MODE_NONE)
piano_roll.drag_mode = DRAG_MODE_PLAYBACK;
} else if(column_index == COLUMN_FRAMENUM || column_index == COLUMN_FRAMENUM2)
{
// clicked on the "Frame#" column
@ -1135,8 +1503,15 @@ LRESULT APIENTRY ListWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
// doubleclick - jump to the frame
if (taseditor_config.deselect_on_doubleclick)
selection.ClearSelection();
playback.jump(row_index);
if (taseditor_config.doubleclick_affects_playback)
{
piano_roll.DragPlaybackCursor();
if (piano_roll.drag_mode == DRAG_MODE_NONE)
piano_roll.drag_mode = DRAG_MODE_PLAYBACK;
}
} else
{
if (row_index >= 0)
{
// set marker if clicked with Alt
if (alt_pressed)
@ -1148,14 +1523,39 @@ LRESULT APIENTRY ListWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
else
history.RegisterMarkersChange(MODTYPE_MARKER_UNSET, row_index);
piano_roll.RedrawRow(row_index);
} else if (piano_roll.drag_mode == DRAG_MODE_NONE)
{
// check if user clicked on a Marker
if (markers_manager.GetMarker(row_index))
{
// start dragging the Marker
RECT temp_rect;
if (ListView_GetSubItemRect(piano_roll.hwndList, row_index, column_index, LVIR_BOUNDS, &temp_rect))
{
piano_roll.marker_drag_box_dx = GET_X_LPARAM(lParam) - temp_rect.left;
piano_roll.marker_drag_box_dy = GET_Y_LPARAM(lParam) - temp_rect.top;
} else
{
piano_roll.marker_drag_box_dx = 0;
piano_roll.marker_drag_box_dy = 0;
}
piano_roll.drag_mode = DRAG_MODE_MARKER;
piano_roll.marker_drag_framenum = row_index;
} else
{
piano_roll.drag_mode = DRAG_MODE_OBSERVE;
}
}
// also select the row by calling hwndList_oldWndProc
PostMessage(hWnd, WM_LBUTTONUP, 0, 0); // ensure that oldWndProc will exit its modal message loop immediately
CallWindowProc(hwndList_oldWndProc, hWnd, msg, wParam, lParam);
}
}
} else if(column_index >= COLUMN_JOYPAD1_A && column_index <= COLUMN_JOYPAD4_R)
{
// clicked on input
if (row_index >= 0)
{
// first call old wndproc to set selection on the row
if (alt_pressed)
wParam |= MK_SHIFT; // Alt should select region, just like Shift
@ -1170,6 +1570,25 @@ LRESULT APIENTRY ListWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
} else
{
piano_roll.ToggleJoypadBit(column_index, row_index, GET_KEYSTATE_WPARAM(wParam));
if (piano_roll.drag_mode == DRAG_MODE_NONE)
{
if (taseditor_config.draw_input)
{
// if clicked on empty cell - start drawing, else start erasing
int joy = (column_index - COLUMN_JOYPAD1_A) / NUM_JOYPAD_BUTTONS;
int bit = (column_index - COLUMN_JOYPAD1_A) % NUM_JOYPAD_BUTTONS;
if (currMovieData.records[row_index].checkBit(joy, bit))
piano_roll.drag_mode = DRAG_MODE_SET;
else
piano_roll.drag_mode = DRAG_MODE_UNSET;
piano_roll.drawing_last_x = GET_X_LPARAM(lParam) + GetScrollPos(piano_roll.hwndList, SB_HORZ);
piano_roll.drawing_last_y = GET_Y_LPARAM(lParam) + GetScrollPos(piano_roll.hwndList, SB_VERT) * piano_roll.list_row_height;
} else
{
piano_roll.drag_mode = DRAG_MODE_OBSERVE;
}
}
}
}
}
@ -1195,7 +1614,6 @@ LRESULT APIENTRY ListWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
playback.ForwardFull(-zDelta / 120);
else if (zDelta > 0)
playback.RewindFull(zDelta / 120);
return 0;
} else if (fwKeys & MK_CONTROL)
{
// Ctrl + wheel = Selection rewind full(speed)/forward full(speed)
@ -1203,7 +1621,6 @@ LRESULT APIENTRY ListWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
selection.JumpNextMarker(-zDelta / 120);
else if (zDelta > 0)
selection.JumpPrevMarker(zDelta / 120);
return 0;
} else if (alt_pressed || fwKeys & MK_RBUTTON)
{
// Right button + wheel = Alt + wheel = rewind/forward
@ -1220,12 +1637,15 @@ LRESULT APIENTRY ListWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
piano_roll.RedrawRow(lastCursor);
bookmarks.RedrawChangedBookmarks(lastCursor);
}
return 0;
} else if (history.CursorOverHistoryList())
{
return SendMessage(history.hwndHistoryList, WM_MOUSEWHEEL_RESENT, wParam, lParam);
} else
{
// normal scrolling
CallWindowProc(hwndList_oldWndProc, hWnd, msg, wParam, lParam);
}
break;
return 0;
}
case WM_RBUTTONDOWN:
case WM_RBUTTONDBLCLK:
@ -1238,17 +1658,78 @@ LRESULT APIENTRY ListWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
LVHITTESTINFO info;
info.pt.x = GET_X_LPARAM(lParam);
info.pt.y = GET_Y_LPARAM(lParam);
ListView_SubItemHitTest(hWnd, (LPARAM)&info);
ListView_SubItemHitTest(hWnd, &info);
// show context menu if user right-clicked on Frame#
if(info.iSubItem <= COLUMN_FRAMENUM || info.iSubItem >= COLUMN_FRAMENUM2)
piano_roll.RightClick(info);
return 0;
}
case WM_NCLBUTTONDOWN:
{
if (wParam == HTBORDER)
{
POINT p;
p.x = GET_X_LPARAM(lParam);
p.y = GET_Y_LPARAM(lParam);
ScreenToClient(piano_roll.hwndList, &p);
if (p.x <= 0)
{
// user clicked on left border of the Piano Roll
// consider this as a "misclick" on Piano Roll's first column
piano_roll.DragPlaybackCursor();
if (piano_roll.drag_mode == DRAG_MODE_NONE)
piano_roll.drag_mode = DRAG_MODE_PLAYBACK;
return 0;
}
}
break;
}
case WM_MOUSEACTIVATE:
{
if (GetFocus() != hWnd)
SetFocus(hWnd);
break;
}
case LVM_ENSUREVISIBLE:
{
piano_roll.must_check_item_under_mouse = true;
break;
}
}
return CallWindowProc(hwndList_oldWndProc, hWnd, msg, wParam, lParam);
}
// ----------------------------------------------------------------------------------------
LRESULT APIENTRY MarkerDragBoxWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
extern PIANO_ROLL piano_roll;
switch(message)
{
case WM_CREATE:
{
// create static bitmap placeholder
char framenum[DIGITS_IN_FRAMENUM + 1];
U32ToDecStr(framenum, piano_roll.marker_drag_framenum, DIGITS_IN_FRAMENUM);
piano_roll.hwndMarkerDragBoxText = CreateWindow(WC_STATIC, framenum, SS_CENTER| WS_CHILD | WS_VISIBLE, 0, 0, COLUMN_FRAMENUM_WIDTH, piano_roll.list_row_height, hwnd, NULL, NULL, NULL);
SendMessage(piano_roll.hwndMarkerDragBoxText, WM_SETFONT, (WPARAM)piano_roll.hMainListSelectFont, 0);
return 0;
}
case WM_CTLCOLORSTATIC:
{
// change color of static text fields
if ((HWND)lParam == piano_roll.hwndMarkerDragBoxText)
{
SetTextColor((HDC)wParam, NORMAL_TEXT_COLOR);
SetBkMode((HDC)wParam, TRANSPARENT);
if (taseditor_config.bind_markers)
return (LRESULT)(piano_roll.marker_drag_box_brush_bind);
else
return (LRESULT)(piano_roll.marker_drag_box_brush);
}
break;
}
}
return DefWindowProc(hwnd, message, wParam, lParam);
}

View File

@ -19,6 +19,9 @@
#define BOOST_WHEN_BOTH_RIGHTBUTTON_AND_ALT_PRESSED 4
#define MARKER_DRAG_BOX_ALPHA 175
#define DRAWING_MIN_LINE_LEN 14 // = min(list_row_width, list_row_height) in pixels
enum
{
COLUMN_ICONS,
@ -60,6 +63,16 @@ enum
TOTAL_COLUMNS
};
enum DRAG_MODES
{
DRAG_MODE_NONE,
DRAG_MODE_OBSERVE,
DRAG_MODE_PLAYBACK,
DRAG_MODE_MARKER,
DRAG_MODE_SET,
DRAG_MODE_UNSET,
};
// when there's too many button columns, there's need for 2nd Frame# column at the end
#define NUM_COLUMNS_NEED_2ND_FRAMENUM COLUMN_JOYPAD4_R
@ -139,6 +152,11 @@ public:
void SetHeaderColumnLight(int column, int level);
void DragPlaybackCursor();
void FinishDrag();
void AcceleratorDispatched();
void GetDispInfo(NMLVDISPINFO* nmlvDispInfo);
LONG CustomDraw(NMLVCUSTOMDRAW* msg);
LONG HeaderCustomDraw(NMLVCUSTOMDRAW* msg);
@ -158,13 +176,22 @@ public:
HWND hwndList, hwndHeader;
TRACKMOUSEEVENT tme;
unsigned int drag_mode;
int list_row_top, list_row_height;
int marker_drag_box_dx, marker_drag_box_dy;
int marker_drag_framenum;
int drawing_last_x, drawing_last_y;
bool must_check_item_under_mouse;
int vk_shift_release_time;
int vk_control_release_time;
HWND hwndMarkerDragBox, hwndMarkerDragBoxText;
// GDI stuff
HIMAGELIST himglist;
HFONT hMainListFont, hMainListSelectFont, hMarkersFont, hMarkersEditFont, hTaseditorAboutFont;
HBRUSH bg_brush;
HBRUSH bg_brush, marker_drag_box_brush, marker_drag_box_brush_bind;
private:
void CenterListAt(int frame);
@ -175,4 +202,7 @@ private:
HMENU hrmenu;
WNDCLASSEX wincl;
BLENDFUNCTION blend;
};

View File

@ -68,7 +68,7 @@ void PLAYBACK::reset()
{
must_find_current_marker = true;
shown_marker = 0;
lastCursor = -1;
lastCursor = currFrameCounter;
lost_position_frame = pause_frame = old_pauseframe = 0;
old_show_pauseframe = show_pauseframe = false;
old_rewind_button_state = rewind_button_state = false;
@ -172,18 +172,30 @@ void PLAYBACK::update()
emu_paused = (FCEUI_EmulationPaused() != 0);
if (pause_frame)
{
if (old_show_pauseframe != show_pauseframe)
if (old_show_pauseframe != show_pauseframe) // update progressbar from time to time
SetProgressbar(currFrameCounter - seeking_start_frame, pause_frame - seeking_start_frame);
} else if (old_emu_paused != emu_paused)
{
// emulator got paused/unpaused externally
if (old_emu_paused && !emu_paused)
// externally unpaused - progressbar should be empty
{
// externally unpaused
if (currFrameCounter < currMovieData.getNumRecords()-1)
{
// don't forget to stop at the end of the movie
pause_frame = currMovieData.getNumRecords();
seeking_start_frame = currFrameCounter;
} else
{
// unlimited emulation, appending the movie - progressbar should be empty
SetProgressbar(0, 1);
else
}
} else
{
// externally paused - progressbar should be full
SetProgressbar(1, 1);
}
}
// update the playback cursor
if(currFrameCounter != lastCursor)
@ -278,6 +290,17 @@ void PLAYBACK::SeekingStart(int finish_frame)
turbo = true;
UnpauseEmulation();
}
void PLAYBACK::SeekingContinue()
{
if (pause_frame)
{
if (taseditor_config.turbo_seek)
turbo = true;
UnpauseEmulation();
}
}
void PLAYBACK::SeekingStop()
{
pause_frame = 0;
@ -388,10 +411,16 @@ bool PLAYBACK::JumpToFrame(int index)
// Returns true if a jump to the frame is made, false if started seeking outside greenzone or if nothing's done
if (index < 0) return false;
if (index+1 == pause_frame && emu_paused)
{
SeekingContinue();
return false;
}
if (index >= greenzone.greenZoneCount)
{
// handle jump outside greenzone
if (currFrameCounter == greenzone.greenZoneCount-1 || JumpToFrame(greenzone.greenZoneCount-1))
if (currFrameCounter >= greenzone.greenZoneCount-1 || JumpToFrame(greenzone.greenZoneCount-1))
// seek from the end of greenzone
SeekingStart(index+1);
return false;

View File

@ -21,6 +21,7 @@ public:
void updateProgressbar();
void SeekingStart(int finish_frame);
void SeekingContinue();
void SeekingStop();
void ToggleEmulationPause();
void PauseEmulation();
@ -43,6 +44,7 @@ public:
bool JumpToFrame(int index);
int lastCursor; // but for currentCursor we use external variable currFrameCounter
int lost_position_frame;
int pause_frame;
bool must_find_current_marker;
@ -53,7 +55,6 @@ public:
private:
int lastCursor; // but for currentCursor we use external variable currFrameCounter
bool old_emu_paused, emu_paused;
int old_pauseframe;
bool old_show_pauseframe, show_pauseframe;

View File

@ -26,7 +26,7 @@ extern PIANO_ROLL piano_roll;
extern MARKERS_MANAGER markers_manager;
LRESULT CALLBACK ScrBmpWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT APIENTRY MarkerNoteTooltipWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT APIENTRY MarkerNoteDescrWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
// resources
char szClassName[] = "ScrBmp";
@ -34,6 +34,8 @@ char szClassName2[] = "MarketNoteTooltip";
POPUP_DISPLAY::POPUP_DISPLAY()
{
hwndScrBmp = 0;
hwndMarkerNoteTooltip = 0;
// create BITMAPINFO
scr_bmi = (LPBITMAPINFO)malloc(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD)); // 256 color in palette
scr_bmi->bmiHeader.biSize = sizeof(scr_bmi->bmiHeader);
@ -44,7 +46,7 @@ POPUP_DISPLAY::POPUP_DISPLAY()
scr_bmi->bmiHeader.biCompression = BI_RGB;
scr_bmi->bmiHeader.biSizeImage = 0;
// register MarketNoteTooltip window class
// register SCREENSHOT_DISPLAY window class
wincl1.hInstance = fceu_hInstance;
wincl1.lpszClassName = szClassName;
wincl1.lpfnWndProc = ScrBmpWndProc;
@ -60,10 +62,10 @@ POPUP_DISPLAY::POPUP_DISPLAY()
if(!RegisterClassEx(&wincl1))
FCEU_printf("Error registering SCREENSHOT_DISPLAY window class\n");
// register ScrBmp window class
// register MARKER_NOTE_DESCRIPTION window class
wincl2.hInstance = fceu_hInstance;
wincl2.lpszClassName = szClassName2;
wincl2.lpfnWndProc = MarkerNoteTooltipWndProc;
wincl2.lpfnWndProc = MarkerNoteDescrWndProc;
wincl2.style = CS_DBLCLKS;
wincl2.cbSize = sizeof(WNDCLASSEX);
wincl2.hIcon = 0;
@ -74,7 +76,7 @@ POPUP_DISPLAY::POPUP_DISPLAY()
wincl2.cbWndExtra = 0;
wincl2.hbrBackground = 0;
if(!RegisterClassEx(&wincl2))
FCEU_printf("Error registering MARKER_NOTE_TOOLTIP window class\n");
FCEU_printf("Error registering MARKER_NOTE_DESCRIPTION window class\n");
// create blendfunction
blend.BlendOp = AC_SRC_OVER;
@ -272,7 +274,7 @@ LRESULT APIENTRY ScrBmpWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lP
return DefWindowProc(hwnd, message, wParam, lParam);
}
}
LRESULT APIENTRY MarkerNoteTooltipWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
LRESULT APIENTRY MarkerNoteDescrWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
extern POPUP_DISPLAY popup_display;
switch(message)

View File

@ -144,7 +144,7 @@ void SPLICER::CloneFrames()
greenzone.InvalidateAndCheck(first_changes);
} else if (markers_changed)
{
history.RegisterMarkersChange(MODTYPE_MARKER_MOVE, *current_selection->begin());
history.RegisterMarkersChange(MODTYPE_MARKER_SHIFT, *current_selection->begin());
piano_roll.RedrawList();
}
if (markers_changed)
@ -191,7 +191,7 @@ void SPLICER::InsertFrames()
greenzone.InvalidateAndCheck(first_changes);
} else if (markers_changed)
{
history.RegisterMarkersChange(MODTYPE_MARKER_MOVE, *current_selection->begin());
history.RegisterMarkersChange(MODTYPE_MARKER_SHIFT, *current_selection->begin());
piano_roll.RedrawList();
}
if (markers_changed)
@ -239,7 +239,7 @@ void SPLICER::InsertNumFrames()
greenzone.InvalidateAndCheck(first_changes);
} else if (markers_changed)
{
history.RegisterMarkersChange(MODTYPE_MARKER_MOVE, index);
history.RegisterMarkersChange(MODTYPE_MARKER_SHIFT, index);
piano_roll.RedrawList();
}
if (markers_changed)
@ -287,7 +287,7 @@ void SPLICER::DeleteFrames()
else
piano_roll.RedrawList();
if (markers_changed)
history.RegisterMarkersChange(MODTYPE_MARKER_MOVE, start_index);
history.RegisterMarkersChange(MODTYPE_MARKER_SHIFT, start_index);
}
}
@ -652,7 +652,7 @@ bool SPLICER::PasteInsert()
greenzone.InvalidateAndCheck(first_changes);
} else if (markers_changed)
{
history.RegisterMarkersChange(MODTYPE_MARKER_MOVE, *current_selection->begin());
history.RegisterMarkersChange(MODTYPE_MARKER_SHIFT, *current_selection->begin());
piano_roll.RedrawList();
}
if (markers_changed)

View File

@ -71,6 +71,8 @@ TASEDITOR_CONFIG::TASEDITOR_CONFIG()
findnote_search_up = false;
enable_auto_function = true;
deselect_on_doubleclick = false;
doubleclick_affects_playback = true;
draw_input = true;
silent_autosave = true;
tooltips = true;
current_pattern = 0;

View File

@ -69,6 +69,8 @@ public:
bool findnote_search_up;
bool enable_auto_function;
bool deselect_on_doubleclick;
bool doubleclick_affects_playback;
bool draw_input;
bool silent_autosave;
bool tooltips;
int current_pattern;

View File

@ -60,7 +60,7 @@ void TASEDITOR_PROJECT::reset()
void TASEDITOR_PROJECT::update()
{
// if it's time to autosave - pop Save As dialog
if (changed && taseditor_config.autosave_period && clock() >= next_save_shedule)
if (changed && taseditor_config.autosave_period && clock() >= next_save_shedule && piano_roll.drag_mode == DRAG_MODE_NONE)
{
if (taseditor_config.silent_autosave)
SaveProject();
@ -123,6 +123,8 @@ bool TASEDITOR_PROJECT::save(const char* different_name, bool save_binary, bool
ofs = FCEUD_UTF8_fstream(GetProjectFile().c_str(), "wb");
if (ofs)
{
// change cursor to hourglass
SetCursor(LoadCursor(0, IDC_WAIT));
currMovieData.loadFrameCount = currMovieData.records.size();
currMovieData.emuVersion = FCEU_VERSION_NUMERIC;
currMovieData.dump(ofs, save_binary);
@ -146,6 +148,8 @@ bool TASEDITOR_PROJECT::save(const char* different_name, bool save_binary, bool
// also reset autosave period if we saved the project to its current filename
if (!different_name)
this->reset();
// restore cursor
piano_roll.must_check_item_under_mouse = true;
return true;
} else
{
@ -162,6 +166,8 @@ bool TASEDITOR_PROJECT::load(char* fullname)
return false;
}
// change cursor to hourglass
SetCursor(LoadCursor(0, IDC_WAIT));
MovieData tempMovieData = MovieData();
extern bool LoadFM2(MovieData& movieData, EMUFILE* fp, int size, bool stopAfterHeader);
if (LoadFM2(tempMovieData, &ifs, ifs.size(), false))
@ -227,6 +233,8 @@ bool TASEDITOR_PROJECT::load(char* fullname)
popup_display.reset();
reset();
RenameProject(fullname);
// restore cursor
piano_roll.must_check_item_under_mouse = true;
return true;
}

View File

@ -12,6 +12,7 @@ Window - User Interface
* implements all operations with TAS Editor window: creating, redrawing, resizing, moving, tooltips, clicks
* processes OS messages and sends signals from user to TAS Editor modules (also implements some minor commands on-site, like Greenzone capacity dialog and such)
* regularly checks if Shift or Ctrl key is held and sets keyboard focus to Piano Roll
* switches off/on emulator's keyboard input when the window loses/gains focus
* on demand: updates the window caption
* updates all checkboxes and menu items when some settings change
@ -449,6 +450,8 @@ void TASEDITOR_WINDOW::UpdateCheckedItems()
CheckMenuItem(hmenu, ID_CONFIG_SUPERIMPOSE_AFFECTS_PASTE, taseditor_config.superimpose_affects_paste?MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hmenu, ID_CONFIG_COLUMNSETPATTERNSKIPSLAG, taseditor_config.pattern_skips_lag?MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hmenu, ID_CONFIG_DESELECTONDOUBLECLICK, taseditor_config.deselect_on_doubleclick?MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hmenu, ID_CONFIG_DOUBLECLICKONFRAME, taseditor_config.doubleclick_affects_playback?MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hmenu, ID_CONFIG_DRAWINPUTBYDRAGGING, taseditor_config.draw_input?MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hmenu, ID_CONFIG_SILENTAUTOSAVE, taseditor_config.silent_autosave?MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hmenu, ID_CONFIG_MUTETURBO, muteTurbo?MF_CHECKED : MF_UNCHECKED);
CheckMenuItem(hmenu, ID_HELP_TOOLTIPS, taseditor_config.tooltips?MF_CHECKED : MF_UNCHECKED);
@ -694,6 +697,9 @@ BOOL CALLBACK WndprocTasEditor(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara
case LVN_ODSTATECHANGED:
selection.ItemRangeChanged((LPNMLVODSTATECHANGE) lParam);
break;
case LVN_ENDSCROLL:
piano_roll.must_check_item_under_mouse = true;
break;
}
break;
case IDC_BOOKMARKSLIST:
@ -1064,6 +1070,14 @@ BOOL CALLBACK WndprocTasEditor(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara
taseditor_config.deselect_on_doubleclick ^= 1;
taseditor_window.UpdateCheckedItems();
break;
case ID_CONFIG_DOUBLECLICKONFRAME:
taseditor_config.doubleclick_affects_playback ^= 1;
taseditor_window.UpdateCheckedItems();
break;
case ID_CONFIG_DRAWINPUTBYDRAGGING:
taseditor_config.draw_input ^= 1;
taseditor_window.UpdateCheckedItems();
break;
case ID_CONFIG_SILENTAUTOSAVE:
taseditor_config.silent_autosave ^= 1;
taseditor_window.UpdateCheckedItems();
@ -1304,6 +1318,26 @@ BOOL CALLBACK WndprocTasEditor(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara
}
case WM_LBUTTONDOWN:
case WM_LBUTTONDBLCLK:
{
// if user clicked on a narrow space to the left of Piano Roll
// consider this as a "misclick" on Piano Roll's first column
int x = GET_X_LPARAM(lParam);
int y = GET_Y_LPARAM(lParam);
RECT wrect;
GetWindowRect(piano_roll.hwndList, &wrect);
if (x > 0
&& x <= window_items[PIANOROLL_IN_WINDOWITEMS].x
&& y > window_items[PIANOROLL_IN_WINDOWITEMS].y
&& y < window_items[PIANOROLL_IN_WINDOWITEMS].y + (wrect.bottom - wrect.top))
{
piano_roll.DragPlaybackCursor();
if (piano_roll.drag_mode == DRAG_MODE_NONE)
piano_roll.drag_mode = DRAG_MODE_PLAYBACK;
}
if (GetFocus() != hWnd)
SetFocus(hWnd);
break;
}
case WM_RBUTTONDOWN:
case WM_RBUTTONDBLCLK:
{

View File

@ -1,6 +1,9 @@
// Specification file for TASEDITOR_WINDOW class
#define TASEDITOR_WINDOW_TOTAL_ITEMS 43
#define PIANOROLL_IN_WINDOWITEMS 2
#define HISTORYLIST_IN_WINDOWITEMS 18
#define TOOLTIP_TEXT_MAX_LEN 80
#define TOOLTIPS_AUTOPOP_TIMEOUT 30000