From 8a946ee58bbf893df3d6969317179db94a243f33 Mon Sep 17 00:00:00 2001 From: Quantum Date: Fri, 7 Jun 2013 19:40:52 -0400 Subject: [PATCH] Used a Piano control instead of a bitmap. Signed-off-by: Xiaomao Chen --- Makefile | 4 +- include/MainWindow.hpp | 5 +- include/PianoControl.hpp | 73 ++++++ include/resource.h | 1 - keyboard.bmp | Bin 65590 -> 0 bytes keyboard.rc | 1 - keymap.bmp | Bin 102478 -> 0 bytes src/MainWindow.cpp | 209 ++++++++------- src/PianoControl.cpp | 540 +++++++++++++++++++++++++++++++++++++++ 9 files changed, 741 insertions(+), 92 deletions(-) create mode 100644 include/PianoControl.hpp delete mode 100644 keyboard.bmp delete mode 100644 keymap.bmp create mode 100644 src/PianoControl.cpp diff --git a/Makefile b/Makefile index 5ccff7e..53b1496 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,7 @@ OUTDIR=build\$(BUILD) DISTDIR=dist\$(BUILD) FILES=$(OUTDIR)\Keyboard.obj \ $(OUTDIR)\MainWindow.obj \ + $(OUTDIR)\PianoControl.obj \ $(OUTDIR)\Window.obj \ $(OUTDIR)\keyboard.res @@ -34,9 +35,10 @@ initdir: if not exist build md dist if not exist $(DISTDIR) md $(DISTDIR) -$(INCDIR)\MainWindow.hpp: $(INCDIR)\Window.hpp +$(INCDIR)\MainWindow.hpp: $(INCDIR)\Window.hpp $(INCDIR)\PianoControl.hpp $(SRCDIR)\MainWindow.cpp: $(INCDIR)\MainWindow.hpp +$(SRCDIR)\PianoControl.cpp: $(INCDIR)\PianoControl.hpp $(SRCDIR)\Keyboard.cpp: $(INCDIR)\MainWindow.hpp $(SRCDIR)\Window.cpp: $(INCDIR)\Window.hpp keyboard.rc: keyboard.ico keymap.bmp diff --git a/include/MainWindow.hpp b/include/MainWindow.hpp index 61bb231..85ec4ac 100644 --- a/include/MainWindow.hpp +++ b/include/MainWindow.hpp @@ -3,6 +3,7 @@ #define id68C60171_0140_4DE1_B7255EFF557A74F9 #include +#include #include #include @@ -32,11 +33,11 @@ protected: HMIDIOUT m_midi; bool isQWERTY; HKL hklQWERTY; + PianoControl *piano; + LPWSTR m_keychars; private: HFONT hFont; HBRUSH hBrush; - HBITMAP hKeyboardLayout; - HDC hdcKeyboard; }; #endif diff --git a/include/PianoControl.hpp b/include/PianoControl.hpp new file mode 100644 index 0000000..075b099 --- /dev/null +++ b/include/PianoControl.hpp @@ -0,0 +1,73 @@ +#pragma once +#ifndef id7A156843_8DC8_4B5A_83D273814FBE10DE +#define id7A156843_8DC8_4B5A_83D273814FBE10DE + +#include + +#include +#include +#include + +#define MPCM_GETKEYSTATUS (WM_USER + 0) +#define MPCM_SETKEYSTATUS (WM_USER + 1) +#define MPCM_GETOCTAVES (WM_USER + 2) +#define MPCM_SETOCTAVES (WM_USER + 3) +#define MPCM_GETKEYTEXT (WM_USER + 4) +#define MPCM_SETKEYTEXT (WM_USER + 5) +#define MPCM_GETBACKGROUND (WM_USER + 6) +#define MPCM_SETBACKGROUND (WM_USER + 7) + +class PianoControl : public Window { +public: + virtual LPCTSTR ClassName() { return TEXT("KeyboardControl"); } + static PianoControl *Create(LPCTSTR szTitle, HWND hwParent, + DWORD dwStyle = 0, + int x = CW_USEDEFAULT, int y = CW_USEDEFAULT, + int cx = CW_USEDEFAULT, int cy = CW_USEDEFAULT); + + virtual void SetOctaves(int octaves); + virtual int GetOctaves() { return octaves; } + + virtual void SetKeyStatus(int key, bool down); + virtual bool GetKeyStatus(int key); + + virtual void SetKeyText(int key, LPCWSTR text); + virtual LPCWSTR GetKeyText(int key); + + virtual void SetBackground(HBRUSH background) { hBackground = background; } + virtual HBRUSH GetBackground() { return hBackground; } + + HFONT GetFont() { return hFont; } + void SetFont(HFONT font) { hFont = font; } +protected: + LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam); + LRESULT OnCreate(); + LRESULT OnDestroy(); + void OnPaint(); + virtual void PaintContent(PAINTSTRUCT *pps); + BOOL WinRegisterClass(WNDCLASS *pwc); + + virtual int keyIDToInternal(int id, bool &black); + virtual bool haveBlackToLeft(int id); + virtual bool haveBlackToRight(int id); + int haveBlack(int id) { + return (haveBlackToLeft(id) ? 2 : 0) | (haveBlackToRight(id) ? 1 : 0); + } + virtual void UpdateKey(int key, bool black); + + bool *blackStatus; + bool *whiteStatus; + LPCWSTR *blackText; + LPCWSTR *whiteText; + + int octaves; + HFONT hFont; + HWND hwParent; + + HDC hMemDC; + HBITMAP hMemBitmap; + HBRUSH hBackground; + int bmx, bmy; +}; + +#endif diff --git a/include/resource.h b/include/resource.h index 3b97a01..4501386 100644 --- a/include/resource.h +++ b/include/resource.h @@ -1,2 +1 @@ #define RID_ICON 1 -#define RID_KEYBOARD 2 diff --git a/keyboard.bmp b/keyboard.bmp deleted file mode 100644 index 13cfed2a4a3a0da523909e1ebcb59518496e1cc1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 65590 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 MSP#T{prju79}|3$i2wiq diff --git a/keyboard.rc b/keyboard.rc index def68c3..06cf7a2 100644 --- a/keyboard.rc +++ b/keyboard.rc @@ -2,7 +2,6 @@ #include RID_ICON ICON keyboard.ico -RID_KEYBOARD BITMAP keymap.bmp STRINGTABLE LANGUAGE 0x09, 0x01 // en-US BEGIN diff --git a/keymap.bmp b/keymap.bmp deleted file mode 100644 index 567402a89e34be46b4dd58ccb964211ffc3dceb9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102478 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 = PianoControl::Create(NULL, m_hwnd, WS_VISIBLE | WS_CHILD, 0, 0, 0, 0); + this->piano->SetBackground( + (HBRUSH) DefWindowProc(m_hwnd, WM_CTLCOLORSTATIC, + (WPARAM) GetDC(m_volumeLabel), + (LPARAM) m_volumeLabel)); + + { + keymap[VK_OEM_3] = 54; // `~ key + keymap['Q'] = 55; + keymap[VK_TAB] = 55; + keymap['A'] = 57; + keymap['Z'] = 57; + keymap['W'] = 56; + keymap['E'] = 58; + keymap['S'] = 59; + keymap['X'] = 59; + keymap['D'] = 60; + keymap['C'] = 60; + keymap['R'] = 61; + keymap['F'] = 62; + keymap['V'] = 62; + keymap['T'] = 63; + keymap['G'] = 64; + keymap['B'] = 64; + keymap['H'] = 65; + keymap['N'] = 65; + keymap['U'] = 66; + keymap['J'] = 67; + keymap['M'] = 67; + keymap['I'] = 68; + keymap['K'] = 69; + keymap[VK_OEM_COMMA] = 69; + keymap['O'] = 70; + keymap['L'] = 71; + keymap[VK_OEM_PERIOD] = 71; + keymap[VK_OEM_1] = 72; // ;: + keymap[VK_OEM_2] = 72; // /? + keymap[VK_OEM_7] = 74; // '" + keymap[VK_OEM_4] = 73; // [ + keymap[VK_OEM_6] = 75; // ] + keymap[VK_RETURN] = 76; + keymap[VK_OEM_5] = 77; // \| + keymap[VK_BACK] = 77; + keymap[0x31] = 57; + keymap[0x32] = 59; + keymap[0x33] = 60; + keymap[0x34] = 62; + keymap[0x35] = 64; + keymap[0x36] = 65; + keymap[0x37] = 67; + keymap[0x38] = 69; + keymap[0x39] = 71; + keymap[0x30] = 72; + keymap[VK_OEM_MINUS] = 74; + keymap[VK_OEM_PLUS] = 76; + } + m_keychars = NULL; + PostMessage(m_hwnd, WM_INPUTLANGCHANGE, 0, 0); return 0; } @@ -183,21 +206,6 @@ HICON MainWindow::GetIcon() void MainWindow::PaintContent(PAINTSTRUCT *pps) { - BITMAP bm; - int cx, cy, tx, ty, x, y; - double ratio; - RECT client; - GetClientRect(m_hwnd, &client); - GetObject(hKeyboardLayout, sizeof(BITMAP), &bm); - cx = client.right - 24, cy = client.bottom - 117; - ratio = ((double) bm.bmWidth) / ((double) bm.bmHeight); - if (cx / ratio > cy) - tx = (int) (cy * ratio), ty = (int) cy; - else - tx = (int) cx, ty = (int) (cx / ratio); - x = 12 + (cx - tx) / 2; - y = 12 + (cy - ty) / 2; - StretchBlt(pps->hdc, x, y, tx, ty, hdcKeyboard, 0, 0, bm.bmWidth, bm.bmHeight, SRCAND); } void MainWindow::OnPaint() @@ -264,16 +272,14 @@ int GetMIDINote(WPARAM wCode) bool MainWindow::Play(WPARAM wParam, LPARAM lParam, bool down) { - int note; + int note, id; WORD wCode = GetQWERTYKeyCode((WORD) wParam); if (wCode > 255 || !keymap[wCode] || (down && (lParam & 0x40000000))) return false; note = GetMIDINote(wCode); if (down) { - int num = note; - while (num > 24) - num -= 24; + int num = note % 24; while (num < 0x7F) { if (num != note) MIDI_MESSAGE(m_midi, 0x90, num, 0); @@ -284,6 +290,7 @@ bool MainWindow::Play(WPARAM wParam, LPARAM lParam, bool down) MIDI_MESSAGE(m_midi, 0x90, note, m_force); else MIDI_MESSAGE(m_midi, 0x90, note, 0); + piano->SetKeyStatus((note - 6) % 24, down); return true; } @@ -314,6 +321,9 @@ LRESULT MainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) REPOS(m_instruSelect, BOTTOM(82, client.bottom - 72, client.right - 94, 25)); EndDeferWindowPos(hdwp); #undef REPOS + //REPOS(piano->GetHWND(), LEFT(12, 12, client.right - 24, client.bottom - 117)); + if (!MoveWindow(piano->GetHWND(), 12, 12, client.right - 24, client.bottom - 117, TRUE)) + MessageBox(m_hwnd, 0, 0, 0); return 0; } case WM_COMMAND: @@ -362,6 +372,31 @@ LRESULT MainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) isQWERTY = lstrcmpi(buf, L"00000409") == 0; if (!isQWERTY && !hklQWERTY) hklQWERTY = LoadKeyboardLayout(L"00000409", KLF_NOTELLSHELL); + + int size = dnslen(keychars) + 1, i; + LPWSTR s; + if (m_keychars) + delete m_keychars; + m_keychars = new WCHAR[size]; + for (i = 0; i < size; ++i) { + WORD scan = VkKeyScanEx(keychars[i], hklQWERTY); + if (LOBYTE(scan) == -1) + goto giveup; + WORD vk = GetRealKeyCode(LOBYTE(scan)); + if (!vk || vk == LOBYTE(scan)) + goto giveup; + WCHAR ch = (WCHAR) MapVirtualKey(vk, MAPVK_VK_TO_CHAR); + if (!ch) + goto giveup; + m_keychars[i] = ch; + continue; + + giveup: + m_keychars[i] = keychars[i]; + } + + for (s = m_keychars, i = 0; i < 24 && lstrlen(s); s += lstrlen(s) + 1, ++i) + piano->SetKeyText(i, s); } case WM_CHAR: case WM_DEADCHAR: diff --git a/src/PianoControl.cpp b/src/PianoControl.cpp new file mode 100644 index 0000000..168d6f3 --- /dev/null +++ b/src/PianoControl.cpp @@ -0,0 +1,540 @@ +#include + +#include +#include + +#define MessageErrorWndTitle(hwnd, e, t) MessageBox(hwnd, e, t, MB_ICONERROR) +#define MessageErrorWnd(hwnd, e) MessageBox(hwnd, e, T("Error!"), MB_ICONERROR) +#define MessageError(e) MessageErrorWnd(NULL, e) + +#define MessageLastErrorWndTitle(hwnd, e, title)\ + do { \ + LPTSTR s; \ + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, e, 0, (LPTSTR)&s, 0, NULL) == 0) {\ + TCHAR str[25]; \ + snprintf(str, 25, T("%x"), str); \ + MessageErrorWndTitle(hwnd, str, title); \ + } else { \ + MessageErrorWndTitle(hwnd, s, title); \ + LocalFree(s); \ + } \ + } while (0) +#define MessageLastErrorWnd(hwnd, e) MessageLastErrorWndTitle(hwnd, e, T("Error!")) +#define MessageLastError(e) MessageLastErrorWnd(NULL, e) + +#define MessageIntBox(hwnd, i, title, opt) \ + do { \ + CHAR buf[100]; \ + sprintf_s(buf, 100, "%d", i); \ + MessageBoxA(hwnd, buf, title, opt); \ + } while (0) + +#define MessageIntWndTitle(hwnd, i, title) MessageIntBox(hwnd, i, title, MB_ICONINFORMATION) +#define MessageIntWnd(hwnd, i) MessageIntWndTitle(hwnd, i, "Debug Info") +#define MessageInt(i) MessageIntWnd(NULL, i) + +BOOL PianoControl::WinRegisterClass(WNDCLASS *pwc) +{ + return __super::WinRegisterClass(pwc); +} + +LRESULT PianoControl::OnCreate() +{ + NONCLIENTMETRICS ncmMetrics = { sizeof(NONCLIENTMETRICS) }; + RECT client; + + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncmMetrics, 0); + GetClientRect(m_hwnd, &client); + + hFont = CreateFontIndirect(&ncmMetrics.lfMessageFont); + hwParent = GetParent(m_hwnd); + + hMemDC = NULL; + hMemBitmap = NULL; + bmx = bmy = 0; + + blackStatus = whiteStatus = NULL; + blackText = whiteText = NULL; + + SetOctaves(2); + return 0; +} + +LRESULT PianoControl::OnDestroy() +{ + if (hMemDC) + DeleteDC(hMemDC); + return 0; +} + +void PianoControl::SetOctaves(int octaves) +{ + bool *newBlackStatus, *newWhiteStatus; + LPCWSTR *newBlackText, *newWhiteText; + + #define RENEW(type, newname, store) {\ + newname = new type[7 * octaves];\ + if (store) {\ + memcpy(newname, store, min(this->octaves * 7, 7 * octaves) * sizeof(type));\ + delete store;\ + } else \ + memset(newname, 0, 7 * octaves * sizeof(type));\ + store = newname;\ + } + RENEW(bool, newBlackStatus, blackStatus); + RENEW(bool, newWhiteStatus, whiteStatus); + RENEW(LPCWSTR, newBlackText, blackText); + RENEW(LPCWSTR, newWhiteText, whiteText); + + this->octaves = octaves; +} + +void PianoControl::UpdateKey(int key, bool black) +{ + RECT client; + int width, height; + int wwidth, bwidth, bheight, hbwidth; + + GetClientRect(m_hwnd, &client); + width = client.right - client.left; + height = client.bottom - client.top; + wwidth = width / 7 / octaves; // Displaying 14 buttons. + bwidth = width / 12 / octaves; // smaller + bheight = height / 2; + hbwidth = bwidth / 2; + + if (black) { + client.left += (key * wwidth) - hbwidth + 2; + client.right = client.left + bwidth - 5; + client.bottom = client.top + bheight; + InvalidateRect(m_hwnd, &client, FALSE); + } else { + client.left += key * wwidth; + client.right = client.left + wwidth; + client.bottom = client.top + height; + InvalidateRect(m_hwnd, &client, FALSE); + } +} + +void PianoControl::SetKeyStatus(int key, bool down) +{ + bool black; + int id = keyIDToInternal(key, black); + + (black ? blackStatus : whiteStatus)[id] = down; + UpdateKey(id, black); +} + +bool PianoControl::GetKeyStatus(int key) +{ + bool black; + int id = keyIDToInternal(key, black); + + return (black ? blackStatus : whiteStatus)[id]; +} + +void PianoControl::SetKeyText(int key, LPCWSTR text) +{ + bool black; + int id = keyIDToInternal(key, black); + + (black ? blackText : whiteText)[id] = text; + UpdateKey(id, black); +} + +LPCWSTR PianoControl::GetKeyText(int key) +{ + bool black; + int id = keyIDToInternal(key, black); + + return (black ? blackText : whiteText)[id]; +} + +int PianoControl::keyIDToInternal(int id, bool &black) { + switch (id % 12) { + case 0: + case 2: + case 4: + case 7: + case 9: + black = true; + break; + default: + black = false; + } + + int ret = 0; + switch (id % 12) { + case 0: + case 1: + ret = 0; + break; + case 2: + case 3: + ret = 1; + break; + case 4: + case 5: + ret = 2; + break; + case 6: + ret = 3; + break; + case 7: + case 8: + ret = 4; + break; + case 9: + case 10: + ret = 5; + break; + case 11: + ret = 6; + break; + } + + return id / 12 * 7 + ret; +} + +bool PianoControl::haveBlackToLeft(int i) { + switch (i % 7) { + case 0: // G + case 1: // A + case 2: // B + return true; + case 3: // C + return false; + case 4: // D + case 5: // E + return true; + case 6: // F + return false; + } + return false; // not reached +} + +bool PianoControl::haveBlackToRight(int i) { + switch (i % 7) { + case 0: // G + case 1: // A + return true; + case 2: // B + return false; + case 3: // C + case 4: // D + return true; + case 5: // E + return false; + case 6: // F + return true; + } + return false; // not reached +} + +void PianoControl::PaintContent(PAINTSTRUCT *pps) +{ + RECT client, rect; + int width, height; + int wwidth, bwidth, bheight, hbwidth; + HDC hdc = pps->hdc; + HBRUSH hbFace = GetSysColorBrush(COLOR_3DFACE), + hbDC = GetStockBrush(DC_BRUSH), + hbOriginal; + HPEN hPenOriginal, hPenDC = GetStockPen(DC_PEN); + HFONT hFontOriginal = SelectFont(hdc, hFont), hFontNew; + LPWSTR szBuffer = NULL; + int bufsize = 0; + COLORREF textColourOriginal = GetTextColor(hdc), + backgroundOriginal = SetBkMode(hdc, TRANSPARENT); + LOGFONT lf; + GetClientRect(m_hwnd, &client); + width = client.right - client.left; + height = client.bottom - client.top; + wwidth = width / 7 / octaves; // Displaying 14 buttons. + bwidth = width / 12 / octaves; // smaller + bheight = height / 2; + bheight = height / 2; + hbwidth = bwidth / 2; + + hbOriginal = SelectBrush(hdc, hBackground); + hPenOriginal = SelectPen(hdc, hPenDC); + + GetObject(hFont, sizeof(LOGFONT), &lf); + lf.lfWidth = 0; + lf.lfHeight = min(bwidth, bheight / 4); + hFontNew = CreateFontIndirect(&lf); + SelectFont(hdc, hFontNew); + + #define MoveTo(hdc, x, y) MoveToEx(hdc, x, y, NULL) + #define CURVE_SIZE 5 + #define CURVE_CIRCLE (2*CURVE_SIZE) + #define DRAWLINE(x1, y1, x2, y2) (\ + MoveTo(hdc, x1, y1),\ + LineTo(hdc, x2, y2)\ + ) + #define DRAWVERTICAL(length, x, y, color) (\ + SetDCPenColor(hdc, color),\ + DRAWLINE(x, y, x, y + length)\ + ) + #define DRAWHORIZON(length, x, y, color) (\ + SetDCPenColor(hdc, color),\ + DRAWLINE(x, y, x + length, y)\ + ) + #define DRAWBORDER(length, dx, color) (\ + SetDCPenColor(hdc, color),\ + MoveTo(hdc, sx + dx, 0),\ + LineTo(hdc, sx + dx, length),\ + MoveTo(hdc, ex - dx - 1, 0),\ + LineTo(hdc, ex - dx - 1, length)\ + ) + #define DRAWBOX(start, d, height, color) (\ + SetDCPenColor(hdc, color),\ + RoundRect(hdc, sx + d, start - CURVE_SIZE, ex - d, height - d, CURVE_CIRCLE, CURVE_CIRCLE)\ + ) + #define INITIALIZE_PAINT_TEXT(store) \ + int len = lstrlen(store[i]), bufneed = len * 3 + 6; \ + int bufidx = 0; \ + if (bufsize < bufneed) { \ + if (szBuffer) \ + delete szBuffer; \ + szBuffer = new WCHAR[bufneed]; \ + } \ + for (LPCWSTR c = store[i]; *c; c++) { \ + szBuffer[bufidx++] = *c; \ + szBuffer[bufidx++] = L'\r'; \ + szBuffer[bufidx++] = L'\n'; \ + } \ + szBuffer[bufidx] = 0; + #define GETBORDER0(down) (down ? GetSysColor(COLOR_3DLIGHT) : RGB(0, 0, 0)) + #define GETBORDER1(down) (down ? GetSysColor(COLOR_3DSHADOW) : GetSysColor(COLOR_3DDKSHADOW)) + #define GETBORDER2(down) (down ? GetSysColor(COLOR_3DDKSHADOW) : GetSysColor(COLOR_3DSHADOW)) + + rect.top = height - CURVE_SIZE, rect.bottom = height; + rect.left = client.left, rect.right = client.right; + FillRect(hdc, &rect, hBackground); + + rect.top = client.top, rect.bottom = client.bottom; + rect.left = client.right - width % (7 * octaves), rect.right = client.right; + FillRect(hdc, &rect, hBackground); + for (int i = 0; i < 7 * octaves; ++i) { + int sx = i * wwidth, ex = i * wwidth + wwidth - 1; + bool down = whiteStatus[i]; + + SelectBrush(hdc, hbDC); + SetDCBrushColor(hdc, GETBORDER1(down)); + DRAWBOX(bheight, 0, height, GETBORDER0(down)); + SetDCBrushColor(hdc, GETBORDER2(down)); + DRAWBOX(bheight, 1, height, GETBORDER1(down)); + SelectBrush(hdc, hbFace); + DRAWBOX(bheight, 2, height, GETBORDER2(down)); + + rect.top = 0, rect.bottom = bheight, rect.left = sx, rect.right = ex; + FillRect(hdc, &rect, hBackground); + + switch (haveBlack(i)) { + case 0: // none + DRAWBORDER(bheight, 0, GETBORDER0(down)); + DRAWBORDER(bheight, 1, GETBORDER1(down)); + DRAWBORDER(bheight, 2, GETBORDER2(down)); + break; + case 1: // right + DRAWVERTICAL(bheight, sx + 0, 0, GETBORDER0(down)); + DRAWVERTICAL(bheight, sx + 1, 0, GETBORDER1(down)); + DRAWVERTICAL(bheight, sx + 2, 0, GETBORDER2(down)); + DRAWVERTICAL(bheight + 0, ex - hbwidth - 0, 0, GETBORDER0(down)); + DRAWVERTICAL(bheight + 1, ex - hbwidth - 1, 0, GETBORDER1(down)); + DRAWVERTICAL(bheight + 2, ex - hbwidth - 2, 0, GETBORDER2(down)); + DRAWHORIZON(hbwidth - 1, ex - hbwidth - 0, bheight + 0, GETBORDER0(down)); + DRAWHORIZON(hbwidth - 1, ex - hbwidth - 1, bheight + 1, GETBORDER1(down)); + DRAWHORIZON(hbwidth - 1, ex - hbwidth - 2, bheight + 2, GETBORDER2(down)); + rect.top = 0, rect.bottom = bheight, rect.left = sx + 3, rect.right = ex - hbwidth - 2; + FillRect(hdc, &rect, hbFace); + break; + case 2: // left + DRAWVERTICAL(bheight + 0, sx + hbwidth + 0, 0, GETBORDER0(down)); + DRAWVERTICAL(bheight + 1, sx + hbwidth + 1, 0, GETBORDER1(down)); + DRAWVERTICAL(bheight + 2, sx + hbwidth + 2, 0, GETBORDER2(down)); + DRAWVERTICAL(bheight, ex - 1, 0, GETBORDER0(down)); + DRAWVERTICAL(bheight, ex - 2, 0, GETBORDER1(down)); + DRAWVERTICAL(bheight, ex - 3, 0, GETBORDER2(down)); + DRAWHORIZON(hbwidth + 1, sx + 0, bheight + 0, GETBORDER0(down)); + DRAWHORIZON(hbwidth + 1, sx + 1, bheight + 1, GETBORDER1(down)); + DRAWHORIZON(hbwidth + 1, sx + 2, bheight + 2, GETBORDER2(down)); + rect.top = 0, rect.bottom = bheight, rect.left = sx + hbwidth + 3, rect.right = ex - 3; + FillRect(hdc, &rect, hbFace); + break; + case 3: // both + DRAWVERTICAL(bheight + 0, sx + hbwidth + 0, 0, GETBORDER0(down)); + DRAWVERTICAL(bheight + 1, sx + hbwidth + 1, 0, GETBORDER1(down)); + DRAWVERTICAL(bheight + 2, sx + hbwidth + 2, 0, GETBORDER2(down)); + DRAWVERTICAL(bheight + 0, ex - hbwidth - 0, 0, GETBORDER0(down)); + DRAWVERTICAL(bheight + 1, ex - hbwidth - 1, 0, GETBORDER1(down)); + DRAWVERTICAL(bheight + 2, ex - hbwidth - 2, 0, GETBORDER2(down)); + DRAWHORIZON(hbwidth + 1, sx + 0, bheight + 0, GETBORDER0(down)); + DRAWHORIZON(hbwidth + 1, sx + 1, bheight + 1, GETBORDER1(down)); + DRAWHORIZON(hbwidth + 1, sx + 2, bheight + 2, GETBORDER2(down)); + DRAWHORIZON(hbwidth - 1, ex - hbwidth - 0, bheight + 0, GETBORDER0(down)); + DRAWHORIZON(hbwidth - 1, ex - hbwidth - 1, bheight + 1, GETBORDER1(down)); + DRAWHORIZON(hbwidth - 1, ex - hbwidth - 2, bheight + 2, GETBORDER2(down)); + rect.top = 0, rect.bottom = bheight, rect.left = sx + hbwidth + 3, rect.right = ex - hbwidth - 2; + FillRect(hdc, &rect, hbFace); + break; + } + + if (whiteText[i]) { + INITIALIZE_PAINT_TEXT(whiteText); + rect.top = bheight + bheight / 7, rect.bottom = height - bheight / 7; + rect.left = sx, rect.right = ex; + SetTextColor(hdc, RGB(0, 0, 0)); + DrawText(hdc, szBuffer, -1, &rect, DT_CENTER); + } + + rect.top = client.top, rect.bottom = client.bottom; + rect.left = ex, rect.right = ex + 1; + FillRect(hdc, &rect, hBackground); + } + for (int i = 0; i < 7 * octaves; ++i) { + if (!haveBlackToLeft(i)) + continue; + int sx = (i * wwidth) - hbwidth + 2, ex = sx + bwidth - 5; + int kj = bwidth / 4, dc = 128 / kj; + bool down = blackStatus[i]; + SelectBrush(hdc, hbDC); + for (int j = 0; j < kj; ++j) { + int gray = down ? j * dc : (128 - j * dc); + COLORREF colour = RGB(gray, gray, gray); + SetDCBrushColor(hdc, colour); + DRAWBOX(-CURVE_SIZE, j, bheight - 2, colour); + } + + if (blackText[i]) { + INITIALIZE_PAINT_TEXT(blackText); + rect.top = bheight / 7, rect.bottom = bheight - bheight / 7; + rect.left = max(0, sx), rect.right = ex; + SetTextColor(hdc, RGB(255, 255, 255)); + DrawText(hdc, szBuffer, -1, &rect, DT_CENTER); + } + } + #undef MoveTo + #undef DRAWLINE + #undef DRAWVERTICAL + #undef DRAWHORIZON + #undef DRAWBORDER + #undef DRAWBOX + #undef GETBORDER1 + #undef GETBORDER2 + SelectBrush(hdc, hbOriginal); + SelectPen(hdc, hPenOriginal); + DeleteObject(hFontNew); + SelectFont(hdc, hFontOriginal); + SetTextColor(hdc, textColourOriginal); + SetBkMode(hdc, backgroundOriginal); + if (szBuffer) + delete szBuffer; +} + +void PianoControl::OnPaint() +{ + PAINTSTRUCT ps; + BeginPaint(m_hwnd, &ps); + + int x = ps.rcPaint.left; + int y = ps.rcPaint.top; + int cx = ps.rcPaint.right - ps.rcPaint.left; + int cy = ps.rcPaint.bottom - ps.rcPaint.top; + HDC hdc = ps.hdc; + + if (!hMemDC) + hMemDC = CreateCompatibleDC(hdc); + if (!hMemBitmap) + hMemBitmap = CreateCompatibleBitmap(hdc, cx + 50, cy + 50); + if (cx > bmx || cy > bmy) { + if (hMemBitmap) + DeleteObject(hMemBitmap); + hMemBitmap = CreateCompatibleBitmap(hdc, cx + 50, cy + 50); + } + if (hMemDC && hMemBitmap) { + ps.hdc = hMemDC; + + HBITMAP hbmPrev = SelectBitmap(hMemDC, hMemBitmap); + SetWindowOrgEx(hMemDC, x, y, NULL); + + PaintContent(&ps); + BitBlt(hdc, x, y, cx, cy, hMemDC, x, y, SRCCOPY); + + SelectObject(hMemDC, hbmPrev); + } else + PaintContent(&ps); + EndPaint(m_hwnd, &ps); +} + +LRESULT PianoControl::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) { + case WM_CREATE: + return OnCreate(); + case WM_DESTROY: + return OnDestroy(); + case WM_NCDESTROY: + PostQuitMessage(0); + break; + case WM_PAINT: + OnPaint(); + return 0; + case WM_SIZE: + InvalidateRect(m_hwnd, NULL, TRUE); + return 0; + case WM_LBUTTONDOWN: + SetFocus(hwParent); + return 0; + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + case WM_KEYUP: + case WM_SYSKEYUP: + case WM_CHAR: + case WM_DEADCHAR: + case WM_SYSCHAR: + case WM_SYSDEADCHAR: + return SendMessage(hwParent, uMsg, wParam, lParam); + case WM_GETFONT: + return (LRESULT) GetFont(); + case WM_SETFONT: + SetFont((HFONT) wParam); + if (LOWORD(lParam)) + InvalidateRect(m_hwnd, NULL, TRUE); + case MPCM_GETKEYSTATUS: + return GetKeyStatus(wParam); + case MPCM_SETKEYSTATUS: + SetKeyStatus(wParam, lParam != 0); + return 0; + case MPCM_GETOCTAVES: + return GetOctaves(); + case MPCM_SETOCTAVES: + SetOctaves(wParam); + return 0; + case MPCM_GETKEYTEXT: + return GetOctaves(); + case MPCM_SETKEYTEXT: + SetOctaves(wParam); + return 0; + case MPCM_GETBACKGROUND: + return (LRESULT) GetBackground(); + case MPCM_SETBACKGROUND: + SetBackground((HBRUSH) wParam); + return 0; + } + return __super::HandleMessage(uMsg, wParam, lParam); +} + +PianoControl *PianoControl::Create(LPCTSTR szTitle, HWND hwParent, + DWORD dwStyle, int x, int y, int cx, int cy) +{ + PianoControl *self = new PianoControl(); + if (self && + self->WinCreateWindow(0, szTitle, dwStyle, x, y, cx, cy, + hwParent, NULL)) { + return self; + } + delete self; + return NULL; +}