Fully implemented undo for: add/delete/edit of keys
Also takes into account if more than one key is being modified at once and such being handled as a collection of commands in a multi-command so it looks hand behaves correct from the users perspective. Closes #6
This commit is contained in:
parent
f4c315c137
commit
49af1595f0
429
ogl_editor/src/Commands.c
Normal file
429
ogl_editor/src/Commands.c
Normal file
@ -0,0 +1,429 @@
|
||||
#include "Commands.h"
|
||||
#include "RemoteConnection.h"
|
||||
#include "Types.h"
|
||||
#include "../../sync/sync.h"
|
||||
#include "../../sync/track.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;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Commands_init(struct sync_track** syncTracks, struct TrackData* trackData)
|
||||
{
|
||||
s_syncTracks = syncTracks;
|
||||
s_trackData = trackData;
|
||||
|
||||
memset(&s_undoStack, 0, sizeof(CommandList));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Commands_beginMulti()
|
||||
{
|
||||
s_multiCommand = malloc(sizeof(struct MultiCommandData));
|
||||
memset(s_multiCommand, 0, sizeof(struct MultiCommandData));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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_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);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
29
ogl_editor/src/Commands.h
Normal file
29
ogl_editor/src/Commands.h
Normal file
@ -0,0 +1,29 @@
|
||||
#ifndef _OGLEDITOR_COMMANDS_H_
|
||||
#define _OGLEDITOR_COMMANDS_H_
|
||||
|
||||
struct sync_track;
|
||||
struct track_key;
|
||||
struct TrackData;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Commands_init(struct sync_track** syncTracks, struct TrackData* trackData);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int Commands_needsSave();
|
||||
|
||||
void Commands_undo();
|
||||
void Commands_redo();
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Commands_deleteKey(int track, int row);
|
||||
void Commands_addOrUpdateKey(int track, struct track_key* key);
|
||||
void Commands_toogleBookmark(int track, int row);
|
||||
void Commands_updateKey(int track, struct track_key* key);
|
||||
void Commands_beginMulti(); // Used (for example) when changing many value at the same time
|
||||
void Commands_endMulti();
|
||||
|
||||
#endif
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#include "minmax.h"
|
||||
#include "TrackData.h"
|
||||
#include "RemoteConnection.h"
|
||||
#include "Commands.h"
|
||||
#include "MinecraftiaFont.h"
|
||||
#include "../../sync/sync.h"
|
||||
#include "../../sync/base.h"
|
||||
@ -488,6 +489,8 @@ static void deleteArea(int rowPos, int track, int bufferWidth, int bufferHeight)
|
||||
const int track_count = getTrackCount();
|
||||
struct sync_track** tracks = getTracks();
|
||||
|
||||
Commands_beginMulti();
|
||||
|
||||
for (i = 0; i < bufferWidth; ++i)
|
||||
{
|
||||
struct sync_track* t;
|
||||
@ -503,13 +506,11 @@ static void deleteArea(int rowPos, int track, int bufferWidth, int bufferHeight)
|
||||
{
|
||||
int row = rowPos + j;
|
||||
|
||||
if (is_key_frame(t, row))
|
||||
{
|
||||
sync_del_key(t, row);
|
||||
RemoteConnection_sendDeleteKeyCommand(t->name, row);
|
||||
}
|
||||
Commands_deleteKey(trackIndex, row);
|
||||
}
|
||||
}
|
||||
|
||||
Commands_endMulti();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -519,6 +520,8 @@ static void biasSelection(float value, int selectLeft, int selectRight, int sele
|
||||
int track, row;
|
||||
struct sync_track** tracks = getTracks();
|
||||
|
||||
Commands_beginMulti();
|
||||
|
||||
for (track = selectLeft; track <= selectRight; ++track)
|
||||
{
|
||||
struct sync_track* t = tracks[track];
|
||||
@ -533,11 +536,11 @@ static void biasSelection(float value, int selectLeft, int selectRight, int sele
|
||||
newKey = t->keys[idx];
|
||||
newKey.value += value;
|
||||
|
||||
sync_set_key(t, &newKey);
|
||||
|
||||
RemoteConnection_sendSetKeyCommand(t->name, &newKey);
|
||||
Commands_updateKey(track, &newKey);
|
||||
}
|
||||
}
|
||||
|
||||
Commands_endMulti();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -572,11 +575,15 @@ static void endEditing()
|
||||
|
||||
track_name = track->name;
|
||||
|
||||
Commands_addOrUpdateKey(active_track, &key);
|
||||
|
||||
/*
|
||||
sync_set_key(track, &key);
|
||||
|
||||
rlog(R_INFO, "Setting key %f at %d row %d (name %s)\n", key.value, active_track, key.row, track_name);
|
||||
|
||||
RemoteConnection_sendSetKeyCommand(track_name, &key);
|
||||
*/
|
||||
|
||||
is_editing = false;
|
||||
s_editorData.trackData.editText = 0;
|
||||
@ -876,6 +883,16 @@ bool Editor_keyDown(int key, int keyCode, int modifiers)
|
||||
handled_key = true;
|
||||
}
|
||||
|
||||
if (key == 'z' || key == 'Z')
|
||||
{
|
||||
if (modifiers & EMGUI_KEY_SHIFT)
|
||||
Commands_redo();
|
||||
else
|
||||
Commands_undo();
|
||||
|
||||
handled_key = true;
|
||||
}
|
||||
|
||||
// Handle paste of data
|
||||
|
||||
if (key == 'v' && (modifiers & EMGUI_KEY_COMMAND))
|
||||
@ -1204,17 +1221,23 @@ static void setWindowTitle(const char* path)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void onFinishedLoad(const char* path)
|
||||
{
|
||||
Editor_update();
|
||||
setWindowTitle(path);
|
||||
setMostRecentFile(path);
|
||||
//Commands_init(getTracks(), getTrackData());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Editor_loadRecentFile(int id)
|
||||
{
|
||||
char path[2048];
|
||||
strcpy(path, s_recentFiles[id]); // must be unique buffer when doing set mostRecent
|
||||
|
||||
if (LoadSave_loadRocketXML(path, getTrackData()))
|
||||
{
|
||||
Editor_update();
|
||||
setWindowTitle(path);
|
||||
setMostRecentFile(path);
|
||||
}
|
||||
onFinishedLoad(path);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -1224,11 +1247,7 @@ static void onOpen()
|
||||
char currentFile[2048];
|
||||
|
||||
if (LoadSave_loadRocketXMLDialog(currentFile, getTrackData()))
|
||||
{
|
||||
Editor_update();
|
||||
setWindowTitle(currentFile);
|
||||
setMostRecentFile(currentFile);
|
||||
}
|
||||
onFinishedLoad(currentFile);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#include "TrackData.h"
|
||||
#include "Commands.h"
|
||||
#include "rlog.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -14,6 +15,9 @@ int TrackData_createGetTrack(TrackData* trackData, const char* name)
|
||||
trackData->tracks[index].color = TrackData_getNextColor(trackData);
|
||||
}
|
||||
|
||||
if (trackData->syncData.tracks)
|
||||
Commands_init(trackData->syncData.tracks, trackData);
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user