mirror of
https://github.com/quantum5/MusicKeyboard.git
synced 2025-04-24 21:21:59 -04:00
Used a Piano control instead of a bitmap.
Signed-off-by: Xiaomao Chen <xiaomao5@live.com>
This commit is contained in:
parent
3f80ecd644
commit
8a946ee58b
4
Makefile
4
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
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#define id68C60171_0140_4DE1_B7255EFF557A74F9
|
||||
|
||||
#include <Window.hpp>
|
||||
#include <PianoControl.hpp>
|
||||
|
||||
#include <mmsystem.h>
|
||||
#include <commctrl.h>
|
||||
|
@ -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
|
||||
|
|
73
include/PianoControl.hpp
Normal file
73
include/PianoControl.hpp
Normal file
|
@ -0,0 +1,73 @@
|
|||
#pragma once
|
||||
#ifndef id7A156843_8DC8_4B5A_83D273814FBE10DE
|
||||
#define id7A156843_8DC8_4B5A_83D273814FBE10DE
|
||||
|
||||
#include <Window.hpp>
|
||||
|
||||
#include <mmsystem.h>
|
||||
#include <commctrl.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
#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
|
|
@ -1,2 +1 @@
|
|||
#define RID_ICON 1
|
||||
#define RID_KEYBOARD 2
|
||||
|
|
BIN
keyboard.bmp
BIN
keyboard.bmp
Binary file not shown.
Before Width: | Height: | Size: 64 KiB |
|
@ -2,7 +2,6 @@
|
|||
#include <winnls.h>
|
||||
|
||||
RID_ICON ICON keyboard.ico
|
||||
RID_KEYBOARD BITMAP keymap.bmp
|
||||
|
||||
STRINGTABLE LANGUAGE 0x09, 0x01 // en-US
|
||||
BEGIN
|
||||
|
|
BIN
keymap.bmp
BIN
keymap.bmp
Binary file not shown.
Before Width: | Height: | Size: 100 KiB |
|
@ -21,6 +21,39 @@
|
|||
DWORD rgbWindowBackground;
|
||||
|
||||
static char keymap[256];
|
||||
static LPWSTR keychars =
|
||||
L"~`\0" // F#3
|
||||
L"\x21c6Q\0" // G3
|
||||
L"W\0" // G#3
|
||||
L"AZ1\0" // A3
|
||||
L"E\0" // A#3
|
||||
L"SX2\0" // B3
|
||||
L"DC3\0" // C4
|
||||
L"R\0" // C#4
|
||||
L"FV4\0" // D4
|
||||
L"T\0" // D#4
|
||||
L"GB5\0" // E4
|
||||
L"HN6\0" // F4
|
||||
L"U\0" // F#4
|
||||
L"JM7\0" // G4
|
||||
L"I\0" // G#4
|
||||
L"K,8\0" // A4
|
||||
L"O\0" // A#4
|
||||
L"L.9\0" // B4
|
||||
L";/0\0" // C5
|
||||
L"[\0" // C#5
|
||||
L"\"-\0" // D5
|
||||
L"]\0" // D#5
|
||||
L"=\x21b5\0" // E5
|
||||
L"\x2190\0" // F5
|
||||
;
|
||||
|
||||
int dnslen(LPWSTR dns)
|
||||
{
|
||||
LPWSTR i = dns;
|
||||
for (; lstrlen(i); i += lstrlen(i) + 1);
|
||||
return i - dns;
|
||||
}
|
||||
|
||||
BOOL MainWindow::WinRegisterClass(WNDCLASS *pwc)
|
||||
{
|
||||
|
@ -28,19 +61,6 @@ BOOL MainWindow::WinRegisterClass(WNDCLASS *pwc)
|
|||
return __super::WinRegisterClass(pwc);
|
||||
}
|
||||
|
||||
BOOL EnableCloseButton(const HWND hwnd, const BOOL bState)
|
||||
{
|
||||
HMENU hMenu;
|
||||
UINT dwExtra;
|
||||
|
||||
if (hwnd == NULL)
|
||||
return FALSE;
|
||||
if ((hMenu = GetSystemMenu(hwnd, FALSE)) == NULL)
|
||||
return FALSE;
|
||||
dwExtra = bState ? MF_ENABLED : (MF_DISABLED | MF_GRAYED);
|
||||
return EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND | dwExtra) != -1;
|
||||
}
|
||||
|
||||
LRESULT MainWindow::OnCreate()
|
||||
{
|
||||
NONCLIENTMETRICS ncmMetrics = { sizeof(NONCLIENTMETRICS) };
|
||||
|
@ -52,10 +72,6 @@ LRESULT MainWindow::OnCreate()
|
|||
hFont = CreateFontIndirect(&ncmMetrics.lfMessageFont);
|
||||
rgbWindowBackground = GetSysColor(COLOR_WINDOW);
|
||||
hBrush = GetSysColorBrush(COLOR_WINDOW);
|
||||
hKeyboardLayout = (HBITMAP) LoadImage(GetInstance(), MAKEINTRESOURCE(RID_KEYBOARD),
|
||||
IMAGE_BITMAP, 0, 0, LR_LOADTRANSPARENT);
|
||||
hdcKeyboard = CreateCompatibleDC(GetDC(m_hwnd));
|
||||
SelectBitmap(hdcKeyboard, hKeyboardLayout);
|
||||
|
||||
// Children
|
||||
m_volumeLabel = CreateWindow(WC_STATIC, L"Volume:",
|
||||
|
@ -110,57 +126,64 @@ LRESULT MainWindow::OnCreate()
|
|||
if (midiOutOpen(&m_midi, 0, NULL, NULL, CALLBACK_NULL) != MMSYSERR_NOERROR)
|
||||
MessageBox(m_hwnd, L"Failed to open MIDI device!", L"Fatal Error", MB_ICONERROR);
|
||||
|
||||
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;
|
||||
PostMessage(m_hwnd, WM_INPUTLANGCHANGE, 0, 0);
|
||||
this->piano = 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));
|
||||
|
||||
SetTimer(m_hwnd, 0xDE00, 1000, NULL);
|
||||
{
|
||||
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:
|
||||
|
|
540
src/PianoControl.cpp
Normal file
540
src/PianoControl.cpp
Normal file
|
@ -0,0 +1,540 @@
|
|||
#include <PianoControl.hpp>
|
||||
|
||||
#include <windowsx.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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;
|
||||
}
|
Loading…
Reference in a new issue