diff --git a/ogl_editor/src/Commands.c b/ogl_editor/src/Commands.c new file mode 100644 index 0000000..cb2c000 --- /dev/null +++ b/ogl_editor/src/Commands.c @@ -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); +} + + diff --git a/ogl_editor/src/Commands.h b/ogl_editor/src/Commands.h new file mode 100644 index 0000000..23e1d5e --- /dev/null +++ b/ogl_editor/src/Commands.h @@ -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 + diff --git a/ogl_editor/src/Editor.c b/ogl_editor/src/Editor.c index e0a406d..c0f2b08 100644 --- a/ogl_editor/src/Editor.c +++ b/ogl_editor/src/Editor.c @@ -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); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/ogl_editor/src/TrackData.c b/ogl_editor/src/TrackData.c index 79ad288..27a9e88 100644 --- a/ogl_editor/src/TrackData.c +++ b/ogl_editor/src/TrackData.c @@ -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; }