578 lines
15 KiB
C
578 lines
15 KiB
C
#include "Commands.h"
|
|
#include "RemoteConnection.h"
|
|
#include "TrackData.h"
|
|
#include "../../sync/sync.h"
|
|
#include "../../sync/track.h"
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <emgui/Types.h>
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static struct sync_track** s_syncTracks;
|
|
static struct TrackData* s_trackData;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
typedef struct Command
|
|
{
|
|
void* userData;
|
|
void (*exec)(void* userData);
|
|
void (*undo)(void* userData);
|
|
struct Command* next;
|
|
struct Command* prev;
|
|
} Command;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
typedef struct CommandList
|
|
{
|
|
Command* first;
|
|
Command* last;
|
|
} CommandList;
|
|
|
|
static CommandList s_undoStack;
|
|
static CommandList s_redoStack;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void CommandList_addEntry(CommandList* commandList, Command* command);
|
|
//static void CommandList_delEntry(CommandList* commandList, Command* command);
|
|
static void CommandList_clear(CommandList* commandList);
|
|
static bool CommandList_isEmpty(CommandList* list);
|
|
static void CommandList_pop(CommandList* commandList);
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
struct MultiCommandData
|
|
{
|
|
CommandList list;
|
|
};
|
|
|
|
static struct MultiCommandData* s_multiCommand = 0;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Commands_init(struct sync_track** syncTracks, struct TrackData* trackData)
|
|
{
|
|
s_syncTracks = syncTracks;
|
|
s_trackData = trackData;
|
|
|
|
memset(&s_undoStack, 0, sizeof(CommandList));
|
|
memset(&s_redoStack, 0, sizeof(CommandList));
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static int countEntriesInList(CommandList* list)
|
|
{
|
|
Command* command;
|
|
int count = 0;
|
|
|
|
for (command = list->first; command; command = command->next)
|
|
count++;
|
|
|
|
return count;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
int Commands_undoCount()
|
|
{
|
|
return countEntriesInList(&s_undoStack);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void execCommand(Command* command)
|
|
{
|
|
// set if we have multi command recording enabled)
|
|
if (s_multiCommand)
|
|
{
|
|
CommandList_addEntry(&s_multiCommand->list, command);
|
|
}
|
|
else
|
|
{
|
|
CommandList_addEntry(&s_undoStack, command);
|
|
command->exec(command->userData);
|
|
}
|
|
|
|
CommandList_clear(&s_redoStack);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void execMultiCommand(void* userData)
|
|
{
|
|
Command* command;
|
|
struct MultiCommandData* data = (struct MultiCommandData*)userData;
|
|
|
|
for (command = data->list.first; command; command = command->next)
|
|
command->exec(command->userData);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void undoMultiCommand(void* userData)
|
|
{
|
|
Command* command;
|
|
struct MultiCommandData* data = (struct MultiCommandData*)userData;
|
|
|
|
for (command = data->list.first; command; command = command->next)
|
|
command->undo(command->userData);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Commands_beginMulti(const char* name)
|
|
{
|
|
s_multiCommand = malloc(sizeof(struct MultiCommandData));
|
|
memset(s_multiCommand, 0, sizeof(struct MultiCommandData));
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Commands_endMulti()
|
|
{
|
|
Command* command;
|
|
|
|
// Check if any command was added during multi command
|
|
|
|
if (CommandList_isEmpty(&s_multiCommand->list))
|
|
{
|
|
free(s_multiCommand);
|
|
s_multiCommand = 0;
|
|
return;
|
|
}
|
|
|
|
command = malloc(sizeof(Command));
|
|
memset(command, 0, sizeof(Command));
|
|
|
|
command->userData = s_multiCommand;
|
|
command->exec = execMultiCommand;
|
|
command->undo = undoMultiCommand;
|
|
|
|
s_multiCommand = 0;
|
|
|
|
execCommand(command);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
struct DeleteKeyData
|
|
{
|
|
int track;
|
|
int row;
|
|
struct track_key oldKey;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void execDeleteKey(void* userData)
|
|
{
|
|
struct DeleteKeyData* data = (struct DeleteKeyData*)userData;
|
|
struct sync_track* t = s_syncTracks[data->track];
|
|
int idx = sync_find_key(t, data->row);
|
|
|
|
data->oldKey = t->keys[idx];
|
|
sync_del_key(t, data->row);
|
|
|
|
RemoteConnection_sendDeleteKeyCommand(t->name, data->row);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void undoDeleteKey(void* userData)
|
|
{
|
|
struct DeleteKeyData* data = (struct DeleteKeyData*)userData;
|
|
struct sync_track* t = s_syncTracks[data->track];
|
|
sync_set_key(t, &data->oldKey);
|
|
|
|
RemoteConnection_sendSetKeyCommand(t->name, &data->oldKey);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Commands_deleteKey(int track, int row)
|
|
{
|
|
struct DeleteKeyData* data;
|
|
Command* command;
|
|
struct sync_track* t = s_syncTracks[track];
|
|
|
|
if (!is_key_frame(t, row))
|
|
return;
|
|
|
|
command = malloc(sizeof(Command));
|
|
memset(command, 0, sizeof(Command));
|
|
|
|
command->userData = data = malloc(sizeof(struct DeleteKeyData));
|
|
command->exec = execDeleteKey;
|
|
command->undo = undoDeleteKey;
|
|
data->track = track;
|
|
data->row = row;
|
|
|
|
execCommand(command);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
struct UpdateKeyData
|
|
{
|
|
int track;
|
|
struct track_key key;
|
|
struct track_key oldKey;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void execUpdateKey(void* userData)
|
|
{
|
|
struct UpdateKeyData* data = (struct UpdateKeyData*)userData;
|
|
struct sync_track* t = s_syncTracks[data->track];
|
|
int idx = sync_find_key(t, data->key.row);
|
|
|
|
data->oldKey = t->keys[idx];
|
|
sync_set_key(t, &data->key);
|
|
|
|
RemoteConnection_sendSetKeyCommand(t->name, &data->key);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void undoUpdateKey(void* userData)
|
|
{
|
|
struct UpdateKeyData* data = (struct UpdateKeyData*)userData;
|
|
struct sync_track* t = s_syncTracks[data->track];
|
|
sync_set_key(t, &data->oldKey);
|
|
|
|
RemoteConnection_sendSetKeyCommand(t->name, &data->oldKey);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Commands_updateKey(int track, struct track_key* key)
|
|
{
|
|
struct UpdateKeyData* data;
|
|
Command* command;
|
|
|
|
command = malloc(sizeof(Command));
|
|
memset(command, 0, sizeof(Command));
|
|
|
|
command->userData = data = malloc(sizeof(struct UpdateKeyData));
|
|
command->exec = execUpdateKey;
|
|
command->undo = undoUpdateKey;
|
|
data->track = track;
|
|
data->key = *key;
|
|
|
|
execCommand(command);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
struct InsertKeyData
|
|
{
|
|
int track;
|
|
struct track_key key;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void execInsertKey(void* userData)
|
|
{
|
|
struct InsertKeyData* data = (struct InsertKeyData*)userData;
|
|
struct sync_track* t = s_syncTracks[data->track];
|
|
sync_set_key(t, &data->key);
|
|
|
|
RemoteConnection_sendSetKeyCommand(t->name, &data->key);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void undoInsertKey(void* userData)
|
|
{
|
|
struct InsertKeyData* data = (struct InsertKeyData*)userData;
|
|
struct sync_track* t = s_syncTracks[data->track];
|
|
sync_del_key(t, data->key.row);
|
|
|
|
RemoteConnection_sendDeleteKeyCommand(t->name, data->key.row);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Commands_addOrUpdateKey(int track, struct track_key* key)
|
|
{
|
|
struct InsertKeyData* data;
|
|
Command* command;
|
|
struct sync_track* t = s_syncTracks[track];
|
|
|
|
if (is_key_frame(t, key->row))
|
|
{
|
|
Commands_updateKey(track, key);
|
|
return;
|
|
}
|
|
|
|
command = malloc(sizeof(Command));
|
|
memset(command, 0, sizeof(Command));
|
|
|
|
command->userData = data = malloc(sizeof(struct InsertKeyData));
|
|
command->exec = execInsertKey;
|
|
command->undo = undoInsertKey;
|
|
data->track = track;
|
|
data->key = *key;
|
|
|
|
execCommand(command);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
struct BookmarkData
|
|
{
|
|
struct TrackData* trackData;
|
|
int row;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void toggleBookmark(void* userData)
|
|
{
|
|
struct BookmarkData* data = (struct BookmarkData*)userData;
|
|
TrackData_toggleBookmark(data->trackData, data->row);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Commands_toggleBookmark(TrackData* trackData, int row)
|
|
{
|
|
struct BookmarkData* data;
|
|
Command* command;
|
|
|
|
command = malloc(sizeof(Command));
|
|
memset(command, 0, sizeof(Command));
|
|
|
|
command->userData = data = malloc(sizeof(struct BookmarkData));
|
|
command->exec = toggleBookmark;
|
|
command->undo = toggleBookmark;
|
|
data->trackData = trackData;
|
|
data->row = row;
|
|
|
|
execCommand(command);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Commands_clearBookmarks(TrackData* trackData)
|
|
{
|
|
int i, bookmarkCount = trackData->bookmarkCount;
|
|
int* bookmarks = trackData->bookmarks;
|
|
|
|
if (trackData->bookmarkCount == 0)
|
|
return;
|
|
|
|
Commands_beginMulti("clearBookmarks");
|
|
|
|
for (i = 0; i < bookmarkCount; ++i)
|
|
{
|
|
const int bookmark = *bookmarks++;
|
|
|
|
if (bookmark == 0)
|
|
continue;
|
|
|
|
Commands_toggleBookmark(trackData, bookmark);
|
|
}
|
|
|
|
Commands_endMulti();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
struct LoopmarkData
|
|
{
|
|
struct TrackData* trackData;
|
|
int row;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void toggleLoopmark(void* userData)
|
|
{
|
|
struct LoopmarkData* data = (struct LoopmarkData*)userData;
|
|
TrackData_toggleLoopmark(data->trackData, data->row);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Commands_toggleLoopmark(TrackData* trackData, int row)
|
|
{
|
|
struct LoopmarkData* data;
|
|
Command* command;
|
|
|
|
command = malloc(sizeof(Command));
|
|
memset(command, 0, sizeof(Command));
|
|
|
|
command->userData = data = malloc(sizeof(struct LoopmarkData));
|
|
command->exec = toggleLoopmark;
|
|
command->undo = toggleLoopmark;
|
|
data->trackData = trackData;
|
|
data->row = row;
|
|
|
|
execCommand(command);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Commands_clearLoopmarks(TrackData* trackData)
|
|
{
|
|
int i, loopmarkCount = trackData->loopmarkCount;
|
|
int* loopmarks = trackData->loopmarks;
|
|
|
|
if (trackData->loopmarkCount == 0)
|
|
return;
|
|
|
|
Commands_beginMulti("clearLoopmarks");
|
|
|
|
for (i = 0; i < loopmarkCount; ++i)
|
|
{
|
|
const int loopmark = *loopmarks++;
|
|
|
|
if (loopmark == 0)
|
|
continue;
|
|
|
|
Commands_toggleLoopmark(trackData, loopmark);
|
|
}
|
|
|
|
Commands_endMulti();
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Commands_undo()
|
|
{
|
|
Command* command;
|
|
|
|
if (CommandList_isEmpty(&s_undoStack))
|
|
{
|
|
return;
|
|
}
|
|
|
|
command = s_undoStack.last;
|
|
CommandList_pop(&s_undoStack);
|
|
|
|
command->prev = 0;
|
|
command->next = 0;
|
|
|
|
CommandList_addEntry(&s_redoStack, command);
|
|
|
|
command->undo(command->userData);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Commands_redo()
|
|
{
|
|
Command* command;
|
|
|
|
if (CommandList_isEmpty(&s_redoStack))
|
|
return;
|
|
|
|
command = s_redoStack.last;
|
|
CommandList_pop(&s_redoStack);
|
|
|
|
command->prev = 0;
|
|
command->next = 0;
|
|
|
|
CommandList_addEntry(&s_undoStack, command);
|
|
|
|
command->exec(command->userData);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void CommandList_addEntry(CommandList* list, Command* command)
|
|
{
|
|
if (list->last)
|
|
{
|
|
list->last->next = command;
|
|
command->prev = list->last;
|
|
list->last = command;
|
|
}
|
|
else
|
|
{
|
|
list->first = command;
|
|
list->last = command;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void CommandList_unlinkEntry(CommandList* list, Command* command)
|
|
{
|
|
Command* prev;
|
|
Command* next;
|
|
|
|
prev = command->prev;
|
|
next = command->next;
|
|
|
|
if (prev)
|
|
{
|
|
if (next)
|
|
{
|
|
prev->next = next;
|
|
next->prev = prev;
|
|
}
|
|
else
|
|
{
|
|
prev->next = 0;
|
|
list->last = prev;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (next)
|
|
{
|
|
next->prev = 0;
|
|
list->first = next;
|
|
}
|
|
else
|
|
{
|
|
list->first = 0;
|
|
list->last = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void CommandList_delEntry(CommandList* list, Command* command)
|
|
{
|
|
CommandList_unlinkEntry(list, command);
|
|
|
|
free(command->userData);
|
|
free(command);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void CommandList_clear(CommandList* list)
|
|
{
|
|
while (list->last)
|
|
{
|
|
CommandList_delEntry(list, list->last);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static void CommandList_pop(CommandList* list)
|
|
{
|
|
CommandList_unlinkEntry(list, list->last);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static bool CommandList_isEmpty(CommandList* list)
|
|
{
|
|
return (!list->first && !list->last);
|
|
}
|
|
|
|
|