2406 lines
70 KiB
C++
2406 lines
70 KiB
C++
/* FCE Ultra - NES/Famicom Emulator
|
|
*
|
|
* Copyright notice for this file:
|
|
* Copyright (C) 2021 mjbudd77
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
// TasEditorWindow.cpp
|
|
//
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include <string>
|
|
|
|
#include <QDir>
|
|
#include <QPainter>
|
|
#include <QSettings>
|
|
#include <QHeaderView>
|
|
#include <QMessageBox>
|
|
#include <QFontMetrics>
|
|
#include <QFileDialog>
|
|
#include <QStandardPaths>
|
|
#include <QApplication>
|
|
|
|
#include "fceu.h"
|
|
#include "movie.h"
|
|
#include "driver.h"
|
|
|
|
#include "Qt/config.h"
|
|
#include "Qt/throttle.h"
|
|
#include "Qt/fceuWrapper.h"
|
|
#include "Qt/ConsoleUtilities.h"
|
|
#include "Qt/TasEditor/TasEditorWindow.h"
|
|
|
|
TasEditorWindow *tasWin = NULL;
|
|
TASEDITOR_PROJECT *project = NULL;
|
|
TASEDITOR_CONFIG *taseditorConfig = NULL;
|
|
TASEDITOR_LUA *taseditor_lua = NULL;
|
|
MARKERS_MANAGER *markersManager = NULL;
|
|
SELECTION *selection = NULL;
|
|
GREENZONE *greenzone = NULL;
|
|
BOOKMARKS *bookmarks = NULL;
|
|
BRANCHES *branches = NULL;
|
|
PLAYBACK *playback = NULL;
|
|
RECORDER *recorder = NULL;
|
|
HISTORY *history = NULL;
|
|
SPLICER *splicer = NULL;
|
|
|
|
// Piano Roll Definitions
|
|
enum PIANO_ROLL_COLUMNS
|
|
{
|
|
COLUMN_ICONS,
|
|
COLUMN_FRAMENUM,
|
|
COLUMN_JOYPAD1_A,
|
|
COLUMN_JOYPAD1_B,
|
|
COLUMN_JOYPAD1_S,
|
|
COLUMN_JOYPAD1_T,
|
|
COLUMN_JOYPAD1_U,
|
|
COLUMN_JOYPAD1_D,
|
|
COLUMN_JOYPAD1_L,
|
|
COLUMN_JOYPAD1_R,
|
|
COLUMN_JOYPAD2_A,
|
|
COLUMN_JOYPAD2_B,
|
|
COLUMN_JOYPAD2_S,
|
|
COLUMN_JOYPAD2_T,
|
|
COLUMN_JOYPAD2_U,
|
|
COLUMN_JOYPAD2_D,
|
|
COLUMN_JOYPAD2_L,
|
|
COLUMN_JOYPAD2_R,
|
|
COLUMN_JOYPAD3_A,
|
|
COLUMN_JOYPAD3_B,
|
|
COLUMN_JOYPAD3_S,
|
|
COLUMN_JOYPAD3_T,
|
|
COLUMN_JOYPAD3_U,
|
|
COLUMN_JOYPAD3_D,
|
|
COLUMN_JOYPAD3_L,
|
|
COLUMN_JOYPAD3_R,
|
|
COLUMN_JOYPAD4_A,
|
|
COLUMN_JOYPAD4_B,
|
|
COLUMN_JOYPAD4_S,
|
|
COLUMN_JOYPAD4_T,
|
|
COLUMN_JOYPAD4_U,
|
|
COLUMN_JOYPAD4_D,
|
|
COLUMN_JOYPAD4_L,
|
|
COLUMN_JOYPAD4_R,
|
|
COLUMN_FRAMENUM2,
|
|
|
|
TOTAL_COLUMNS
|
|
};
|
|
|
|
enum DRAG_MODES
|
|
{
|
|
DRAG_MODE_NONE,
|
|
DRAG_MODE_OBSERVE,
|
|
DRAG_MODE_PLAYBACK,
|
|
DRAG_MODE_MARKER,
|
|
DRAG_MODE_SET,
|
|
DRAG_MODE_UNSET,
|
|
DRAG_MODE_SELECTION,
|
|
DRAG_MODE_DESELECTION,
|
|
};
|
|
#define BOOKMARKS_WITH_BLUE_ARROW 20
|
|
#define BOOKMARKS_WITH_GREEN_ARROW 40
|
|
#define BLUE_ARROW_IMAGE_ID 60
|
|
#define GREEN_ARROW_IMAGE_ID 61
|
|
#define GREEN_BLUE_ARROW_IMAGE_ID 62
|
|
|
|
// Piano Roll Colors
|
|
#define NORMAL_TEXT_COLOR 0x0
|
|
#define NORMAL_BACKGROUND_COLOR 0xFFFFFF
|
|
|
|
//#define NORMAL_FRAMENUM_COLOR 0xFFFFFF
|
|
//#define NORMAL_INPUT_COLOR1 0xEDEDED
|
|
//#define NORMAL_INPUT_COLOR2 0xE2E2E2
|
|
|
|
#define NORMAL_FRAMENUM_COLOR 0xFF, 0xFF, 0xFF
|
|
#define NORMAL_INPUT_COLOR1 0xED, 0xED, 0xED
|
|
#define NORMAL_INPUT_COLOR2 0xE2, 0xE2, 0xE2
|
|
|
|
//#define GREENZONE_FRAMENUM_COLOR 0xDDFFDD
|
|
//#define GREENZONE_INPUT_COLOR1 0xC8F7C4
|
|
//#define GREENZONE_INPUT_COLOR2 0xADE7AD
|
|
|
|
#define GREENZONE_FRAMENUM_COLOR 0xDD, 0xFF, 0xDD
|
|
#define GREENZONE_INPUT_COLOR1 0xC4, 0xF7, 0xC8
|
|
#define GREENZONE_INPUT_COLOR2 0xAD, 0xE7, 0xAD
|
|
|
|
//#define PALE_GREENZONE_FRAMENUM_COLOR 0xE4FFE4
|
|
//#define PALE_GREENZONE_INPUT_COLOR1 0xD3F9D2
|
|
//#define PALE_GREENZONE_INPUT_COLOR2 0xBAEBBA
|
|
|
|
#define PALE_GREENZONE_FRAMENUM_COLOR 0xE4, 0xFF, 0xE4
|
|
#define PALE_GREENZONE_INPUT_COLOR1 0xD2, 0xF9, 0xD3
|
|
#define PALE_GREENZONE_INPUT_COLOR2 0xBA, 0xEB, 0xBA
|
|
|
|
//#define VERY_PALE_GREENZONE_FRAMENUM_COLOR 0xF9FFF9
|
|
//#define VERY_PALE_GREENZONE_INPUT_COLOR1 0xE0FBE0
|
|
//#define VERY_PALE_GREENZONE_INPUT_COLOR2 0xD2F2D2
|
|
|
|
#define VERY_PALE_GREENZONE_FRAMENUM_COLOR 0xF9, 0xFF, 0xF9
|
|
#define VERY_PALE_GREENZONE_INPUT_COLOR1 0xE0, 0xFB, 0xE0
|
|
#define VERY_PALE_GREENZONE_INPUT_COLOR2 0xD2, 0xF2, 0xD2
|
|
|
|
//#define LAG_FRAMENUM_COLOR 0xDDDCFF
|
|
//#define LAG_INPUT_COLOR1 0xD2D0F0
|
|
//#define LAG_INPUT_COLOR2 0xC9C6E8
|
|
|
|
#define LAG_FRAMENUM_COLOR 0xFF, 0xDC, 0xDD
|
|
#define LAG_INPUT_COLOR1 0xF0, 0xD0, 0xD2
|
|
#define LAG_INPUT_COLOR2 0xE8, 0xC6, 0xC9
|
|
|
|
//#define PALE_LAG_FRAMENUM_COLOR 0xE3E3FF
|
|
//#define PALE_LAG_INPUT_COLOR1 0xDADAF4
|
|
//#define PALE_LAG_INPUT_COLOR2 0xCFCEEA
|
|
|
|
#define PALE_LAG_FRAMENUM_COLOR 0xFF, 0xE3, 0xE3
|
|
#define PALE_LAG_INPUT_COLOR1 0xF4, 0xDA, 0xDA
|
|
#define PALE_LAG_INPUT_COLOR2 0xEA, 0xCE, 0xCF
|
|
|
|
//#define VERY_PALE_LAG_FRAMENUM_COLOR 0xE9E9FF
|
|
//#define VERY_PALE_LAG_INPUT_COLOR1 0xE5E5F7
|
|
//#define VERY_PALE_LAG_INPUT_COLOR2 0xE0E0F1
|
|
|
|
#define VERY_PALE_LAG_FRAMENUM_COLOR 0xFF, 0xE9, 0xE9
|
|
#define VERY_PALE_LAG_INPUT_COLOR1 0xF7, 0xE5, 0xE5
|
|
#define VERY_PALE_LAG_INPUT_COLOR2 0xF1, 0xE0, 0xE0
|
|
|
|
//#define CUR_FRAMENUM_COLOR 0xFCEDCF
|
|
//#define CUR_INPUT_COLOR1 0xF7E7B5
|
|
//#define CUR_INPUT_COLOR2 0xE5DBA5
|
|
|
|
#define CUR_FRAMENUM_COLOR 0xCF, 0xED, 0xFC
|
|
#define CUR_INPUT_COLOR1 0xB5, 0xE7, 0xF7
|
|
#define CUR_INPUT_COLOR2 0xA5, 0xDB, 0xE5
|
|
|
|
//#define UNDOHINT_FRAMENUM_COLOR 0xF9DDE6
|
|
//#define UNDOHINT_INPUT_COLOR1 0xF7D2E1
|
|
//#define UNDOHINT_INPUT_COLOR2 0xE9BED1
|
|
|
|
#define UNDOHINT_FRAMENUM_COLOR 0xE6, 0xDD, 0xF9
|
|
#define UNDOHINT_INPUT_COLOR1 0xE1, 0xD2, 0xF7
|
|
#define UNDOHINT_INPUT_COLOR2 0xD1, 0xBE, 0xE9
|
|
|
|
#define MARKED_FRAMENUM_COLOR 0xAEF0FF
|
|
#define CUR_MARKED_FRAMENUM_COLOR 0xCAEDEA
|
|
#define MARKED_UNDOHINT_FRAMENUM_COLOR 0xDDE5E9
|
|
|
|
#define BINDMARKED_FRAMENUM_COLOR 0xC9FFF7
|
|
#define CUR_BINDMARKED_FRAMENUM_COLOR 0xD5F2EC
|
|
#define BINDMARKED_UNDOHINT_FRAMENUM_COLOR 0xE1EBED
|
|
|
|
#define PLAYBACK_MARKER_COLOR 0xC9AF00
|
|
|
|
#define MARKER_DRAG_COUNTDOWN_MAX 14
|
|
|
|
//----------------------------------------------------------------------------
|
|
//---- Main TAS Editor Window
|
|
//----------------------------------------------------------------------------
|
|
bool tasWindowIsOpen(void)
|
|
{
|
|
return tasWin != NULL;
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void tasWindowSetFocus(bool val)
|
|
{
|
|
if ( tasWin )
|
|
{
|
|
tasWin->activateWindow();
|
|
tasWin->raise();
|
|
tasWin->setFocus();
|
|
}
|
|
}
|
|
// this getter contains formula to decide whether to record or replay movie
|
|
bool isTaseditorRecording(void)
|
|
{
|
|
if ( tasWin == NULL )
|
|
{
|
|
return false;
|
|
}
|
|
if (movie_readonly || playback->getPauseFrame() >= 0 || (taseditorConfig->oldControlSchemeForBranching && !recorder->stateWasLoadedInReadWriteMode))
|
|
{
|
|
return false; // replay
|
|
}
|
|
return true; // record
|
|
}
|
|
|
|
void recordInputByTaseditor(void)
|
|
{
|
|
if ( recorder )
|
|
{
|
|
recorder->recordInput();
|
|
}
|
|
return;
|
|
}
|
|
|
|
void applyMovieInputConfig(void)
|
|
{
|
|
// update FCEUX input config
|
|
FCEUD_SetInput(currMovieData.fourscore, currMovieData.microphone, (ESI)currMovieData.ports[0], (ESI)currMovieData.ports[1], (ESIFC)currMovieData.ports[2]);
|
|
// update PAL flag
|
|
pal_emulation = currMovieData.palFlag;
|
|
if (pal_emulation)
|
|
{
|
|
dendy = 0;
|
|
}
|
|
FCEUI_SetVidSystem(pal_emulation);
|
|
RefreshThrottleFPS();
|
|
//PushCurrentVideoSettings();
|
|
// update PPU type
|
|
newppu = currMovieData.PPUflag;
|
|
//SetMainWindowText();
|
|
// return focus to TAS Editor window
|
|
//SetFocus(taseditorWindow.hwndTASEditor);
|
|
RAMInitOption = currMovieData.RAMInitOption;
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
TasEditorWindow::TasEditorWindow(QWidget *parent)
|
|
: QDialog( parent, Qt::Window )
|
|
{
|
|
QSettings settings;
|
|
QVBoxLayout *mainLayout;
|
|
//QHBoxLayout *hbox;
|
|
QMenuBar *menuBar;
|
|
|
|
tasWin = this;
|
|
::project = &this->project;
|
|
::taseditorConfig = &this->taseditorConfig;
|
|
::taseditor_lua = &this->taseditor_lua;
|
|
::markersManager = &this->markersManager;
|
|
::selection = &this->selection;
|
|
::greenzone = &this->greenzone;
|
|
::bookmarks = &this->bookmarks;
|
|
::playback = &this->playback;
|
|
::recorder = &this->recorder;
|
|
::history = &this->history;
|
|
::branches = &this->branches;
|
|
::splicer = &this->splicer;
|
|
|
|
setWindowTitle("TAS Editor");
|
|
|
|
resize(512, 512);
|
|
|
|
mainLayout = new QVBoxLayout();
|
|
mainHBox = new QSplitter( Qt::Horizontal );
|
|
|
|
buildPianoRollDisplay();
|
|
buildSideControlPanel();
|
|
|
|
mainHBox->addWidget( pianoRollContainerWidget );
|
|
mainHBox->addWidget( controlPanelContainerWidget );
|
|
mainLayout->addWidget(mainHBox);
|
|
|
|
menuBar = buildMenuBar();
|
|
|
|
setLayout(mainLayout);
|
|
mainLayout->setMenuBar( menuBar );
|
|
|
|
initModules();
|
|
|
|
// Restore Window Geometry
|
|
restoreGeometry(settings.value("tasEditor/geometry").toByteArray());
|
|
|
|
// Restore Horizontal Panel State
|
|
mainHBox->restoreState( settings.value("tasEditor/hPanelState").toByteArray() );
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
TasEditorWindow::~TasEditorWindow(void)
|
|
{
|
|
QSettings settings;
|
|
|
|
printf("Destroy Tas Editor Window\n");
|
|
|
|
fceuWrapperLock();
|
|
//if (!askToSaveProject()) return false;
|
|
|
|
// destroy window
|
|
//taseditorWindow.exit();
|
|
//disableGeneralKeyboardInput();
|
|
// release memory
|
|
//editor.free();
|
|
//pianoRoll.free();
|
|
markersManager.free();
|
|
greenzone.free();
|
|
bookmarks.free();
|
|
branches.free();
|
|
//popupDisplay.free();
|
|
history.free();
|
|
playback.stopSeeking();
|
|
selection.free();
|
|
|
|
// switch off TAS Editor mode
|
|
movieMode = MOVIEMODE_INACTIVE;
|
|
FCEU_DispMessage("TAS Editor disengaged", 0);
|
|
FCEUMOV_CreateCleanMovie();
|
|
|
|
if ( tasWin == this )
|
|
{
|
|
tasWin = NULL;
|
|
}
|
|
|
|
::project = NULL;
|
|
::taseditorConfig = NULL;
|
|
::taseditor_lua = NULL;
|
|
::markersManager = NULL;
|
|
::selection = NULL;
|
|
::greenzone = NULL;
|
|
::bookmarks = NULL;
|
|
::playback = NULL;
|
|
::recorder = NULL;
|
|
::history = NULL;
|
|
::branches = NULL;
|
|
::splicer = NULL;
|
|
|
|
fceuWrapperUnLock();
|
|
|
|
// Save Horizontal Panel State
|
|
settings.setValue("tasEditor/hPanelState", mainHBox->saveState());
|
|
|
|
// Save Window Geometry
|
|
settings.setValue("tasEditor/geometry", saveGeometry());
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void TasEditorWindow::closeEvent(QCloseEvent *event)
|
|
{
|
|
printf("Tas Editor Close Window Event\n");
|
|
|
|
if (!askToSaveProject()) return;
|
|
|
|
done(0);
|
|
deleteLater();
|
|
event->accept();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void TasEditorWindow::closeWindow(void)
|
|
{
|
|
if (!askToSaveProject()) return;
|
|
//printf("Close Window\n");
|
|
done(0);
|
|
deleteLater();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
QMenuBar *TasEditorWindow::buildMenuBar(void)
|
|
{
|
|
QMenu *fileMenu;
|
|
//QActionGroup *actGroup;
|
|
QAction *act;
|
|
int useNativeMenuBar=0;
|
|
|
|
QMenuBar *menuBar = new QMenuBar(this);
|
|
|
|
// This is needed for menu bar to show up on MacOS
|
|
g_config->getOption( "SDL.UseNativeMenuBar", &useNativeMenuBar );
|
|
|
|
menuBar->setNativeMenuBar( useNativeMenuBar ? true : false );
|
|
|
|
//-----------------------------------------------------------------------
|
|
// Menu Start
|
|
//-----------------------------------------------------------------------
|
|
// File
|
|
fileMenu = menuBar->addMenu(tr("&File"));
|
|
|
|
// File -> New
|
|
act = new QAction(tr("&New"), this);
|
|
act->setShortcut(QKeySequence(tr("Ctrl+N")));
|
|
act->setStatusTip(tr("Open New Project"));
|
|
//act->setIcon( style()->standardIcon( QStyle::SP_FileDialogStart ) );
|
|
connect(act, SIGNAL(triggered()), this, SLOT(createNewProject(void)) );
|
|
|
|
fileMenu->addAction(act);
|
|
|
|
// File -> Open
|
|
act = new QAction(tr("&Open"), this);
|
|
act->setShortcut(QKeySequence(tr("Ctrl+O")));
|
|
act->setStatusTip(tr("Open Project"));
|
|
//act->setIcon( style()->standardIcon( QStyle::SP_BrowserStop ) );
|
|
connect(act, SIGNAL(triggered()), this, SLOT(openProject(void)) );
|
|
|
|
fileMenu->addAction(act);
|
|
|
|
// File -> Save
|
|
act = new QAction(tr("&Save"), this);
|
|
act->setShortcut(QKeySequence(tr("Ctrl+S")));
|
|
act->setStatusTip(tr("Save Project"));
|
|
//act->setIcon( style()->standardIcon( QStyle::SP_BrowserStop ) );
|
|
connect(act, SIGNAL(triggered()), this, SLOT(saveProjectCb(void)) );
|
|
|
|
fileMenu->addAction(act);
|
|
|
|
// File -> Save As
|
|
act = new QAction(tr("Save &As"), this);
|
|
act->setShortcut(QKeySequence(tr("Ctrl+Shift+S")));
|
|
act->setStatusTip(tr("Save Project As"));
|
|
//act->setIcon( style()->standardIcon( QStyle::SP_BrowserStop ) );
|
|
connect(act, SIGNAL(triggered()), this, SLOT(saveProjectAsCb(void)) );
|
|
|
|
fileMenu->addAction(act);
|
|
|
|
// File -> Save Compact
|
|
act = new QAction(tr("Save &Compact"), this);
|
|
//act->setShortcut(QKeySequence(tr("Ctrl+Shift+S")));
|
|
act->setStatusTip(tr("Save Compact"));
|
|
//act->setIcon( style()->standardIcon( QStyle::SP_BrowserStop ) );
|
|
connect(act, SIGNAL(triggered()), this, SLOT(saveProjectCompactCb(void)) );
|
|
|
|
fileMenu->addAction(act);
|
|
|
|
// File -> Recent
|
|
recentMenu = fileMenu->addMenu( tr("Recent") );
|
|
|
|
recentMenu->setEnabled(false); // TODO: setup recent projects menu
|
|
|
|
fileMenu->addSeparator();
|
|
|
|
// File -> Import Input
|
|
act = new QAction(tr("&Import Input"), this);
|
|
//act->setShortcut(QKeySequence(tr("Ctrl+Shift+S")));
|
|
act->setStatusTip(tr("Import Input"));
|
|
//act->setIcon( style()->standardIcon( QStyle::SP_BrowserStop ) );
|
|
//connect(act, SIGNAL(triggered()), this, SLOT(closeFile(void)) );
|
|
|
|
fileMenu->addAction(act);
|
|
|
|
// File -> Export to fm2
|
|
act = new QAction(tr("&Export to fm2"), this);
|
|
//act->setShortcut(QKeySequence(tr("Ctrl+Shift+S")));
|
|
act->setStatusTip(tr("Export to fm2"));
|
|
//act->setIcon( style()->standardIcon( QStyle::SP_BrowserStop ) );
|
|
//connect(act, SIGNAL(triggered()), this, SLOT(closeFile(void)) );
|
|
|
|
fileMenu->addAction(act);
|
|
|
|
fileMenu->addSeparator();
|
|
|
|
// File -> Quit
|
|
act = new QAction(tr("&Quit Window"), this);
|
|
act->setShortcut(QKeySequence::Close);
|
|
act->setStatusTip(tr("Close Window"));
|
|
act->setIcon(style()->standardIcon(QStyle::SP_DialogCloseButton));
|
|
connect(act, SIGNAL(triggered()), this, SLOT(closeWindow(void)) );
|
|
|
|
fileMenu->addAction(act);
|
|
|
|
return menuBar;
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void TasEditorWindow::buildPianoRollDisplay(void)
|
|
{
|
|
QVBoxLayout *vbox;
|
|
QHBoxLayout *hbox;
|
|
QGridLayout *grid;
|
|
|
|
grid = new QGridLayout();
|
|
pianoRoll = new QPianoRoll(this);
|
|
pianoRollVBar = new QScrollBar( Qt::Vertical, this );
|
|
pianoRollHBar = new QScrollBar( Qt::Horizontal, this );
|
|
upperMarkerLabel = new QLabel( tr("Marker 0") );
|
|
lowerMarkerLabel = new QLabel( tr("Marker 1") );
|
|
upperMarkerName = new QLineEdit();
|
|
lowerMarkerName = new QLineEdit();
|
|
|
|
pianoRoll->setScrollBars( pianoRollHBar, pianoRollVBar );
|
|
connect( pianoRollHBar, SIGNAL(valueChanged(int)), pianoRoll, SLOT(hbarChanged(int)) );
|
|
connect( pianoRollVBar, SIGNAL(valueChanged(int)), pianoRoll, SLOT(vbarChanged(int)) );
|
|
|
|
grid->addWidget( pianoRoll , 0, 0 );
|
|
grid->addWidget( pianoRollVBar, 0, 1 );
|
|
grid->addWidget( pianoRollHBar, 1, 0 );
|
|
|
|
vbox = new QVBoxLayout();
|
|
|
|
pianoRollHBar->setMinimum(0);
|
|
pianoRollHBar->setMaximum(100);
|
|
pianoRollVBar->setMinimum(0);
|
|
pianoRollVBar->setMaximum(100);
|
|
|
|
hbox = new QHBoxLayout();
|
|
hbox->addWidget( upperMarkerLabel, 1 );
|
|
hbox->addWidget( upperMarkerName, 10 );
|
|
|
|
vbox->addLayout( hbox, 1 );
|
|
vbox->addLayout( grid, 100 );
|
|
|
|
hbox = new QHBoxLayout();
|
|
hbox->addWidget( lowerMarkerLabel, 1 );
|
|
hbox->addWidget( lowerMarkerName, 10 );
|
|
|
|
vbox->addLayout( hbox, 1 );
|
|
|
|
pianoRollContainerWidget = new QWidget();
|
|
pianoRollContainerWidget->setLayout( vbox );
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void TasEditorWindow::buildSideControlPanel(void)
|
|
{
|
|
QShortcut *shortcut;
|
|
QVBoxLayout *vbox;
|
|
QHBoxLayout *hbox;
|
|
QGridLayout *grid;
|
|
|
|
patternsNames.resize(4);
|
|
patternsNames[0] = "Alternating (1010...)";
|
|
patternsNames[1] = "Alternating at 30FPS (11001100...)";
|
|
patternsNames[2] = "One Quarter (10001000...)";
|
|
patternsNames[3] = "Tap'n'Hold (101111111...)";
|
|
|
|
ctlPanelMainVbox = new QVBoxLayout();
|
|
|
|
playbackGBox = new QGroupBox( tr("Playback") );
|
|
recorderGBox = new QGroupBox( tr("Recorder") );
|
|
splicerGBox = new QGroupBox( tr("Splicer") );
|
|
luaGBox = new QGroupBox( tr("Lua") );
|
|
bookmarksGBox = new QGroupBox( tr("BookMarks/Branches") );
|
|
historyGBox = new QGroupBox( tr("History") );
|
|
|
|
rewindMkrBtn = new QPushButton();
|
|
rewindFrmBtn = new QPushButton();
|
|
playPauseBtn = new QPushButton();
|
|
advFrmBtn = new QPushButton();
|
|
advMkrBtn = new QPushButton();
|
|
|
|
rewindMkrBtn->setIcon( style()->standardIcon( QStyle::SP_MediaSkipBackward ) );
|
|
rewindFrmBtn->setIcon( style()->standardIcon( QStyle::SP_MediaSeekBackward ) );
|
|
playPauseBtn->setIcon( style()->standardIcon( QStyle::SP_MediaPause ) );
|
|
advFrmBtn->setIcon( style()->standardIcon( QStyle::SP_MediaSeekForward ) );
|
|
advMkrBtn->setIcon( style()->standardIcon( QStyle::SP_MediaSkipForward ) );
|
|
|
|
followCursorCbox = new QCheckBox( tr("Follow Cursor") );
|
|
turboSeekCbox = new QCheckBox( tr("Turbo Seek") );
|
|
autoRestoreCbox = new QCheckBox( tr("Auto-Restore Last Position") );
|
|
|
|
recRecordingCbox = new QCheckBox( tr("Recording") );
|
|
recSuperImposeCbox = new QCheckBox( tr("Superimpose") );
|
|
recUsePatternCbox = new QCheckBox( tr("Use Pattern") );
|
|
recAllBtn = new QRadioButton( tr("All") );
|
|
rec1PBtn = new QRadioButton( tr("1P") );
|
|
rec2PBtn = new QRadioButton( tr("2P") );
|
|
rec3PBtn = new QRadioButton( tr("3P") );
|
|
rec4PBtn = new QRadioButton( tr("4P") );
|
|
|
|
selectionLbl = new QLabel( tr("Empty") );
|
|
clipboardLbl = new QLabel( tr("Empty") );
|
|
|
|
runLuaBtn = new QPushButton( tr("Run Function") );
|
|
autoLuaCBox = new QCheckBox( tr("Auto Function") );
|
|
runLuaBtn->setEnabled(false);
|
|
autoLuaCBox->setChecked(true);
|
|
|
|
bkbrTree = new QTreeWidget();
|
|
histTree = new QTreeWidget();
|
|
|
|
prevMkrBtn = new QPushButton();
|
|
nextMkrBtn = new QPushButton();
|
|
similarBtn = new QPushButton( tr("Similar") );
|
|
moreBtn = new QPushButton( tr("More") );
|
|
|
|
prevMkrBtn->setIcon( style()->standardIcon( QStyle::SP_MediaSkipBackward ) );
|
|
nextMkrBtn->setIcon( style()->standardIcon( QStyle::SP_MediaSkipForward ) );
|
|
|
|
vbox = new QVBoxLayout();
|
|
hbox = new QHBoxLayout();
|
|
vbox->addLayout( hbox );
|
|
hbox->addWidget( rewindMkrBtn );
|
|
hbox->addWidget( rewindFrmBtn );
|
|
hbox->addWidget( playPauseBtn );
|
|
hbox->addWidget( advFrmBtn );
|
|
hbox->addWidget( advMkrBtn );
|
|
|
|
hbox = new QHBoxLayout();
|
|
vbox->addLayout( hbox );
|
|
hbox->addWidget( followCursorCbox );
|
|
hbox->addWidget( turboSeekCbox );
|
|
|
|
vbox->addWidget( autoRestoreCbox );
|
|
|
|
playbackGBox->setLayout( vbox );
|
|
|
|
grid = new QGridLayout();
|
|
grid->addWidget( recRecordingCbox, 0, 0, 1, 2 );
|
|
grid->addWidget( recAllBtn , 0, 3, 1, 1 );
|
|
grid->addWidget( rec1PBtn , 1, 0, 1, 1 );
|
|
grid->addWidget( rec2PBtn , 1, 1, 1, 1 );
|
|
grid->addWidget( rec3PBtn , 1, 2, 1, 1 );
|
|
grid->addWidget( rec4PBtn , 1, 3, 1, 1 );
|
|
grid->addWidget( recSuperImposeCbox, 2, 0, 1, 2 );
|
|
grid->addWidget( recUsePatternCbox , 2, 2, 1, 2 );
|
|
recorderGBox->setLayout( grid );
|
|
|
|
grid = new QGridLayout();
|
|
grid->addWidget( new QLabel( tr("Selection:") ), 0, 0, 1, 1 );
|
|
grid->addWidget( new QLabel( tr("Clipboard:") ), 1, 0, 1, 1 );
|
|
grid->addWidget( selectionLbl, 0, 1, 1, 3 );
|
|
grid->addWidget( clipboardLbl, 1, 1, 1, 3 );
|
|
splicerGBox->setLayout( grid );
|
|
|
|
hbox = new QHBoxLayout();
|
|
hbox->addWidget( runLuaBtn );
|
|
hbox->addWidget( autoLuaCBox );
|
|
luaGBox->setLayout( hbox );
|
|
|
|
vbox = new QVBoxLayout();
|
|
vbox->addWidget( bkbrTree );
|
|
bookmarksGBox->setLayout( vbox );
|
|
|
|
vbox = new QVBoxLayout();
|
|
vbox->addWidget( histTree );
|
|
historyGBox->setLayout( vbox );
|
|
|
|
ctlPanelMainVbox->addWidget( playbackGBox );
|
|
ctlPanelMainVbox->addWidget( recorderGBox );
|
|
ctlPanelMainVbox->addWidget( splicerGBox );
|
|
ctlPanelMainVbox->addWidget( luaGBox );
|
|
ctlPanelMainVbox->addWidget( bookmarksGBox );
|
|
ctlPanelMainVbox->addWidget( historyGBox );
|
|
|
|
hbox = new QHBoxLayout();
|
|
hbox->addWidget( prevMkrBtn );
|
|
hbox->addWidget( similarBtn );
|
|
hbox->addWidget( moreBtn );
|
|
hbox->addWidget( nextMkrBtn );
|
|
ctlPanelMainVbox->addLayout( hbox );
|
|
|
|
controlPanelContainerWidget = new QWidget();
|
|
controlPanelContainerWidget->setLayout( ctlPanelMainVbox );
|
|
|
|
recRecordingCbox->setChecked( !movie_readonly );
|
|
connect( recRecordingCbox, SIGNAL(stateChanged(int)), this, SLOT(recordingChanged(int)) );
|
|
|
|
recUsePatternCbox->setChecked( taseditorConfig.recordingUsePattern );
|
|
connect( recUsePatternCbox, SIGNAL(stateChanged(int)), this, SLOT(usePatternChanged(int)) );
|
|
|
|
recSuperImposeCbox->setTristate(true);
|
|
connect( recSuperImposeCbox, SIGNAL(stateChanged(int)), this, SLOT(superImposedChanged(int)) );
|
|
|
|
connect( recAllBtn, &QRadioButton::clicked, [ this ] { recordInputChanged( MULTITRACK_RECORDING_ALL ); } );
|
|
connect( rec1PBtn , &QRadioButton::clicked, [ this ] { recordInputChanged( MULTITRACK_RECORDING_1P ); } );
|
|
connect( rec2PBtn , &QRadioButton::clicked, [ this ] { recordInputChanged( MULTITRACK_RECORDING_2P ); } );
|
|
connect( rec3PBtn , &QRadioButton::clicked, [ this ] { recordInputChanged( MULTITRACK_RECORDING_3P ); } );
|
|
connect( rec4PBtn , &QRadioButton::clicked, [ this ] { recordInputChanged( MULTITRACK_RECORDING_4P ); } );
|
|
|
|
connect( rewindMkrBtn, SIGNAL(clicked(void)), this, SLOT(playbackFrameRewindFull(void)) );
|
|
connect( rewindFrmBtn, SIGNAL(clicked(void)), this, SLOT(playbackFrameRewind(void)) );
|
|
connect( playPauseBtn, SIGNAL(clicked(void)), this, SLOT(playbackPauseCB(void)) );
|
|
connect( advFrmBtn , SIGNAL(clicked(void)), this, SLOT(playbackFrameForward(void)) );
|
|
connect( advMkrBtn , SIGNAL(clicked(void)), this, SLOT(playbackFrameForwardFull(void)));
|
|
|
|
shortcut = new QShortcut( QKeySequence("Pause"), this);
|
|
connect( shortcut, SIGNAL(activated(void)), this, SLOT(playbackPauseCB(void)) );
|
|
|
|
shortcut = new QShortcut( QKeySequence("Shift+Up"), this);
|
|
connect( shortcut, SIGNAL(activated(void)), this, SLOT(playbackFrameRewind(void)) );
|
|
|
|
shortcut = new QShortcut( QKeySequence("Shift+Down"), this);
|
|
connect( shortcut, SIGNAL(activated(void)), this, SLOT(playbackFrameForward(void)) );
|
|
|
|
shortcut = new QShortcut( QKeySequence("Shift+PgUp"), this);
|
|
connect( shortcut, SIGNAL(activated(void)), this, SLOT(playbackFrameRewindFull(void)) );
|
|
|
|
shortcut = new QShortcut( QKeySequence("Shift+PgDown"), this);
|
|
connect( shortcut, SIGNAL(activated(void)), this, SLOT(playbackFrameForwardFull(void)) );
|
|
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
int TasEditorWindow::initModules(void)
|
|
{
|
|
// init modules
|
|
//editor.init();
|
|
//pianoRoll.init();
|
|
selection.init();
|
|
splicer.init();
|
|
playback.init();
|
|
greenzone.init();
|
|
recorder.init();
|
|
markersManager.init();
|
|
project.init();
|
|
bookmarks.init();
|
|
branches.init();
|
|
//popupDisplay.init();
|
|
history.init();
|
|
taseditor_lua.init();
|
|
// either start new movie or use current movie
|
|
if (!FCEUMOV_Mode(MOVIEMODE_RECORD|MOVIEMODE_PLAY) || currMovieData.savestate.size() != 0)
|
|
{
|
|
if (currMovieData.savestate.size() != 0)
|
|
{
|
|
FCEUD_PrintError("This version of TAS Editor doesn't work with movies starting from savestate.");
|
|
}
|
|
// create new movie
|
|
FCEUI_StopMovie();
|
|
movieMode = MOVIEMODE_TASEDITOR;
|
|
FCEUMOV_CreateCleanMovie();
|
|
playback.restartPlaybackFromZeroGround();
|
|
}
|
|
else
|
|
{
|
|
// use current movie to create a new project
|
|
FCEUI_StopMovie();
|
|
movieMode = MOVIEMODE_TASEDITOR;
|
|
}
|
|
// if movie length is less or equal to currFrame, pad it with empty frames
|
|
if (((int)currMovieData.records.size() - 1) < currFrameCounter)
|
|
{
|
|
currMovieData.insertEmpty(-1, currFrameCounter - ((int)currMovieData.records.size() - 1));
|
|
}
|
|
// ensure that movie has correct set of ports/fourscore
|
|
setInputType(currMovieData, getInputType(currMovieData));
|
|
// force the input configuration stored in the movie to apply to FCEUX config
|
|
applyMovieInputConfig();
|
|
// reset some modules that need MovieData info
|
|
//pianoRoll.reset();
|
|
recorder.reset();
|
|
// create initial snapshot in history
|
|
history.reset();
|
|
// reset Taseditor variables
|
|
//mustCallManualLuaFunction = false;
|
|
|
|
//SetFocus(history.hwndHistoryList); // set focus only once, to show blue selection cursor
|
|
//SetFocus(pianoRoll.hwndList);
|
|
FCEU_DispMessage("TAS Editor engaged", 0);
|
|
update();
|
|
return 0;
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void TasEditorWindow::frameUpdate(void)
|
|
{
|
|
fceuWrapperLock();
|
|
|
|
//printf("TAS Frame Update: %zi\n", currMovieData.records.size());
|
|
|
|
//taseditorWindow.update();
|
|
greenzone.update();
|
|
recorder.update();
|
|
//pianoRoll.update();
|
|
markersManager.update();
|
|
playback.update();
|
|
bookmarks.update();
|
|
branches.update();
|
|
//popupDisplay.update();
|
|
selection.update();
|
|
splicer.update();
|
|
history.update();
|
|
project.update();
|
|
// run Lua functions if needed
|
|
if (taseditorConfig.enableLuaAutoFunction)
|
|
{
|
|
//TaseditorAutoFunction();
|
|
}
|
|
//if (mustCallManualLuaFunction)
|
|
//{
|
|
// TaseditorManualFunction();
|
|
// mustCallManualLuaFunction = false;
|
|
//}
|
|
|
|
pianoRoll->update();
|
|
|
|
fceuWrapperUnLock();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
bool TasEditorWindow::loadProject(const char* fullname)
|
|
{
|
|
bool success = false;
|
|
|
|
fceuWrapperLock();
|
|
|
|
// try to load project
|
|
if (project.load(fullname))
|
|
{
|
|
// loaded successfully
|
|
applyMovieInputConfig();
|
|
// add new file to Recent menu
|
|
//taseditorWindow.updateRecentProjectsArray(fullname);
|
|
//taseditorWindow.updateCaption();
|
|
update();
|
|
success = true;
|
|
} else
|
|
{
|
|
// failed to load
|
|
//taseditorWindow.updateCaption();
|
|
update();
|
|
}
|
|
fceuWrapperUnLock();
|
|
|
|
return success;
|
|
}
|
|
bool TasEditorWindow::saveProject(bool save_compact)
|
|
{
|
|
bool ret = true;
|
|
|
|
fceuWrapperLock();
|
|
|
|
if (project.getProjectFile().empty())
|
|
{
|
|
ret = saveProjectAs(save_compact);
|
|
} else
|
|
{
|
|
if (save_compact)
|
|
{
|
|
project.save(0, taseditorConfig.saveCompact_SaveInBinary, taseditorConfig.saveCompact_SaveMarkers, taseditorConfig.saveCompact_SaveBookmarks, taseditorConfig.saveCompact_GreenzoneSavingMode, taseditorConfig.saveCompact_SaveHistory, taseditorConfig.saveCompact_SavePianoRoll, taseditorConfig.saveCompact_SaveSelection);
|
|
}
|
|
else
|
|
{
|
|
project.save(0, taseditorConfig.projectSavingOptions_SaveInBinary, taseditorConfig.projectSavingOptions_SaveMarkers, taseditorConfig.projectSavingOptions_SaveBookmarks, taseditorConfig.projectSavingOptions_GreenzoneSavingMode, taseditorConfig.projectSavingOptions_SaveHistory, taseditorConfig.projectSavingOptions_SavePianoRoll, taseditorConfig.projectSavingOptions_SaveSelection);
|
|
}
|
|
//taseditorWindow.updateCaption();
|
|
}
|
|
|
|
fceuWrapperUnLock();
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool TasEditorWindow::saveProjectAs(bool save_compact)
|
|
{
|
|
std::string last;
|
|
int ret, useNativeFileDialogVal;
|
|
QString filename;
|
|
std::string lastPath;
|
|
//char dir[512];
|
|
const char *base, *rom;
|
|
QFileDialog dialog(this, tr("Save TAS Editor Project As") );
|
|
QList<QUrl> urls;
|
|
QDir d;
|
|
|
|
dialog.setFileMode(QFileDialog::AnyFile);
|
|
|
|
dialog.setNameFilter(tr("TAS Project Files (*.fm3) ;; All files (*)"));
|
|
|
|
dialog.setViewMode(QFileDialog::List);
|
|
dialog.setFilter( QDir::AllEntries | QDir::AllDirs | QDir::Hidden );
|
|
dialog.setLabelText( QFileDialog::Accept, tr("Save") );
|
|
|
|
base = FCEUI_GetBaseDirectory();
|
|
|
|
urls << QUrl::fromLocalFile( QDir::rootPath() );
|
|
urls << QUrl::fromLocalFile(QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first());
|
|
urls << QUrl::fromLocalFile(QStandardPaths::standardLocations(QStandardPaths::DownloadLocation).first());
|
|
|
|
if ( base )
|
|
{
|
|
urls << QUrl::fromLocalFile( QDir( base ).absolutePath() );
|
|
|
|
d.setPath( QString(base) + "/movies");
|
|
|
|
if ( d.exists() )
|
|
{
|
|
urls << QUrl::fromLocalFile( d.absolutePath() );
|
|
}
|
|
|
|
dialog.setDirectory( d.absolutePath() );
|
|
}
|
|
dialog.setDefaultSuffix( tr(".fm3") );
|
|
|
|
g_config->getOption ("SDL.TasProjectFilePath", &lastPath);
|
|
if ( lastPath.size() > 0 )
|
|
{
|
|
dialog.setDirectory( QString::fromStdString(lastPath) );
|
|
}
|
|
|
|
rom = getRomFile();
|
|
|
|
if ( rom )
|
|
{
|
|
char baseName[512];
|
|
getFileBaseName( rom, baseName );
|
|
|
|
if ( baseName[0] != 0 )
|
|
{
|
|
dialog.selectFile(baseName);
|
|
}
|
|
}
|
|
|
|
// Check config option to use native file dialog or not
|
|
g_config->getOption ("SDL.UseNativeFileDialog", &useNativeFileDialogVal);
|
|
|
|
dialog.setOption(QFileDialog::DontUseNativeDialog, !useNativeFileDialogVal);
|
|
dialog.setSidebarUrls(urls);
|
|
|
|
ret = dialog.exec();
|
|
|
|
if ( ret )
|
|
{
|
|
QStringList fileList;
|
|
fileList = dialog.selectedFiles();
|
|
|
|
if ( fileList.size() > 0 )
|
|
{
|
|
filename = fileList[0];
|
|
}
|
|
}
|
|
|
|
if ( filename.isNull() )
|
|
{
|
|
return false;
|
|
}
|
|
//qDebug() << "selected file path : " << filename.toUtf8();
|
|
|
|
project.renameProject( filename.toStdString().c_str(), true);
|
|
if (save_compact)
|
|
{
|
|
project.save( filename.toStdString().c_str(), taseditorConfig.saveCompact_SaveInBinary, taseditorConfig.saveCompact_SaveMarkers, taseditorConfig.saveCompact_SaveBookmarks, taseditorConfig.saveCompact_GreenzoneSavingMode, taseditorConfig.saveCompact_SaveHistory, taseditorConfig.saveCompact_SavePianoRoll, taseditorConfig.saveCompact_SaveSelection);
|
|
}
|
|
else
|
|
{
|
|
project.save( filename.toStdString().c_str(), taseditorConfig.projectSavingOptions_SaveInBinary, taseditorConfig.projectSavingOptions_SaveMarkers, taseditorConfig.projectSavingOptions_SaveBookmarks, taseditorConfig.projectSavingOptions_GreenzoneSavingMode, taseditorConfig.projectSavingOptions_SaveHistory, taseditorConfig.projectSavingOptions_SavePianoRoll, taseditorConfig.projectSavingOptions_SaveSelection);
|
|
}
|
|
//taseditorWindow.updateRecentProjectsArray(nameo);
|
|
// saved successfully - remove * mark from caption
|
|
//taseditorWindow.updateCaption();
|
|
return true;
|
|
}
|
|
|
|
// returns false if user doesn't want to exit
|
|
bool TasEditorWindow::askToSaveProject(void)
|
|
{
|
|
bool changesFound = false;
|
|
if (project.getProjectChanged())
|
|
{
|
|
changesFound = true;
|
|
}
|
|
|
|
// ask saving project
|
|
if (changesFound)
|
|
{
|
|
int ans = QMessageBox::question( this, tr("TAS Editor"), tr("Save project changes?"),
|
|
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes );
|
|
|
|
//int answer = MessageBox(taseditorWindow.hwndTASEditor, "Save Project changes?", "TAS Editor", MB_YESNOCANCEL);
|
|
if (ans == QMessageBox::Yes)
|
|
{
|
|
return saveProject();
|
|
}
|
|
return (ans != QMessageBox::No);
|
|
}
|
|
return true;
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void TasEditorWindow::openProject(void)
|
|
{
|
|
std::string last;
|
|
int ret, useNativeFileDialogVal;
|
|
QString filename;
|
|
std::string lastPath;
|
|
//char dir[512];
|
|
const char *base, *rom;
|
|
QFileDialog dialog(this, tr("Open TAS Editor Project") );
|
|
QList<QUrl> urls;
|
|
QDir d;
|
|
|
|
dialog.setFileMode(QFileDialog::ExistingFile);
|
|
|
|
dialog.setNameFilter(tr("TAS Project Files (*.fm3) ;; All files (*)"));
|
|
|
|
dialog.setViewMode(QFileDialog::List);
|
|
dialog.setFilter( QDir::AllEntries | QDir::AllDirs | QDir::Hidden );
|
|
dialog.setLabelText( QFileDialog::Accept, tr("Open") );
|
|
|
|
base = FCEUI_GetBaseDirectory();
|
|
|
|
urls << QUrl::fromLocalFile( QDir::rootPath() );
|
|
urls << QUrl::fromLocalFile(QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first());
|
|
urls << QUrl::fromLocalFile(QStandardPaths::standardLocations(QStandardPaths::DownloadLocation).first());
|
|
|
|
if ( base )
|
|
{
|
|
urls << QUrl::fromLocalFile( QDir( base ).absolutePath() );
|
|
|
|
d.setPath( QString(base) + "/movies");
|
|
|
|
if ( d.exists() )
|
|
{
|
|
urls << QUrl::fromLocalFile( d.absolutePath() );
|
|
}
|
|
|
|
dialog.setDirectory( d.absolutePath() );
|
|
}
|
|
dialog.setDefaultSuffix( tr(".fm3") );
|
|
|
|
g_config->getOption ("SDL.TasProjectFilePath", &lastPath);
|
|
if ( lastPath.size() > 0 )
|
|
{
|
|
dialog.setDirectory( QString::fromStdString(lastPath) );
|
|
}
|
|
|
|
rom = getRomFile();
|
|
|
|
if ( rom )
|
|
{
|
|
char baseName[512];
|
|
getFileBaseName( rom, baseName );
|
|
|
|
if ( baseName[0] != 0 )
|
|
{
|
|
dialog.selectFile(baseName);
|
|
}
|
|
}
|
|
|
|
// Check config option to use native file dialog or not
|
|
g_config->getOption ("SDL.UseNativeFileDialog", &useNativeFileDialogVal);
|
|
|
|
dialog.setOption(QFileDialog::DontUseNativeDialog, !useNativeFileDialogVal);
|
|
dialog.setSidebarUrls(urls);
|
|
|
|
ret = dialog.exec();
|
|
|
|
if ( ret )
|
|
{
|
|
QStringList fileList;
|
|
fileList = dialog.selectedFiles();
|
|
|
|
if ( fileList.size() > 0 )
|
|
{
|
|
filename = fileList[0];
|
|
}
|
|
}
|
|
|
|
if ( filename.isNull() )
|
|
{
|
|
return;
|
|
}
|
|
//qDebug() << "selected file path : " << filename.toUtf8();
|
|
|
|
loadProject( filename.toStdString().c_str());
|
|
|
|
return;
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void TasEditorWindow::createNewProject(void)
|
|
{
|
|
//if (!askToSaveProject()) return;
|
|
|
|
fceuWrapperLock();
|
|
|
|
static struct NewProjectParameters params;
|
|
//if (DialogBoxParam(fceu_hInstance, MAKEINTRESOURCE(IDD_TASEDITOR_NEWPROJECT), taseditorWindow.hwndTASEditor, newProjectProc, (LPARAM)¶ms) > 0)
|
|
//{
|
|
FCEUMOV_CreateCleanMovie();
|
|
// apply selected options
|
|
setInputType(currMovieData, params.inputType);
|
|
applyMovieInputConfig();
|
|
if (params.copyCurrentInput)
|
|
{
|
|
// copy Input from current snapshot (from history)
|
|
history.getCurrentSnapshot().inputlog.toMovie(currMovieData);
|
|
}
|
|
if (!params.copyCurrentMarkers)
|
|
{
|
|
markersManager.reset();
|
|
}
|
|
if (params.authorName != L"") currMovieData.comments.push_back(L"author " + params.authorName);
|
|
|
|
// reset Taseditor
|
|
project.init(); // new project has blank name
|
|
greenzone.reset();
|
|
if (params.copyCurrentInput)
|
|
{
|
|
// copy LagLog from current snapshot (from history)
|
|
greenzone.lagLog = history.getCurrentSnapshot().laglog;
|
|
}
|
|
playback.reset();
|
|
playback.restartPlaybackFromZeroGround();
|
|
bookmarks.reset();
|
|
branches.reset();
|
|
history.reset();
|
|
//pianoRoll.reset();
|
|
selection.reset();
|
|
//editor.reset();
|
|
splicer.reset();
|
|
recorder.reset();
|
|
//popupDisplay.reset();
|
|
//taseditorWindow.redraw();
|
|
//taseditorWindow.updateCaption();
|
|
update();
|
|
//}
|
|
fceuWrapperUnLock();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void TasEditorWindow::saveProjectCb(void)
|
|
{
|
|
saveProject();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void TasEditorWindow::saveProjectAsCb(void)
|
|
{
|
|
saveProjectAs();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void TasEditorWindow::saveProjectCompactCb(void)
|
|
{
|
|
saveProject(true);
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void TasEditorWindow::recordingChanged(int state)
|
|
{
|
|
FCEUI_MovieToggleReadOnly();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void TasEditorWindow::superImposedChanged(int state)
|
|
{
|
|
if ( state == Qt::Checked )
|
|
{
|
|
taseditorConfig.superimpose = SUPERIMPOSE_CHECKED;
|
|
}
|
|
else if ( state == Qt::PartiallyChecked )
|
|
{
|
|
taseditorConfig.superimpose = SUPERIMPOSE_INDETERMINATE;
|
|
}
|
|
else
|
|
{
|
|
taseditorConfig.superimpose = SUPERIMPOSE_UNCHECKED;
|
|
}
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void TasEditorWindow::usePatternChanged(int state)
|
|
{
|
|
taseditorConfig.recordingUsePattern ^= 1;
|
|
recorder.patternOffset = 0;
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void TasEditorWindow::recordInputChanged(int input)
|
|
{
|
|
//printf("Input Change: %i\n", input);
|
|
recorder.multitrackRecordingJoypadNumber = input;
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void TasEditorWindow::playbackPauseCB(void)
|
|
{
|
|
fceuWrapperLock();
|
|
playback.toggleEmulationPause();
|
|
pianoRoll->update();
|
|
fceuWrapperUnLock();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void TasEditorWindow::playbackFrameRewind(void)
|
|
{
|
|
fceuWrapperLock();
|
|
playback.handleRewindFrame();
|
|
pianoRoll->update();
|
|
fceuWrapperUnLock();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void TasEditorWindow::playbackFrameForward(void)
|
|
{
|
|
fceuWrapperLock();
|
|
playback.handleForwardFrame();
|
|
pianoRoll->update();
|
|
fceuWrapperUnLock();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void TasEditorWindow::playbackFrameRewindFull(void)
|
|
{
|
|
fceuWrapperLock();
|
|
playback.handleRewindFull();
|
|
pianoRoll->update();
|
|
fceuWrapperUnLock();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void TasEditorWindow::playbackFrameForwardFull(void)
|
|
{
|
|
fceuWrapperLock();
|
|
playback.handleForwardFull();
|
|
pianoRoll->update();
|
|
fceuWrapperUnLock();
|
|
}
|
|
// ----------------------------------------------------------------------------------------------
|
|
// following functions use function parameters to determine range of frames
|
|
void TasEditorWindow::toggleInput(int start, int end, int joy, int button, int consecutivenessTag)
|
|
{
|
|
if (joy < 0 || joy >= joysticksPerFrame[getInputType(currMovieData)]) return;
|
|
|
|
int check_frame = end;
|
|
if (start > end)
|
|
{
|
|
// swap
|
|
int temp_start = start;
|
|
start = end;
|
|
end = temp_start;
|
|
}
|
|
if (start < 0) start = end;
|
|
if (end >= currMovieData.getNumRecords())
|
|
return;
|
|
|
|
if (currMovieData.records[check_frame].checkBit(joy, button))
|
|
{
|
|
// clear range
|
|
for (int i = start; i <= end; ++i)
|
|
currMovieData.records[i].clearBit(joy, button);
|
|
greenzone.invalidateAndUpdatePlayback(history.registerChanges(MODTYPE_UNSET, start, end, 0, NULL, consecutivenessTag));
|
|
} else
|
|
{
|
|
// set range
|
|
for (int i = start; i <= end; ++i)
|
|
currMovieData.records[i].setBit(joy, button);
|
|
greenzone.invalidateAndUpdatePlayback(history.registerChanges(MODTYPE_SET, start, end, 0, NULL, consecutivenessTag));
|
|
}
|
|
}
|
|
void TasEditorWindow::setInputUsingPattern(int start, int end, int joy, int button, int consecutivenessTag)
|
|
{
|
|
if (joy < 0 || joy >= joysticksPerFrame[getInputType(currMovieData)]) return;
|
|
|
|
if (start > end)
|
|
{
|
|
// swap
|
|
int temp_start = start;
|
|
start = end;
|
|
end = temp_start;
|
|
}
|
|
if (start < 0) start = end;
|
|
if (end >= currMovieData.getNumRecords())
|
|
return;
|
|
|
|
int pattern_offset = 0, current_pattern = taseditorConfig.currentPattern;
|
|
bool changes_made = false;
|
|
bool value;
|
|
|
|
for (int i = start; i <= end; ++i)
|
|
{
|
|
// skip lag frames
|
|
if (taseditorConfig.autofirePatternSkipsLag && greenzone.lagLog.getLagInfoAtFrame(i) == LAGGED_YES)
|
|
continue;
|
|
value = (patterns[current_pattern][pattern_offset] != 0);
|
|
if (currMovieData.records[i].checkBit(joy, button) != value)
|
|
{
|
|
changes_made = true;
|
|
currMovieData.records[i].setBitValue(joy, button, value);
|
|
}
|
|
pattern_offset++;
|
|
if (pattern_offset >= (int)patterns[current_pattern].size())
|
|
pattern_offset -= patterns[current_pattern].size();
|
|
}
|
|
if (changes_made)
|
|
greenzone.invalidateAndUpdatePlayback(history.registerChanges(MODTYPE_PATTERN, start, end, 0, patternsNames[current_pattern].c_str(), consecutivenessTag));
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
//----------------------------------------------------------------------------
|
|
//---- TAS Piano Roll Widget
|
|
//----------------------------------------------------------------------------
|
|
QPianoRoll::QPianoRoll(QWidget *parent)
|
|
: QWidget( parent )
|
|
{
|
|
QPalette pal;
|
|
std::string fontString;
|
|
QColor fg("black"), bg("white"), c;
|
|
|
|
useDarkTheme = false;
|
|
|
|
viewWidth = 512;
|
|
viewHeight = 512;
|
|
|
|
g_config->getOption("SDL.TasPianoRollFont", &fontString);
|
|
|
|
if ( fontString.size() > 0 )
|
|
{
|
|
font.fromString( QString::fromStdString( fontString ) );
|
|
}
|
|
else
|
|
{
|
|
font.setFamily("Courier New");
|
|
font.setStyle( QFont::StyleNormal );
|
|
font.setStyleHint( QFont::Monospace );
|
|
}
|
|
pal = this->palette();
|
|
|
|
windowColor = pal.color(QPalette::Window);
|
|
|
|
// Figure out if we are using a light or dark theme by checking the
|
|
// default window text grayscale color. If more white, then we will
|
|
// use white text on black background, else we do the opposite.
|
|
c = pal.color(QPalette::WindowText);
|
|
|
|
if ( qGray( c.red(), c.green(), c.blue() ) > 128 )
|
|
{
|
|
useDarkTheme = true;
|
|
}
|
|
//printf("WindowText: R:%i G:%i B:%i \n", c.red(), c.green(), c.blue() );
|
|
|
|
if ( useDarkTheme )
|
|
{
|
|
pal.setColor(QPalette::Base , fg );
|
|
pal.setColor(QPalette::Window , fg );
|
|
pal.setColor(QPalette::WindowText, bg );
|
|
}
|
|
else
|
|
{
|
|
pal.setColor(QPalette::Base , bg );
|
|
pal.setColor(QPalette::Window , bg );
|
|
pal.setColor(QPalette::WindowText, fg );
|
|
}
|
|
this->parent = qobject_cast <TasEditorWindow*>( parent );
|
|
this->setMouseTracking(true);
|
|
this->setPalette(pal);
|
|
|
|
numCtlr = 2;
|
|
calcFontData();
|
|
|
|
vbar = NULL;
|
|
hbar = NULL;
|
|
|
|
lineOffset = 0;
|
|
maxLineOffset = 0;
|
|
dragMode = DRAG_MODE_NONE;
|
|
dragSelectionStartingFrame = 0;
|
|
dragSelectionEndingFrame = 0;
|
|
realRowUnderMouse = -1;
|
|
markerDragFrameNumber = 0;
|
|
markerDragCountdown = 0;
|
|
drawingStartTimestamp = 0;
|
|
mouse_x = mouse_y = -1;
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
QPianoRoll::~QPianoRoll(void)
|
|
{
|
|
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void QPianoRoll::setScrollBars( QScrollBar *h, QScrollBar *v )
|
|
{
|
|
hbar = h; vbar = v;
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void QPianoRoll::hbarChanged(int val)
|
|
{
|
|
if ( viewWidth >= pxLineWidth )
|
|
{
|
|
pxLineXScroll = 0;
|
|
}
|
|
else
|
|
{
|
|
pxLineXScroll = val;
|
|
}
|
|
update();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void QPianoRoll::vbarChanged(int val)
|
|
{
|
|
lineOffset = val;
|
|
|
|
if ( lineOffset < 0 )
|
|
{
|
|
lineOffset = 0;
|
|
}
|
|
else if ( lineOffset > maxLineOffset )
|
|
{
|
|
lineOffset = maxLineOffset;
|
|
}
|
|
update();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void QPianoRoll::calcFontData(void)
|
|
{
|
|
QWidget::setFont(font);
|
|
QFontMetrics metrics(font);
|
|
#if QT_VERSION > QT_VERSION_CHECK(5, 11, 0)
|
|
pxCharWidth = metrics.horizontalAdvance(QLatin1Char('2'));
|
|
#else
|
|
pxCharWidth = metrics.width(QLatin1Char('2'));
|
|
#endif
|
|
pxCharHeight = metrics.capHeight();
|
|
pxLineSpacing = metrics.lineSpacing() * 1.25;
|
|
pxLineLead = pxLineSpacing - metrics.height();
|
|
pxCursorHeight = metrics.height();
|
|
pxLineTextOfs = pxCharHeight + (pxLineSpacing - pxCharHeight) / 2;
|
|
|
|
//printf("W:%i H:%i LS:%i \n", pxCharWidth, pxCharHeight, pxLineSpacing );
|
|
|
|
viewLines = (viewHeight / pxLineSpacing) + 1;
|
|
|
|
pxWidthCol1 = 3 * pxCharWidth;
|
|
pxWidthFrameCol = 12 * pxCharWidth;
|
|
pxWidthBtnCol = 3 * pxCharWidth;
|
|
pxWidthCtlCol = 8 * pxWidthBtnCol;
|
|
|
|
pxFrameColX = pxWidthCol1;
|
|
|
|
for (int i=0; i<4; i++)
|
|
{
|
|
pxFrameCtlX[i] = pxFrameColX + pxWidthFrameCol + (i*pxWidthCtlCol);
|
|
}
|
|
pxLineWidth = pxFrameCtlX[ numCtlr-1 ] + pxWidthCtlCol;
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
QPoint QPianoRoll::convPixToCursor( QPoint p )
|
|
{
|
|
QPoint c(0,0);
|
|
|
|
if ( p.x() < 0 )
|
|
{
|
|
c.setX(0);
|
|
}
|
|
else
|
|
{
|
|
float x = (float)(p.x() + pxLineXScroll) / pxCharWidth;
|
|
|
|
c.setX( (int)x );
|
|
}
|
|
|
|
if ( p.y() < 0 )
|
|
{
|
|
c.setY( -1 );
|
|
}
|
|
else
|
|
{
|
|
float py = ( (float)p.y() ) / (float)pxLineSpacing;
|
|
|
|
c.setY( (int)py - 1 );
|
|
}
|
|
return c;
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
int QPianoRoll::calcColumn( int px )
|
|
{
|
|
int col = -1;
|
|
|
|
px = px + pxLineXScroll;
|
|
|
|
if ( px < pxFrameColX )
|
|
{
|
|
col = COLUMN_ICONS;
|
|
}
|
|
else if ( px < pxFrameCtlX[0] )
|
|
{
|
|
col = COLUMN_FRAMENUM;
|
|
}
|
|
else
|
|
{
|
|
int i=0;
|
|
|
|
while ( px < pxFrameCtlX[i] )
|
|
{
|
|
if ( i >= 3 )
|
|
{
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
col = COLUMN_JOYPAD1_A + (i*8) + ( (px - pxFrameCtlX[i]) / pxWidthBtnCol);
|
|
}
|
|
return col;
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void QPianoRoll::drawArrow( QPainter *painter, int xl, int yl, int value )
|
|
{
|
|
int x, y, w, h, b, b2;
|
|
QPoint p[3];
|
|
|
|
x = xl+(pxCharWidth/3);
|
|
y = yl+1;
|
|
w = pxCharWidth;
|
|
h = pxLineSpacing-2;
|
|
|
|
p[0] = QPoint( x, y );
|
|
p[1] = QPoint( x, y+h );
|
|
p[2] = QPoint( x+w, y+(h/2) );
|
|
|
|
if ( value == GREEN_BLUE_ARROW_IMAGE_ID )
|
|
{
|
|
painter->setBrush( QColor( 96, 192, 192) );
|
|
}
|
|
else if ( value == GREEN_ARROW_IMAGE_ID )
|
|
{
|
|
painter->setBrush( QColor(0, 192, 64) );
|
|
}
|
|
else if ( value == BLUE_ARROW_IMAGE_ID )
|
|
{
|
|
painter->setBrush( QColor(10, 36, 106) );
|
|
}
|
|
|
|
painter->drawPolygon( p, 3 );
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void QPianoRoll::resizeEvent(QResizeEvent *event)
|
|
{
|
|
viewWidth = event->size().width();
|
|
viewHeight = event->size().height();
|
|
|
|
//printf("QPianoRoll Resize: %ix%i $%04X\n", viewWidth, viewHeight );
|
|
|
|
viewLines = (viewHeight / pxLineSpacing) + 1;
|
|
|
|
maxLineOffset = currMovieData.records.size() - viewLines + 1;
|
|
|
|
if ( maxLineOffset < 0 )
|
|
{
|
|
maxLineOffset = 0;
|
|
}
|
|
vbar->setMinimum(0);
|
|
vbar->setMaximum(maxLineOffset);
|
|
|
|
if ( viewWidth >= pxLineWidth )
|
|
{
|
|
pxLineXScroll = 0;
|
|
hbar->hide();
|
|
}
|
|
else
|
|
{
|
|
hbar->setPageStep( viewWidth );
|
|
hbar->setMaximum( pxLineWidth - viewWidth );
|
|
hbar->show();
|
|
pxLineXScroll = hbar->value();
|
|
}
|
|
vbar->setPageStep( (3*viewLines)/4 );
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void QPianoRoll::mousePressEvent(QMouseEvent * event)
|
|
{
|
|
int col, line, row_index, column_index, kbModifiers, alt_pressed;
|
|
QPoint c = convPixToCursor( event->pos() );
|
|
|
|
mouse_x = event->pos().x();
|
|
mouse_y = event->pos().y();
|
|
|
|
line = lineOffset + c.y();
|
|
col = calcColumn( event->pos().x() );
|
|
|
|
row_index = line;
|
|
rowUnderMouse = realRowUnderMouse = line;
|
|
columnUnderMouse = column_index = col;
|
|
|
|
kbModifiers = QApplication::keyboardModifiers();
|
|
alt_pressed = (kbModifiers & Qt::AltModifier) ? 1 : 0;
|
|
|
|
printf("Mouse Button Pressed: 0x%x (%i,%i)\n", event->button(), c.x(), c.y() );
|
|
|
|
if ( event->button() == Qt::LeftButton )
|
|
{
|
|
if (col == COLUMN_ICONS)
|
|
{
|
|
// clicked on the "icons" column
|
|
startDraggingPlaybackCursor();
|
|
}
|
|
else if ( (col == COLUMN_FRAMENUM) || (col == COLUMN_FRAMENUM2) )
|
|
{
|
|
// clicked on the "Frame#" column
|
|
if (row_index >= 0)
|
|
{
|
|
if (kbModifiers & Qt::ShiftModifier)
|
|
{
|
|
// select region from selection_beginning to row_index
|
|
int selection_beginning = selection->getCurrentRowsSelectionBeginning();
|
|
if (selection_beginning >= 0)
|
|
{
|
|
if (selection_beginning < row_index)
|
|
{
|
|
selection->setRegionOfRowsSelection(selection_beginning, row_index + 1);
|
|
}
|
|
else
|
|
{
|
|
selection->setRegionOfRowsSelection(row_index, selection_beginning + 1);
|
|
}
|
|
}
|
|
startSelectingDrag(row_index);
|
|
}
|
|
else if (kbModifiers & Qt::AltModifier)
|
|
{
|
|
// make Selection by Pattern
|
|
int selection_beginning = selection->getCurrentRowsSelectionBeginning();
|
|
if (selection_beginning >= 0)
|
|
{
|
|
selection->clearAllRowsSelection();
|
|
if (selection_beginning < row_index)
|
|
{
|
|
selection->setRegionOfRowsSelectionUsingPattern(selection_beginning, row_index);
|
|
}
|
|
else
|
|
{
|
|
selection->setRegionOfRowsSelectionUsingPattern(row_index, selection_beginning);
|
|
}
|
|
}
|
|
if (selection->isRowSelected(row_index))
|
|
{
|
|
startDeselectingDrag(row_index);
|
|
}
|
|
else
|
|
{
|
|
startSelectingDrag(row_index);
|
|
}
|
|
}
|
|
else if (kbModifiers & Qt::ControlModifier)
|
|
{
|
|
// clone current selection, so that user will be able to revert
|
|
if (selection->getCurrentRowsSelectionSize() > 0)
|
|
{
|
|
selection->addCurrentSelectionToHistory();
|
|
}
|
|
if (selection->isRowSelected(row_index))
|
|
{
|
|
selection->clearSingleRowSelection(row_index);
|
|
startDeselectingDrag(row_index);
|
|
}
|
|
else
|
|
{
|
|
selection->setRowSelection(row_index);
|
|
startSelectingDrag(row_index);
|
|
}
|
|
}
|
|
else // just click
|
|
{
|
|
selection->clearAllRowsSelection();
|
|
selection->setRowSelection(row_index);
|
|
startSelectingDrag(row_index);
|
|
}
|
|
}
|
|
}
|
|
else if (column_index >= COLUMN_JOYPAD1_A && column_index <= COLUMN_JOYPAD4_R)
|
|
{
|
|
// clicked on Input
|
|
if (row_index >= 0)
|
|
{
|
|
if (!alt_pressed && !(kbModifiers & Qt::ShiftModifier))
|
|
{
|
|
// clicked without Shift/Alt - bring Selection cursor to this row
|
|
selection->clearAllRowsSelection();
|
|
selection->setRowSelection(row_index);
|
|
}
|
|
// toggle Input
|
|
drawingStartTimestamp = clock();
|
|
int joy = (column_index - COLUMN_JOYPAD1_A) / NUM_JOYPAD_BUTTONS;
|
|
int button = (column_index - COLUMN_JOYPAD1_A) % NUM_JOYPAD_BUTTONS;
|
|
int selection_beginning = selection->getCurrentRowsSelectionBeginning();
|
|
if (alt_pressed && selection_beginning >= 0)
|
|
{
|
|
tasWin->setInputUsingPattern(selection_beginning, row_index, joy, button, drawingStartTimestamp);
|
|
}
|
|
else if ((kbModifiers & Qt::ShiftModifier) && selection_beginning >= 0)
|
|
{
|
|
tasWin->toggleInput(selection_beginning, row_index, joy, button, drawingStartTimestamp);
|
|
}
|
|
else
|
|
{
|
|
tasWin->toggleInput(row_index, row_index, joy, button, drawingStartTimestamp);
|
|
}
|
|
// and start dragging/drawing
|
|
if (dragMode == DRAG_MODE_NONE)
|
|
{
|
|
if (taseditorConfig->drawInputByDragging)
|
|
{
|
|
// if clicked this click created buttonpress, then start painting, else start erasing
|
|
if (currMovieData.records[row_index].checkBit(joy, button))
|
|
{
|
|
dragMode = DRAG_MODE_SET;
|
|
}
|
|
else
|
|
{
|
|
dragMode = DRAG_MODE_UNSET;
|
|
}
|
|
//pianoRoll.drawingLastX = GET_X_LPARAM(lParam) + GetScrollPos(pianoRoll.hwndList, SB_HORZ);
|
|
//pianoRoll.drawingLastY = GET_Y_LPARAM(lParam) + GetScrollPos(pianoRoll.hwndList, SB_VERT) * pianoRoll.listRowHeight;
|
|
}
|
|
else
|
|
{
|
|
dragMode = DRAG_MODE_OBSERVE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//updateDrag();
|
|
}
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void QPianoRoll::mouseReleaseEvent(QMouseEvent * event)
|
|
{
|
|
int col, line;
|
|
QPoint c = convPixToCursor( event->pos() );
|
|
|
|
mouse_x = event->pos().x();
|
|
mouse_y = event->pos().y();
|
|
|
|
line = lineOffset + c.y();
|
|
col = calcColumn( event->pos().x() );
|
|
|
|
rowUnderMouse = realRowUnderMouse = line;
|
|
columnUnderMouse = col;
|
|
|
|
printf("Mouse Button Released: 0x%x (%i,%i)\n", event->button(), c.x(), c.y() );
|
|
|
|
if ( event->button() == Qt::LeftButton )
|
|
{
|
|
if (dragMode != DRAG_MODE_NONE)
|
|
{
|
|
// check if user released left button
|
|
finishDrag();
|
|
}
|
|
}
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void QPianoRoll::mouseMoveEvent(QMouseEvent * event)
|
|
{
|
|
int col, line;
|
|
QPoint c = convPixToCursor( event->pos() );
|
|
|
|
mouse_x = event->pos().x();
|
|
mouse_y = event->pos().y();
|
|
|
|
line = lineOffset + c.y();
|
|
col = calcColumn( event->pos().x() );
|
|
|
|
rowUnderMouse = realRowUnderMouse = line;
|
|
columnUnderMouse = col;
|
|
|
|
printf("Mouse Move Event: 0x%x (%i,%i) Col:%i\n", event->button(), c.x(), c.y(), col );
|
|
|
|
if ( event->button() == Qt::LeftButton )
|
|
{
|
|
|
|
}
|
|
updateDrag();
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void QPianoRoll::updateDrag(void)
|
|
{
|
|
int kbModifiers, altHeld;
|
|
|
|
if ( dragMode == DRAG_MODE_NONE )
|
|
{
|
|
return;
|
|
}
|
|
kbModifiers = QApplication::keyboardModifiers();
|
|
|
|
altHeld = (kbModifiers & Qt::AltModifier) ? 1 : 0;
|
|
|
|
// perform drag
|
|
switch (dragMode)
|
|
{
|
|
case DRAG_MODE_PLAYBACK:
|
|
{
|
|
handlePlaybackCursorDragging();
|
|
break;
|
|
}
|
|
case DRAG_MODE_MARKER:
|
|
{
|
|
// if suddenly source frame lost its Marker, abort drag
|
|
if (!markersManager->getMarkerAtFrame(markerDragFrameNumber))
|
|
{
|
|
//if (hwndMarkerDragBox)
|
|
//{
|
|
// DestroyWindow(hwndMarkerDragBox);
|
|
// hwndMarkerDragBox = 0;
|
|
//}
|
|
dragMode = DRAG_MODE_NONE;
|
|
break;
|
|
}
|
|
// when dragging, always show semi-transparent yellow rectangle under mouse
|
|
//POINT p = {0, 0};
|
|
//GetCursorPos(&p);
|
|
//markerDragBoxX = p.x - markerDragBoxDX;
|
|
//markerDragBoxY = p.y - markerDragBoxDY;
|
|
//if (!hwndMarkerDragBox)
|
|
//{
|
|
// hwndMarkerDragBox = CreateWindowEx(WS_EX_LAYERED | WS_EX_TRANSPARENT, markerDragBoxClassName, markerDragBoxClassName, WS_POPUP, markerDragBoxX, markerDragBoxY, COLUMN_FRAMENUM_WIDTH, listRowHeight, taseditorWindow.hwndTASEditor, NULL, fceu_hInstance, NULL);
|
|
// ShowWindow(hwndMarkerDragBox, SW_SHOWNA);
|
|
//} else
|
|
//{
|
|
// SetWindowPos(hwndMarkerDragBox, 0, markerDragBoxX, markerDragBoxY, 0, 0, SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE);
|
|
//}
|
|
//SetLayeredWindowAttributes(hwndMarkerDragBox, 0, MARKER_DRAG_BOX_ALPHA, LWA_ALPHA);
|
|
//UpdateLayeredWindow(hwndMarkerDragBox, 0, 0, 0, 0, 0, 0, &blend, ULW_ALPHA);
|
|
break;
|
|
}
|
|
case DRAG_MODE_SET:
|
|
case DRAG_MODE_UNSET:
|
|
{
|
|
//ScreenToClient(hwndList, &p);
|
|
//int drawing_current_x = p.x + GetScrollPos(hwndList, SB_HORZ);
|
|
//int drawing_current_y = p.y + GetScrollPos(hwndList, SB_VERT) * listRowHeight;
|
|
//// draw (or erase) line from [drawing_current_x, drawing_current_y] to (drawing_last_x, drawing_last_y)
|
|
//int total_dx = drawingLastX - drawing_current_x, total_dy = drawingLastY - drawing_current_y;
|
|
//if (!shiftHeld)
|
|
//{
|
|
// // when user is not holding Shift, draw only vertical lines
|
|
// total_dx = 0;
|
|
// drawing_current_x = drawingLastX;
|
|
// p.x = drawing_current_x - GetScrollPos(hwndList, SB_HORZ);
|
|
//}
|
|
//LVHITTESTINFO info;
|
|
int row_index, column_index, joy, bit;
|
|
int min_row_index = currMovieData.getNumRecords(), max_row_index = -1;
|
|
bool changes_made = false;
|
|
if (altHeld)
|
|
{
|
|
// special mode: draw pattern
|
|
int selection_beginning = selection->getCurrentRowsSelectionBeginning();
|
|
if (selection_beginning >= 0)
|
|
{
|
|
// perform hit test
|
|
//info.pt.x = p.x;
|
|
//info.pt.y = p.y;
|
|
//ListView_SubItemHitTest(hwndList, &info);
|
|
row_index = rowUnderMouse;
|
|
//if (row_index < 0)
|
|
//{
|
|
// row_index = ListView_GetTopIndex(hwndList) + (info.pt.y - listTopMargin) / listRowHeight;
|
|
//}
|
|
// pad movie size if user tries to draw pattern below Piano Roll limit
|
|
if (row_index >= currMovieData.getNumRecords())
|
|
{
|
|
currMovieData.insertEmpty(-1, row_index + 1 - currMovieData.getNumRecords());
|
|
}
|
|
column_index = columnUnderMouse;
|
|
|
|
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;
|
|
tasWin->setInputUsingPattern(selection_beginning, row_index, joy, bit, drawingStartTimestamp);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//double total_len = sqrt((double)(total_dx * total_dx + total_dy * total_dy));
|
|
//int drawing_min_line_len = listRowHeight; // = 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;
|
|
//info.pt.y = p.y + (len / total_len) * total_dy;
|
|
//ListView_SubItemHitTest(hwndList, &info);
|
|
//row_index = info.iItem;
|
|
row_index = rowUnderMouse;
|
|
//if (row_index < 0)
|
|
// row_index = ListView_GetTopIndex(hwndList) + (info.pt.y - listTopMargin) / listRowHeight;
|
|
// pad movie size if user tries to draw below Piano Roll limit
|
|
if (row_index >= currMovieData.getNumRecords())
|
|
{
|
|
currMovieData.insertEmpty(-1, row_index + 1 - currMovieData.getNumRecords());
|
|
}
|
|
column_index = columnUnderMouse;
|
|
|
|
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 (dragMode == 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 (dragMode == 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 (dragMode == DRAG_MODE_SET)
|
|
{
|
|
greenzone->invalidateAndUpdatePlayback(history->registerChanges(MODTYPE_SET, min_row_index, max_row_index, 0, NULL, drawingStartTimestamp));
|
|
}
|
|
else
|
|
{
|
|
greenzone->invalidateAndUpdatePlayback(history->registerChanges(MODTYPE_UNSET, min_row_index, max_row_index, 0, NULL, drawingStartTimestamp));
|
|
}
|
|
}
|
|
}
|
|
//drawingLastX = drawing_current_x;
|
|
//drawingLastY = drawing_current_y;
|
|
break;
|
|
}
|
|
case DRAG_MODE_SELECTION:
|
|
{
|
|
int new_drag_selection_ending_frame = realRowUnderMouse;
|
|
// if trying to select above Piano Roll, select from frame 0
|
|
if (new_drag_selection_ending_frame < 0)
|
|
new_drag_selection_ending_frame = 0;
|
|
else if (new_drag_selection_ending_frame >= currMovieData.getNumRecords())
|
|
new_drag_selection_ending_frame = currMovieData.getNumRecords() - 1;
|
|
if (new_drag_selection_ending_frame >= 0 && new_drag_selection_ending_frame != dragSelectionEndingFrame)
|
|
{
|
|
// change Selection shape
|
|
if (new_drag_selection_ending_frame >= dragSelectionStartingFrame)
|
|
{
|
|
// selecting from upper to lower
|
|
if (dragSelectionEndingFrame < dragSelectionStartingFrame)
|
|
{
|
|
selection->clearRegionOfRowsSelection(dragSelectionEndingFrame, dragSelectionStartingFrame);
|
|
selection->setRegionOfRowsSelection(dragSelectionStartingFrame, new_drag_selection_ending_frame + 1);
|
|
} else // both ending_frame and new_ending_frame are >= starting_frame
|
|
{
|
|
if (dragSelectionEndingFrame > new_drag_selection_ending_frame)
|
|
selection->clearRegionOfRowsSelection(new_drag_selection_ending_frame + 1, dragSelectionEndingFrame + 1);
|
|
else
|
|
selection->setRegionOfRowsSelection(dragSelectionEndingFrame + 1, new_drag_selection_ending_frame + 1);
|
|
}
|
|
} else
|
|
{
|
|
// selecting from lower to upper
|
|
if (dragSelectionEndingFrame > dragSelectionStartingFrame)
|
|
{
|
|
selection->clearRegionOfRowsSelection(dragSelectionStartingFrame + 1, dragSelectionEndingFrame + 1);
|
|
selection->setRegionOfRowsSelection(new_drag_selection_ending_frame, dragSelectionStartingFrame);
|
|
} else // both ending_frame and new_ending_frame are <= starting_frame
|
|
{
|
|
if (dragSelectionEndingFrame < new_drag_selection_ending_frame)
|
|
selection->clearRegionOfRowsSelection(dragSelectionEndingFrame, new_drag_selection_ending_frame);
|
|
else
|
|
selection->setRegionOfRowsSelection(new_drag_selection_ending_frame, dragSelectionEndingFrame);
|
|
}
|
|
}
|
|
dragSelectionEndingFrame = new_drag_selection_ending_frame;
|
|
}
|
|
break;
|
|
}
|
|
case DRAG_MODE_DESELECTION:
|
|
{
|
|
int new_drag_selection_ending_frame = realRowUnderMouse;
|
|
// if trying to deselect above Piano Roll, deselect from frame 0
|
|
if (new_drag_selection_ending_frame < 0)
|
|
new_drag_selection_ending_frame = 0;
|
|
else if (new_drag_selection_ending_frame >= currMovieData.getNumRecords())
|
|
new_drag_selection_ending_frame = currMovieData.getNumRecords() - 1;
|
|
if (new_drag_selection_ending_frame >= 0 && new_drag_selection_ending_frame != dragSelectionEndingFrame)
|
|
{
|
|
// change Deselection shape
|
|
if (new_drag_selection_ending_frame >= dragSelectionStartingFrame)
|
|
// deselecting from upper to lower
|
|
selection->clearRegionOfRowsSelection(dragSelectionStartingFrame, new_drag_selection_ending_frame + 1);
|
|
else
|
|
// deselecting from lower to upper
|
|
selection->clearRegionOfRowsSelection(new_drag_selection_ending_frame, dragSelectionStartingFrame + 1);
|
|
dragSelectionEndingFrame = new_drag_selection_ending_frame;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
|
|
void QPianoRoll::startDraggingPlaybackCursor(void)
|
|
{
|
|
if (dragMode == DRAG_MODE_NONE)
|
|
{
|
|
dragMode = DRAG_MODE_PLAYBACK;
|
|
// call it once
|
|
handlePlaybackCursorDragging();
|
|
}
|
|
}
|
|
void QPianoRoll::startDraggingMarker(int mouseX, int mouseY, int rowIndex, int columnIndex)
|
|
{
|
|
if (dragMode == DRAG_MODE_NONE)
|
|
{
|
|
// start dragging the Marker
|
|
dragMode = DRAG_MODE_MARKER;
|
|
markerDragFrameNumber = rowIndex;
|
|
markerDragCountdown = MARKER_DRAG_COUNTDOWN_MAX;
|
|
//RECT temp_rect;
|
|
//if (ListView_GetSubItemRect(hwndList, rowIndex, columnIndex, LVIR_BOUNDS, &temp_rect))
|
|
//{
|
|
// markerDragBoxDX = mouseX - temp_rect.left;
|
|
// markerDragBoxDY = mouseY - temp_rect.top;
|
|
//} else
|
|
//{
|
|
// markerDragBoxDX = 0;
|
|
// markerDragBoxDY = 0;
|
|
//}
|
|
//// redraw the row to show that Marker was lifted
|
|
//redrawRow(rowIndex);
|
|
update();
|
|
}
|
|
}
|
|
void QPianoRoll::startSelectingDrag(int start_frame)
|
|
{
|
|
if (dragMode == DRAG_MODE_NONE)
|
|
{
|
|
dragMode = DRAG_MODE_SELECTION;
|
|
dragSelectionStartingFrame = start_frame;
|
|
dragSelectionEndingFrame = dragSelectionStartingFrame; // assuming that start_frame is already selected
|
|
}
|
|
}
|
|
void QPianoRoll::startDeselectingDrag(int start_frame)
|
|
{
|
|
if (dragMode == DRAG_MODE_NONE)
|
|
{
|
|
dragMode = DRAG_MODE_DESELECTION;
|
|
dragSelectionStartingFrame = start_frame;
|
|
dragSelectionEndingFrame = dragSelectionStartingFrame; // assuming that start_frame is already deselected
|
|
}
|
|
}
|
|
|
|
void QPianoRoll::handlePlaybackCursorDragging(void)
|
|
{
|
|
int target_frame = realRowUnderMouse;
|
|
if (target_frame < 0)
|
|
{
|
|
target_frame = 0;
|
|
}
|
|
if (currFrameCounter != target_frame)
|
|
{
|
|
playback->jump(target_frame);
|
|
}
|
|
}
|
|
|
|
void QPianoRoll::finishDrag(void)
|
|
{
|
|
switch (dragMode)
|
|
{
|
|
case DRAG_MODE_MARKER:
|
|
{
|
|
// place Marker here
|
|
if (markersManager->getMarkerAtFrame(markerDragFrameNumber))
|
|
{
|
|
//POINT p = {0, 0};
|
|
//GetCursorPos(&p);
|
|
//int mouse_x = p.x, mouse_y = p.y;
|
|
//ScreenToClient(hwndList, &p);
|
|
//RECT wrect;
|
|
//GetClientRect(hwndList, &wrect);
|
|
if (mouse_x < 0 || mouse_x > viewWidth || mouse_y < 0 || mouse_y > viewHeight)
|
|
{
|
|
// user threw the Marker away
|
|
markersManager->removeMarkerFromFrame(markerDragFrameNumber);
|
|
//redrawRow(markerDragFrameNumber);
|
|
history->registerMarkersChange(MODTYPE_MARKER_REMOVE, markerDragFrameNumber);
|
|
selection->mustFindCurrentMarker = playback->mustFindCurrentMarker = true;
|
|
// calculate vector of movement
|
|
//POINT p = {0, 0};
|
|
//GetCursorPos(&p);
|
|
//markerDragBoxDX = (mouse_x - markerDragBoxDX) - markerDragBoxX;
|
|
//markerDragBoxDY = (mouse_y - markerDragBoxDY) - markerDragBoxY;
|
|
//if (markerDragBoxDX || markerDragBoxDY)
|
|
//{
|
|
// // limit max speed
|
|
// double marker_drag_box_speed = sqrt((double)(markerDragBoxDX * markerDragBoxDX + markerDragBoxDY * markerDragBoxDY));
|
|
// if (marker_drag_box_speed > MARKER_DRAG_MAX_SPEED)
|
|
// {
|
|
// markerDragBoxDX *= MARKER_DRAG_MAX_SPEED / marker_drag_box_speed;
|
|
// markerDragBoxDY *= MARKER_DRAG_MAX_SPEED / marker_drag_box_speed;
|
|
// }
|
|
//}
|
|
//markerDragCountdown = MARKER_DRAG_COUNTDOWN_MAX;
|
|
}
|
|
else
|
|
{
|
|
if (rowUnderMouse >= 0 && (columnUnderMouse <= COLUMN_FRAMENUM || columnUnderMouse >= COLUMN_FRAMENUM2))
|
|
{
|
|
if (rowUnderMouse == markerDragFrameNumber)
|
|
{
|
|
// it was just double-click and release
|
|
// if Selection points at dragged Marker, set focus to lower Note edit field
|
|
int dragged_marker_id = markersManager->getMarkerAtFrame(markerDragFrameNumber);
|
|
int selection_marker_id = markersManager->getMarkerAboveFrame(selection->getCurrentRowsSelectionBeginning());
|
|
if (dragged_marker_id == selection_marker_id)
|
|
{
|
|
//SetFocus(selection.hwndSelectionMarkerEditField);
|
|
// select all text in case user wants to overwrite it
|
|
//SendMessage(selection.hwndSelectionMarkerEditField, EM_SETSEL, 0, -1);
|
|
}
|
|
}
|
|
else if (markersManager->getMarkerAtFrame(rowUnderMouse))
|
|
{
|
|
int dragged_marker_id = markersManager->getMarkerAtFrame(markerDragFrameNumber);
|
|
int destination_marker_id = markersManager->getMarkerAtFrame(rowUnderMouse);
|
|
// swap Notes of these Markers
|
|
char dragged_marker_note[MAX_NOTE_LEN];
|
|
strcpy(dragged_marker_note, markersManager->getNoteCopy(dragged_marker_id).c_str());
|
|
if (strcmp(markersManager->getNoteCopy(destination_marker_id).c_str(), dragged_marker_note))
|
|
{
|
|
// notes are different, swap them
|
|
markersManager->setNote(dragged_marker_id, markersManager->getNoteCopy(destination_marker_id).c_str());
|
|
markersManager->setNote(destination_marker_id, dragged_marker_note);
|
|
history->registerMarkersChange(MODTYPE_MARKER_SWAP, markerDragFrameNumber, rowUnderMouse);
|
|
selection->mustFindCurrentMarker = playback->mustFindCurrentMarker = true;
|
|
//setLightInHeaderColumn(COLUMN_FRAMENUM, HEADER_LIGHT_MAX);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// move Marker
|
|
int new_marker_id = markersManager->setMarkerAtFrame(rowUnderMouse);
|
|
if (new_marker_id)
|
|
{
|
|
markersManager->setNote(new_marker_id, markersManager->getNoteCopy(markersManager->getMarkerAtFrame(markerDragFrameNumber)).c_str());
|
|
// and delete it from old frame
|
|
markersManager->removeMarkerFromFrame(markerDragFrameNumber);
|
|
history->registerMarkersChange(MODTYPE_MARKER_DRAG, markerDragFrameNumber, rowUnderMouse, markersManager->getNoteCopy(markersManager->getMarkerAtFrame(rowUnderMouse)).c_str());
|
|
selection->mustFindCurrentMarker = playback->mustFindCurrentMarker = true;
|
|
//setLightInHeaderColumn(COLUMN_FRAMENUM, HEADER_LIGHT_MAX);
|
|
//redrawRow(rowUnderMouse);
|
|
}
|
|
}
|
|
}
|
|
//redrawRow(markerDragFrameNumber);
|
|
//if (hwndMarkerDragBox)
|
|
//{
|
|
// DestroyWindow(hwndMarkerDragBox);
|
|
// hwndMarkerDragBox = 0;
|
|
//}
|
|
}
|
|
} else
|
|
{
|
|
// abort drag
|
|
//if (hwndMarkerDragBox)
|
|
//{
|
|
// DestroyWindow(hwndMarkerDragBox);
|
|
// hwndMarkerDragBox = 0;
|
|
//}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
dragMode = DRAG_MODE_NONE;
|
|
//mustCheckItemUnderMouse = true;
|
|
}
|
|
//----------------------------------------------------------------------------
|
|
void QPianoRoll::paintEvent(QPaintEvent *event)
|
|
{
|
|
int x, y, row, nrow, lineNum;
|
|
QPainter painter(this);
|
|
QColor white("white"), black("black"), blkColor;
|
|
static const char *buttonNames[] = { "A", "B", "S", "T", "U", "D", "L", "R", NULL };
|
|
char stmp[32];
|
|
|
|
painter.setFont(font);
|
|
viewWidth = event->rect().width();
|
|
viewHeight = event->rect().height();
|
|
|
|
nrow = (viewHeight / pxLineSpacing) + 1;
|
|
|
|
if ( nrow < 1 ) nrow = 1;
|
|
|
|
viewLines = nrow;
|
|
|
|
maxLineOffset = currMovieData.records.size() - nrow + 1;
|
|
|
|
if ( maxLineOffset < 0 )
|
|
{
|
|
maxLineOffset = 0;
|
|
}
|
|
|
|
if ( lineOffset < 0 )
|
|
{
|
|
lineOffset = 0;
|
|
}
|
|
if ( lineOffset > maxLineOffset )
|
|
{
|
|
lineOffset = maxLineOffset;
|
|
}
|
|
vbar->setMinimum(0);
|
|
vbar->setMaximum(maxLineOffset);
|
|
|
|
painter.fillRect( 0, 0, viewWidth, viewHeight, this->palette().color(QPalette::Window) );
|
|
|
|
// Draw Title Bar
|
|
x = -pxLineXScroll; y = 0;
|
|
painter.fillRect( 0, 0, viewWidth, pxLineSpacing, windowColor );
|
|
painter.setPen( black );
|
|
|
|
x = -pxLineXScroll + pxFrameColX + (pxWidthFrameCol - 6*pxCharWidth) / 2;
|
|
painter.drawText( x, pxLineTextOfs, tr("Frame#") );
|
|
|
|
// Draw Grid
|
|
painter.drawLine( -pxLineXScroll, 0, -pxLineXScroll, viewHeight );
|
|
|
|
x = pxFrameColX - pxLineXScroll;
|
|
painter.drawLine( x, 0, x, viewHeight );
|
|
|
|
for (int i=0; i<numCtlr; i++)
|
|
{
|
|
x = pxFrameCtlX[i] - pxLineXScroll;
|
|
|
|
if ( i % 2 )
|
|
{
|
|
painter.fillRect( x, pxLineSpacing, pxWidthCtlCol, viewHeight, this->palette().color(QPalette::AlternateBase) );
|
|
}
|
|
}
|
|
|
|
y = pxLineSpacing;
|
|
|
|
for (row=0; row<nrow; row++)
|
|
{
|
|
uint8_t data;
|
|
|
|
lineNum = lineOffset + row;
|
|
|
|
if ( lineNum >= currMovieData.records.size() )
|
|
{
|
|
break;
|
|
}
|
|
int frame_lag = greenzone->lagLog.getLagInfoAtFrame(lineNum);
|
|
|
|
for (int i=0; i<numCtlr; i++)
|
|
{
|
|
x = pxFrameCtlX[i] - pxLineXScroll;
|
|
|
|
if ( lineNum == history->getUndoHint())
|
|
{
|
|
// undo hint here
|
|
blkColor = (i%2) ? QColor(UNDOHINT_INPUT_COLOR2) : QColor(UNDOHINT_INPUT_COLOR1);
|
|
}
|
|
else if ( lineNum == currFrameCounter || lineNum == (playback->getFlashingPauseFrame() - 1))
|
|
{
|
|
// this is current frame
|
|
blkColor = (i%2) ? QColor(CUR_INPUT_COLOR2) : QColor(CUR_INPUT_COLOR1);
|
|
}
|
|
else if ( lineNum < greenzone->getSize() )
|
|
{
|
|
if (!greenzone->isSavestateEmpty(lineNum))
|
|
{
|
|
// the frame is normal Greenzone frame
|
|
if (frame_lag == LAGGED_YES)
|
|
{
|
|
blkColor = (i%2) ? QColor(LAG_INPUT_COLOR2) : QColor(LAG_INPUT_COLOR1);
|
|
}
|
|
else
|
|
{
|
|
blkColor = (i%2) ? QColor(GREENZONE_INPUT_COLOR2) : QColor(GREENZONE_INPUT_COLOR1);
|
|
}
|
|
}
|
|
else if ( !greenzone->isSavestateEmpty(lineNum & EVERY16TH)
|
|
|| !greenzone->isSavestateEmpty(lineNum & EVERY8TH)
|
|
|| !greenzone->isSavestateEmpty(lineNum & EVERY4TH)
|
|
|| !greenzone->isSavestateEmpty(lineNum & EVERY2ND))
|
|
{
|
|
// the frame is in a gap (in Greenzone tail)
|
|
if (frame_lag == LAGGED_YES)
|
|
{
|
|
blkColor = (i%2) ? QColor(PALE_LAG_INPUT_COLOR2) : QColor(PALE_LAG_INPUT_COLOR1);
|
|
}
|
|
else
|
|
{
|
|
blkColor = (i%2) ? QColor(PALE_GREENZONE_INPUT_COLOR2) : QColor(PALE_GREENZONE_INPUT_COLOR1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// the frame is above Greenzone tail
|
|
if (frame_lag == LAGGED_YES)
|
|
{
|
|
blkColor = (i%2) ? QColor(VERY_PALE_LAG_INPUT_COLOR2) : QColor(VERY_PALE_LAG_INPUT_COLOR1);
|
|
}
|
|
else if (frame_lag == LAGGED_NO)
|
|
{
|
|
blkColor = (i%2) ? QColor(VERY_PALE_GREENZONE_INPUT_COLOR2) : QColor(VERY_PALE_GREENZONE_INPUT_COLOR1);
|
|
}
|
|
else
|
|
{
|
|
blkColor = (i%2) ? QColor(NORMAL_INPUT_COLOR2) : QColor(NORMAL_INPUT_COLOR1);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// the frame is below Greenzone head
|
|
if (frame_lag == LAGGED_YES)
|
|
{
|
|
blkColor = (i%2) ? QColor(VERY_PALE_LAG_INPUT_COLOR2) : QColor(VERY_PALE_LAG_INPUT_COLOR1);
|
|
}
|
|
else if (frame_lag == LAGGED_NO)
|
|
{
|
|
blkColor = (i%2) ? QColor(VERY_PALE_GREENZONE_INPUT_COLOR2) : QColor(VERY_PALE_GREENZONE_INPUT_COLOR1);
|
|
}
|
|
else
|
|
{
|
|
blkColor = (i%2) ? QColor(NORMAL_INPUT_COLOR2) : QColor(NORMAL_INPUT_COLOR1);
|
|
}
|
|
}
|
|
painter.fillRect( x, y, pxWidthCtlCol, pxLineSpacing, blkColor );
|
|
}
|
|
|
|
for (int i=0; i<numCtlr; i++)
|
|
{
|
|
data = currMovieData.records[ lineNum ].joysticks[i];
|
|
|
|
x = pxFrameCtlX[i] - pxLineXScroll;
|
|
|
|
for (int j=0; j<8; j++)
|
|
{
|
|
if ( data & (0x01 << j) )
|
|
{
|
|
painter.drawText( x + pxCharWidth, y+pxLineTextOfs, tr(buttonNames[j]) );
|
|
}
|
|
x += pxWidthBtnCol;
|
|
}
|
|
painter.drawLine( x, 0, x, viewHeight );
|
|
}
|
|
|
|
x = -pxLineXScroll + pxFrameColX + (pxWidthFrameCol - 10*pxCharWidth) / 2;
|
|
|
|
sprintf( stmp, "%010i", lineNum );
|
|
|
|
painter.drawText( x, y+pxLineTextOfs, tr(stmp) );
|
|
|
|
x = -pxLineXScroll;
|
|
|
|
int iImage = bookmarks->findBookmarkAtFrame(lineNum);
|
|
if (iImage < 0)
|
|
{
|
|
// no bookmark at this frame
|
|
if (lineNum == playback->getLastPosition())
|
|
{
|
|
if (lineNum == currFrameCounter)
|
|
{
|
|
iImage = GREEN_BLUE_ARROW_IMAGE_ID;
|
|
}
|
|
else
|
|
{
|
|
iImage = GREEN_ARROW_IMAGE_ID;
|
|
}
|
|
}
|
|
else if (lineNum == currFrameCounter)
|
|
{
|
|
iImage = BLUE_ARROW_IMAGE_ID;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// bookmark at this frame
|
|
if (lineNum == playback->getLastPosition())
|
|
{
|
|
iImage += BOOKMARKS_WITH_GREEN_ARROW;
|
|
}
|
|
else if (lineNum == currFrameCounter)
|
|
{
|
|
iImage += BOOKMARKS_WITH_BLUE_ARROW;
|
|
}
|
|
}
|
|
|
|
if ( iImage >= 0 )
|
|
{
|
|
drawArrow( &painter, x, y, iImage );
|
|
}
|
|
|
|
y += pxLineSpacing;
|
|
}
|
|
|
|
for (int i=0; i<numCtlr; i++)
|
|
{
|
|
x = pxFrameCtlX[i] - pxLineXScroll;
|
|
|
|
for (int j=0; j<8; j++)
|
|
{
|
|
painter.drawLine( x, 0, x, viewHeight );
|
|
|
|
painter.drawText( x + pxCharWidth, pxLineTextOfs, tr(buttonNames[j]) );
|
|
|
|
x += pxWidthBtnCol;
|
|
}
|
|
painter.drawLine( x, 0, x, viewHeight );
|
|
}
|
|
y = 0;
|
|
for (int i=0; i<nrow; i++)
|
|
{
|
|
painter.drawLine( 0, y, viewWidth, y );
|
|
|
|
y += pxLineSpacing;
|
|
}
|
|
|
|
}
|
|
//----------------------------------------------------------------------------
|