From 8f9877f79ba95b504a060a1ca1bc1e662ed379ac Mon Sep 17 00:00:00 2001 From: Erik Faye-Lund Date: Thu, 17 Jan 2008 22:54:45 +0000 Subject: [PATCH] edit with undo! --- syncdata.h | 21 +++++ synctracker2.cpp | 148 ++++++++++++++++++++++++++++++++- synctracker2.vcproj | 2 + trackview.cpp | 230 ++++++++++++++++++++++++++++++---------------------- trackview.h | 145 ++++++++++++++++++++++++++++----- 5 files changed, 425 insertions(+), 121 deletions(-) diff --git a/syncdata.h b/syncdata.h index c500739..a84f830 100644 --- a/syncdata.h +++ b/syncdata.h @@ -51,11 +51,22 @@ public: return &iter->second; } + void deleteKeyFrame(int row) + { + keyFrames.erase(row); + } + void setKeyFrame(int row, const KeyFrame &keyFrame) { keyFrames[row] = keyFrame; } + void setKeyFrame(int row, const float value) + { + keyFrames[row] = KeyFrame(value); + } + + private: typedef std::map KeyFrameContainer; KeyFrameContainer keyFrames; @@ -71,6 +82,16 @@ public: return tracks[name] = SyncTrack(); } + SyncTrack &getTrack(size_t track) + { + assert(track >= 0); + assert(track < tracks.size()); + + SyncData::TrackContainer::iterator trackIter = tracks.begin(); + for (size_t currTrack = 0; currTrack < track; ++currTrack, ++trackIter); + return trackIter->second; + } + size_t getTrackCount() { return tracks.size(); } // private: diff --git a/synctracker2.cpp b/synctracker2.cpp index 4605042..79d6e31 100644 --- a/synctracker2.cpp +++ b/synctracker2.cpp @@ -3,6 +3,7 @@ #include "stdafx.h" #include +#include #include "trackview.h" @@ -10,27 +11,86 @@ const TCHAR *mainWindowClassName = _T("MainWindow"); TrackView *trackView; HWND trackViewWin; +HWND statusBarWin; static LRESULT CALLBACK mainWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_CREATE: + { trackViewWin = trackView->create(GetModuleHandle(NULL), hwnd); + + InitCommonControls(); + statusBarWin = CreateWindowEx( + 0, // no extended styles + STATUSCLASSNAME, // status bar + (LPCTSTR)"mordi", // no text + SBARS_SIZEGRIP | WS_VISIBLE | WS_CHILD, // styles + 0, 0, 0, 0, // x, y, cx, cy + hwnd, // parent window + (HMENU)100, // window ID + GetModuleHandle(NULL), // instance + NULL // window data + ); + + int statwidths[] = {100, -1}; + SendMessage(statusBarWin, SB_SETPARTS, sizeof(statwidths)/sizeof(int), (LPARAM)statwidths); + SendMessage(statusBarWin, SB_SETTEXT, 0, (LPARAM)"Hi there :)"); + + HMENU fileMenu = CreatePopupMenu(); + AppendMenu(fileMenu, MF_STRING, 0, "&Open\tCtrl+O"); + AppendMenu(fileMenu, MF_STRING, 2, "&Save\tCtrl+S"); + AppendMenu(fileMenu, MF_STRING, 3, "Save &As"); + AppendMenu(fileMenu, MF_SEPARATOR, 0, NULL); + AppendMenu(fileMenu, MF_STRING, 3, "&Exit"); + + HMENU editMenu = CreatePopupMenu(); + AppendMenu(editMenu, MF_STRING, WM_USER+0, "&Undo\tCtrl+Z"); + AppendMenu(editMenu, MF_STRING, WM_USER+1, "&Redo\tShift+Ctrl+Z"); + AppendMenu(editMenu, MF_SEPARATOR, 0, NULL); + AppendMenu(editMenu, MF_STRING, WM_CUT, "Cu&t\tCtrl+X"); + AppendMenu(editMenu, MF_STRING, WM_COPY, "&Copy\tCtrl+C"); + AppendMenu(editMenu, MF_STRING, WM_PASTE, "&Paste\tCtrl+V"); + + HMENU rootMenu = CreateMenu(); + AppendMenu(rootMenu, MF_STRING | MF_POPUP, (UINT_PTR)fileMenu, "&File"); + AppendMenu(rootMenu, MF_STRING | MF_POPUP, (UINT_PTR)editMenu, "&Edit"); + SetMenu(hwnd, rootMenu); + } break; case WM_SIZE: { int width = LOWORD(lParam); int height = HIWORD(lParam); - MoveWindow(trackViewWin, 0, 0, width, height, TRUE); + + RECT statusBarRect; + GetClientRect(statusBarWin, &statusBarRect); + int statusBarHeight = statusBarRect.bottom - statusBarRect.top; + + MoveWindow(trackViewWin, 0, 0, width, height - statusBarHeight, TRUE); + MoveWindow(statusBarWin, 0, height - statusBarHeight, width, statusBarHeight, TRUE); } break; case WM_SETFOCUS: SetFocus(trackViewWin); // needed to forward keyboard input break; - + + case WM_COMMAND: + switch (wParam) + { + case WM_COPY: + /* PostMessage(m_hWnd, WM_COPY, 0, 0); */ + /* HMMMM.... not working... */ + printf("copy!\n"); + break; + default: + printf("cmd %d %d\n", wParam, lParam); + } + break; + default: return DefWindowProc(hwnd, msg, wParam, lParam); } @@ -57,12 +117,84 @@ static ATOM registerMainWindowClass(HINSTANCE hInstance) return RegisterClassEx(&wc); } + +#include + int _tmain(int argc, _TCHAR* argv[]) { HWND hwnd; MSG Msg; HINSTANCE hInstance = GetModuleHandle(NULL); + +#if 0 + WSADATA wsaData; + int error = WSAStartup( MAKEWORD( 2, 0 ), &wsaData ); + if (0 != error) + { + fputs("Failed to init WinSock", stderr); + exit(1); + } + + if (LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 0) + { + fputs("Wrong version of WinSock", stderr); + exit(1); + } + + SOCKET server = socket( AF_INET, SOCK_STREAM, 0 ); + + struct sockaddr_in sin; + memset( &sin, 0, sizeof sin ); + + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = INADDR_ANY; + sin.sin_port = htons( 21 ); + + if (SOCKET_ERROR == bind( server, (struct sockaddr *)&sin, sizeof(sin))) + { + fputs("Coult not start server", stderr); + exit(1); + } + + while ( listen( server, SOMAXCONN ) == SOCKET_ERROR ); + + int length = sizeof(sin); + SOCKET client = accept( server, (sockaddr *)&sin, &length ); + + const char *test = "TEST123"; + int sent = send( client, test, strlen(test), 0); + if (SOCKET_ERROR == sent) + { + int err = WSAGetLastError(); + fprintf(stderr, "failed to send - err %d\n", err); + exit(1); + } + + unsigned long int nonBlock = 1; + if (ioctlsocket(client, FIONBIO, &nonBlock) == SOCKET_ERROR) + { + printf("ioctlsocket() failed \n"); + exit(1); + } + + int read = 0; + while (read < 20) + { + char buffer[1024]; + int len = recv(client, buffer, 1024, 0); + if (0 < len) + { + printf("got \"%s\" - left: %d\n", buffer, 20 - (read + len)); + read += len; + } + } + + closesocket(client); + closesocket(server); + WSACleanup(); +#endif + SyncData syncData; SyncTrack &camXTrack = syncData.getTrack("cam.x"); SyncTrack &camYTrack = syncData.getTrack("cam.y"); @@ -118,13 +250,23 @@ int _tmain(int argc, _TCHAR* argv[]) ShowWindow(hwnd, TRUE); UpdateWindow(hwnd); - + +#if 0 +/* while (1) + { + SOCKET client; + int length; + length = sizeof sin; + client = accept( server, (sockaddr *)&sin, &length ); + } */ +#else // Step 3: The Message Loop while(GetMessage(&Msg, NULL, 0, 0) > 0) { TranslateMessage(&Msg); DispatchMessage(&Msg); } +#endif delete trackView; trackView = NULL; diff --git a/synctracker2.vcproj b/synctracker2.vcproj index cf182ba..ad1e40c 100644 --- a/synctracker2.vcproj +++ b/synctracker2.vcproj @@ -60,6 +60,7 @@ /> 0) drawEditString = true; } const SyncTrack &track = trackIter->second; bool key = track.isKeyFrame(row); /* format the text */ - if (!key) _sntprintf_s(temp, 256, _T("---")); + if (drawEditString) _sntprintf_s(temp, 256, editString.c_str()); + else if (!key) _sntprintf_s(temp, 256, _T("---")); else { float val = track.getKeyFrame(row)->value; @@ -347,21 +353,6 @@ void TrackView::setEditTrack(int newEditTrack) editTrack = max(editTrack, 0); editTrack = min(editTrack, getTrackCount() - 1); - // sync up iterators -/* int currEditTrack = oldEditTrack; - while (editTrack != currEditTrack) - { - if (currEditTrack < editTrack) - { - currEditTrack++; - } - else - { - currEditTrack--; - } - } */ - - RECT trackRect; // dirty old and new marker @@ -406,7 +397,7 @@ static int getScrollPos(HWND hwnd, int bar) return int(si.nTrackPos); } -void TrackView::onVScroll(UINT sbCode, int newPos) +LRESULT TrackView::onVScroll(UINT sbCode, int newPos) { switch (sbCode) { @@ -435,9 +426,11 @@ void TrackView::onVScroll(UINT sbCode, int newPos) setEditRow(getScrollPos(hwnd, SB_VERT)); break; } + + return FALSE; } -void TrackView::onHScroll(UINT sbCode, int newPos) +LRESULT TrackView::onHScroll(UINT sbCode, int newPos) { switch (sbCode) { @@ -470,57 +463,73 @@ void TrackView::onHScroll(UINT sbCode, int newPos) setEditTrack(getScrollPos(hwnd, SB_HORZ)); break; } + + return FALSE; } -void TrackView::onKeyDown(UINT keyCode, UINT flags) +LRESULT TrackView::onKeyDown(UINT keyCode, UINT flags) { + bool refreshCaret = false; + bool ctrlDown = GetKeyState(VK_CONTROL) < 0 ? true : false; + bool shiftDown = GetKeyState(VK_SHIFT) < 0 ? true : false; + bool altDown = GetKeyState(VK_MENU) < 0 ? true : false; + + if (editString.empty()) + { + switch (keyCode) + { + case VK_UP: setEditRow(editRow - 1); break; + case VK_DOWN: setEditRow(editRow + 1); break; + + case VK_LEFT: setEditTrack(editTrack - 1); break; + case VK_RIGHT: setEditTrack(editTrack + 1); break; + + case VK_PRIOR: setEditRow(editRow - windowRows / 2); break; + case VK_NEXT: setEditRow(editRow + windowRows / 2); break; + + case 'U': + if (true == ctrlDown && true == shiftDown) + { + if (!syncDataEdit.redo()) MessageBeep(0); + } + else if (true == ctrlDown) + { + if (!syncDataEdit.undo()) MessageBeep(0); + } + InvalidateRect(hwnd, NULL, TRUE); + break; + } + } + switch (keyCode) { - case VK_UP: setEditRow(editRow - 1); break; - case VK_DOWN: setEditRow(editRow + 1); break; - - case VK_LEFT: setEditTrack(editTrack - 1); break; - case VK_RIGHT: setEditTrack(editTrack + 1); break; - - case VK_PRIOR: setEditRow(editRow - windowRows / 2); break; - case VK_NEXT: setEditRow(editRow + windowRows / 2); break; - } -} - -void TrackView::onChar(UINT keyCode, UINT flags) -{ - bool refresh = false; - printf("char: \"%c\" (%d) - flags: %x\n", (char)keyCode, keyCode, flags); - switch ((char)keyCode) - { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '.': - editString.push_back(keyCode); - printf("accepted: %c - %s\n", (char)keyCode, editString.c_str()); - refresh = true; + case VK_RETURN: + { + SyncDataEdit::EditCommand *cmd = new SyncDataEdit::EditCommand( + editTrack, editRow, + float(atof(editString.c_str())) + ); + + syncDataEdit.exec(cmd); + + editString.clear(); + refreshCaret = true; + } break; - case VK_RETURN: - printf("submit: %f\n", atof(editString.c_str())); - // TODO: submit new number - editString.clear(); - refresh = true; + case VK_DELETE: + { + SyncTrack &track = getSyncData()->getTrack(editTrack); + track.deleteKeyFrame(editRow); + refreshCaret = true; + } break; case VK_BACK: if (editString.size() > 0) { editString.resize(editString.size() - 1); - refresh = true; + refreshCaret = true; } else MessageBeep(0); break; @@ -532,28 +541,67 @@ void TrackView::onChar(UINT keyCode, UINT flags) // return to old value (i.e don't clear) editString.clear(); - refresh = true; + refreshCaret = true; MessageBeep(0); } break; - - default: - MessageBeep(0); } - if (refresh) + if (refreshCaret) { -/* RECT rowRect; -NB: getScreenX(int track) - rowRect.left = clientRect.left; - rowRect.right = clientRect.right; - rowRect.top = getScreenY(oldEditRow); - rowRect.bottom = rowRect.top + fontHeight; - InvalidateRect(hwnd, &rowRect, TRUE); */ + RECT rowRect; + rowRect.left = getScreenX(editTrack); + rowRect.right = getScreenX(editTrack + 1); + rowRect.top = getScreenY(editRow); + rowRect.bottom = getScreenY(editRow + 1); + InvalidateRect(hwnd, &rowRect, TRUE); } + + return FALSE; } -void TrackView::onSize(int width, int height) +LRESULT TrackView::onChar(UINT keyCode, UINT flags) +{ + bool refreshCaret = false; + printf("char: \"%c\" (%d) - flags: %x\n", (char)keyCode, keyCode, flags); + switch ((char)keyCode) + { + case '.': + // only one '.' allowed + if (std::string::npos != editString.find('.')) + { + MessageBeep(0); + break; + } + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + editString.push_back(keyCode); + printf("accepted: %c - %s\n", (char)keyCode, editString.c_str()); + refreshCaret = true; + break; + } + + if (refreshCaret) + { + RECT rowRect; + rowRect.left = getScreenX(editTrack); + rowRect.right = getScreenX(editTrack + 1); + rowRect.top = getScreenY(editRow); + rowRect.bottom = getScreenY(editRow + 1); + InvalidateRect(hwnd, &rowRect, TRUE); + } + return FALSE; +} + +LRESULT TrackView::onSize(int width, int height) { const int oldWindowWidth = windowWidth; const int oldWindowHeight = windowHeight; @@ -566,6 +614,7 @@ void TrackView::onSize(int width, int height) setEditRow(editRow); setupScrollBars(); + return FALSE; } LRESULT TrackView::windowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) @@ -574,9 +623,7 @@ LRESULT TrackView::windowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) switch(msg) { - case WM_CREATE: - onCreate(); - break; + case WM_CREATE: return onCreate(); case WM_CLOSE: DestroyWindow(hwnd); @@ -586,28 +633,15 @@ LRESULT TrackView::windowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) PostQuitMessage(0); break; - case WM_SIZE: - onSize(LOWORD(lParam), HIWORD(lParam)); - break; - - case WM_VSCROLL: - onVScroll(LOWORD(wParam), getScrollPos(hwnd, SB_VERT)); - break; - - case WM_HSCROLL: - onHScroll(LOWORD(wParam), getScrollPos(hwnd, SB_HORZ)); - break; - - case WM_PAINT: - onPaint(); - break; - - case WM_KEYDOWN: - onKeyDown((UINT)wParam, (UINT)lParam); - break; - - case WM_CHAR: - onChar((UINT)wParam, (UINT)lParam); + case WM_SIZE: return onSize(LOWORD(lParam), HIWORD(lParam)); + case WM_VSCROLL: return onVScroll(LOWORD(wParam), getScrollPos(hwnd, SB_VERT)); + case WM_HSCROLL: return onHScroll(LOWORD(wParam), getScrollPos(hwnd, SB_HORZ)); + case WM_PAINT: return onPaint(); + case WM_KEYDOWN: return onKeyDown((UINT)wParam, (UINT)lParam); + case WM_CHAR: return onChar((UINT)wParam, (UINT)lParam); + + case WM_COPY: + printf("copy!\n"); break; default: diff --git a/trackview.h b/trackview.h index 68b87c4..d404ef2 100644 --- a/trackview.h +++ b/trackview.h @@ -2,47 +2,152 @@ #include "syncdata.h" +#include #include +#include + class SyncData; +class SyncDataEdit +{ +public: + SyncDataEdit() : syncData(NULL) {} + + void setSyncData(SyncData *syncData) { this->syncData = syncData; } + SyncData *getSyncData() { return syncData; } + + class Command + { + public: + virtual ~Command() {} + virtual void exec(SyncDataEdit *data) = 0; + virtual void undo(SyncDataEdit *data) = 0; + }; + + class EditCommand : public Command + { + public: + EditCommand(int track, int row, float value) : track(track), row(row), newVal(value) {} + ~EditCommand() {} + + virtual void exec(SyncDataEdit *data) + { + SyncTrack &track = data->getSyncData()->getTrack(this->track); + oldValExisting = track.isKeyFrame(row); + if (oldValExisting) oldVal = track.getKeyFrame(row)->value; + track.setKeyFrame(row, newVal); + } + + virtual void undo(SyncDataEdit *data) + { + SyncTrack &track = data->getSyncData()->getTrack(this->track); + if (!oldValExisting) track.deleteKeyFrame(row); + else track.setKeyFrame(row, oldVal); + } + + private: + int track, row; + float newVal, oldVal; + bool oldValExisting; + }; + + void exec(Command *cmd) + { + undoStack.push(cmd); + cmd->exec(this); + clearRedoStack(); + } + + bool undo() + { + if (undoStack.size() == 0) return false; + + Command *cmd = undoStack.top(); + undoStack.pop(); + + redoStack.push(cmd); + cmd->undo(this); + return true; + } + + bool redo() + { + if (redoStack.size() == 0) return false; + + Command *cmd = redoStack.top(); + redoStack.pop(); + + undoStack.push(cmd); + cmd->exec(this); + return true; + } + + void clearRedoStack() + { + while (!redoStack.empty()) + { + Command *cmd = redoStack.top(); + redoStack.pop(); + delete cmd; + } + } + +private: + + std::stack undoStack; + std::stack redoStack; + + SyncData *syncData; +}; + class TrackView { public: TrackView(); ~TrackView(); - + HWND create(HINSTANCE hInstance, HWND hwndParent); HWND getWin(){ return hwnd; } - - void setSyncData(SyncData *syncData) { this->syncData = syncData; } - SyncData *getSyncData() { return syncData; } - + + void setSyncData(SyncData *syncData) { this->syncDataEdit.setSyncData(syncData); } + SyncData *getSyncData() { return syncDataEdit.getSyncData(); } + private: // some nasty hackery to forward the window messages friend static LRESULT CALLBACK trackViewWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); LRESULT windowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); - + // events - void onCreate(); - void onPaint(); - void onVScroll(UINT sbCode, int newPos); - void onHScroll(UINT sbCode, int newPos); - void onSize(int width, int height); - void onKeyDown(UINT keyCode, UINT flags); - void onChar(UINT keyCode, UINT flags); + LRESULT onCreate(); + LRESULT onPaint(); + LRESULT onVScroll(UINT sbCode, int newPos); + LRESULT onHScroll(UINT sbCode, int newPos); + LRESULT onSize(int width, int height); + LRESULT onKeyDown(UINT keyCode, UINT flags); + LRESULT onChar(UINT keyCode, UINT flags); // the window procedure - + void paintTracks(HDC hdc, RECT rcTracks); void paintTopMargin(HDC hdc, RECT rcTracks); void setupScrollBars(); void setScrollPos(int newScrollPosX, int newScrollPosY); void scrollWindow(int newScrollPosX, int newScrollPosY); - + + void invalidatePos(int track, int row) + { + RECT rect; + rect.left = getScreenX(track); + rect.right = getScreenX(track + 1); + rect.top = getScreenY(row); + rect.bottom = getScreenY(row + 1); + InvalidateRect(hwnd, &rect, TRUE); + } + void setEditRow(int newEditRow); void setEditTrack(int newEditTrack); - + int getScreenY(int row); int getScreenX(int track); @@ -52,16 +157,16 @@ private: if (NULL == syncData) return 0; return int(syncData->getTrackCount()); }; - + /* cursor position */ int editRow, editTrack; - + int scrollPosX, scrollPosY; int windowWidth, windowHeight; int windowRows, windowTracks; - SyncData *syncData; - + SyncDataEdit syncDataEdit; + std::string editString; HWND hwnd;