summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonas Kümmerlin <jonas@kuemmerlin.eu>2019-01-01 22:12:51 +0100
committerJonas Kümmerlin <jonas@kuemmerlin.eu>2019-01-01 22:12:51 +0100
commit261902fcae887249ffa03114e49afdef85fb4442 (patch)
treefbc81a4581dc762d7194f7ee5b9b7b2790319dbf
parentdbd2fb78dec385f0abfe07fd4609dc8f444bdefd (diff)
first try at combobox implementation
-rw-r--r--Makefile.mingw5
-rw-r--r--Makefile.vc65
-rw-r--r--Makefile.vc6-ansi5
-rw-r--r--alf/alf.cpp36
-rw-r--r--alf/alf.h31
-rw-r--r--alf/alfcombobox.cpp270
-rw-r--r--alf/alfcompat.cpp11
-rw-r--r--alf/alfcompat.h2
-rw-r--r--alf/alfpriv.h7
-rw-r--r--widgetfactory.cpp22
10 files changed, 377 insertions, 17 deletions
diff --git a/Makefile.mingw b/Makefile.mingw
index 796496a..4bc45ce 100644
--- a/Makefile.mingw
+++ b/Makefile.mingw
@@ -4,12 +4,15 @@ CXX = i686-w64-mingw32-c++
CFLAGS = -std=c++98 -Wall -Wextra -mwindows -municode -DUNICODE -D_UNICODE -fno-exceptions -fno-rtti -gstabs
LDFLAGS = -luser32 -lcomctl32 -lshell32 -lrpcrt4 -lversion -static
-out/widgetfactory.exe: out/widgetfactory.o out/alfbutton.o out/alfcompat.o out/alf.o out/alfdpiaware.o out/alfedit.o out/alflabel.o
+out/widgetfactory.exe: out/widgetfactory.o out/alfbutton.o out/alfcombobox.o out/alfcompat.o out/alf.o out/alfdpiaware.o out/alfedit.o out/alflabel.o
$(CXX) $(CFLAGS) -o $@ $^ $(LDFLAGS)
out/alfbutton.o: alf/alfbutton.cpp alf/alfcompat.h alf/alf.h alf/alflist.h alf/alfpriv.h
$(CXX) $(CFLAGS) -c -o $@ $<
+out/alfcombobox.o: alf/alfcombobox.cpp alf/alfcompat.h alf/alf.h alf/alflist.h alf/alfpriv.h
+ $(CXX) $(CFLAGS) -c -o $@ $<
+
out/alfcompat.o: alf/alfcompat.cpp alf/alfcompat.h alf/alf.h alf/alflist.h alf/alfpriv.h
$(CXX) $(CFLAGS) -c -o $@ $<
diff --git a/Makefile.vc6 b/Makefile.vc6
index bb27b3f..c0ab753 100644
--- a/Makefile.vc6
+++ b/Makefile.vc6
@@ -4,12 +4,15 @@ CXX = cl.exe
CFLAGS = -O2 -GA -W3 -DUNICODE -D_UNICODE -D_WIN32=0x0501 -D_WIN32_WINNT=0x0501 -D_WIN32_IE=0x0501 -nologo
LDFLAGS = /link unicows.lib kernel32.lib user32.lib comctl32.lib shell32.lib gdi32.lib version.lib rpcrt4.lib
-out/widgetfactory.exe: out/widgetfactory.obj out/alfbutton.obj out/alfcompat.obj out/alf.obj out/alfdpiaware.obj out/alfedit.obj out/alflabel.obj
+out/widgetfactory.exe: out/widgetfactory.obj out/alfbutton.obj out/alfcombobox.obj out/alfcompat.obj out/alf.obj out/alfdpiaware.obj out/alfedit.obj out/alflabel.obj
$(CXX) $(CFLAGS) -Fe$@ $** $(LDFLAGS)
out/alfbutton.obj: alf/alfbutton.cpp alf/alfcompat.h alf/alf.h alf/alflist.h alf/alfpriv.h
$(CXX) $(CFLAGS) -c -Fo$@ alf/alfbutton.cpp
+out/alfcombobox.obj: alf/alfcombobox.cpp alf/alfcompat.h alf/alf.h alf/alflist.h alf/alfpriv.h
+ $(CXX) $(CFLAGS) -c -Fo$@ alf/alfcombobox.cpp
+
out/alfcompat.obj: alf/alfcompat.cpp alf/alfcompat.h alf/alf.h alf/alflist.h alf/alfpriv.h
$(CXX) $(CFLAGS) -c -Fo$@ alf/alfcompat.cpp
diff --git a/Makefile.vc6-ansi b/Makefile.vc6-ansi
index 4e64419..569e9a5 100644
--- a/Makefile.vc6-ansi
+++ b/Makefile.vc6-ansi
@@ -4,12 +4,15 @@ CXX = cl.exe
CFLAGS = -O2 -GA -W3 -D_WIN32=0x0501 -D_WIN32_WINNT=0x0501 -D_WIN32_IE=0x0501 -nologo
LDFLAGS = /link kernel32.lib user32.lib comctl32.lib shell32.lib gdi32.lib version.lib rpcrt4.lib
-out/widgetfactory.exe: out/widgetfactory.obj out/alfbutton.obj out/alfcompat.obj out/alf.obj out/alfdpiaware.obj out/alfedit.obj out/alflabel.obj
+out/widgetfactory.exe: out/widgetfactory.obj out/alfbutton.obj out/alfcombobox.obj out/alfcompat.obj out/alf.obj out/alfdpiaware.obj out/alfedit.obj out/alflabel.obj
$(CXX) $(CFLAGS) -Fe$@ $** $(LDFLAGS)
out/alfbutton.obj: alf/alfbutton.cpp alf/alfcompat.h alf/alf.h alf/alflist.h alf/alfpriv.h
$(CXX) $(CFLAGS) -c -Fo$@ alf/alfbutton.cpp
+out/alfcombobox.obj: alf/alfcombobox.cpp alf/alfcompat.h alf/alf.h alf/alflist.h alf/alfpriv.h
+ $(CXX) $(CFLAGS) -c -Fo$@ alf/alfcombobox.cpp
+
out/alfcompat.obj: alf/alfcompat.cpp alf/alfcompat.h alf/alf.h alf/alflist.h alf/alfpriv.h
$(CXX) $(CFLAGS) -c -Fo$@ alf/alfcompat.cpp
diff --git a/alf/alf.cpp b/alf/alf.cpp
index ae6c8f9..9d1379c 100644
--- a/alf/alf.cpp
+++ b/alf/alf.cpp
@@ -519,10 +519,31 @@ ALF_CreateApplication(HINSTANCE hInstance)
InitCommonControlsEx(&icc);
app->compatFn = ALF_CreateCompatFuncTable();
+ ALF_RegisterComboClass(app);
return app;
}
+
+void
+ALF_BuildRandomClassName(const TCHAR *prefix, TCHAR *buf)
+{
+ UUID uuid;
+ UuidCreate(&uuid);
+
+#ifdef UNICODE
+ unsigned short *uuidstr = NULL;
+#else
+ unsigned char *uuidstr = NULL;
+#endif
+ UuidToString(&uuid, &uuidstr);
+
+ lstrcpy(buf, prefix);
+ lstrcat(buf, (LPCTSTR)uuidstr);
+
+ RpcStringFree(&uuidstr);
+}
+
LPTSTR
ALF_RegisterWindowClass(ALFAPP app, const ALFWindowClassParams *params)
{
@@ -535,20 +556,7 @@ ALF_RegisterWindowClass(ALFAPP app, const ALFWindowClassParams *params)
ZeroMemory(classNameBuf, sizeof(classNameBuf));
classNamePtr = classNameBuf;
- UUID uuid;
- UuidCreate(&uuid);
-
-#ifdef UNICODE
- unsigned short *uuidstr = NULL;
-#else
- unsigned char *uuidstr = NULL;
-#endif
- UuidToString(&uuid, &uuidstr);
-
- lstrcpy(classNameBuf, TEXT("ALFWindow."));
- lstrcat(classNameBuf, (LPCTSTR)uuidstr);
-
- RpcStringFree(&uuidstr);
+ ALF_BuildRandomClassName(TEXT("ALFWindow."), classNameBuf);
}
cls.style = params->classStyle;
diff --git a/alf/alf.h b/alf/alf.h
index 6d36aff..c6a7b77 100644
--- a/alf/alf.h
+++ b/alf/alf.h
@@ -142,6 +142,37 @@ ALF_SetModalResult(HWND win, int result);
int
ALF_GetModalResult(HWND win);
+// combo box
+
+HWND
+ALF_AddEditableComboBox(HWND win, WORD id, UINT x, UINT y, const TCHAR *defaultText);
+
+HWND
+ALF_AddSelectionOnlyComboBox(HWND win, WORD id, UINT x, UINT y);
+
+int /* index */
+ALF_ComboBoxAddString(HWND combo, const TCHAR *text);
+
+void
+ALF_ComboBoxInsertString(HWND combo, int index, const TCHAR *text);
+
+void
+ALF_ComboBoxRemoveString(HWND combo, int index);
+
+int
+ALF_ComboBoxCurrentIndex(HWND combo);
+
+void
+ALF_ComboBoxSetCurrentIndex(HWND combo);
+
+// NOTE: call HeapFree
+TCHAR *
+ALF_ComboBoxCurrentText(HWND combo);
+
+void
+ALF_ComboBoxSetText(HWND combo, const TCHAR *text);
+
+
#ifdef __cplusplus
} // extern C
#endif
diff --git a/alf/alfcombobox.cpp b/alf/alfcombobox.cpp
new file mode 100644
index 0000000..43c547a
--- /dev/null
+++ b/alf/alfcombobox.cpp
@@ -0,0 +1,270 @@
+#include "alfpriv.h"
+
+typedef struct {
+ ALFAPP app;
+ DWORD comboStyle;
+} ALFComboCreateParams;
+
+static int
+ALF__ComboItemHeight(HWND hwnd)
+{
+ int height = 0;
+ HDC hDc = GetDC(hwnd);
+ HFONT font = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0);
+ if (font) {
+ HFONT oldfont = SelectFont(hDc, font);
+
+ TEXTMETRIC tm;
+ ZeroMemory(&tm, sizeof(tm));
+
+ if (GetTextMetrics(hDc, &tm)) {
+ height = tm.tmHeight;
+ }
+
+ SelectFont(hDc, oldfont);
+ }
+
+ ReleaseDC(hwnd, hDc);
+ return height;
+}
+
+static LRESULT CALLBACK
+ALF__ComboWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ if (uMsg == WM_NCCREATE) {
+ ALFComboCreateParams *params = (ALFComboCreateParams*)((CREATESTRUCT*)lParam)->lpCreateParams;
+
+ SetWindowLongPtr(hwnd, 0, (LONG_PTR)params->app);
+ }
+
+ ALFAPP app = (ALFAPP)GetWindowLongPtr(hwnd, 0);
+ HWND hwndChild = (HWND)GetWindowLongPtr(hwnd, sizeof(void*));
+
+ if (uMsg == WM_CREATE) {
+ ALFComboCreateParams *params = (ALFComboCreateParams*)((CREATESTRUCT*)lParam)->lpCreateParams;
+ HWND hwndCombo = CreateWindowEx(0,
+ TEXT("COMBOBOX"),
+ TEXT(""),
+ WS_CHILD | WS_VISIBLE | WS_TABSTOP | CBS_OWNERDRAWFIXED | CBS_HASSTRINGS | params->comboStyle,
+ 0, 0, 0, 200 /* FIXME needed for commctl32 v5, what is the best value here? */,
+ hwnd,
+ (HMENU) GetWindowLongPtrW(hwnd, GWLP_ID),
+ ((CREATESTRUCT*)lParam)->hInstance,
+ NULL);
+
+ SetWindowLongPtr(hwnd, sizeof(void*), (LONG_PTR)hwndCombo);
+ }
+ if (uMsg == WM_ENABLE && hwndChild) {
+ EnableWindow(hwndChild, (BOOL)wParam);
+ return 0;
+ }
+ if (uMsg == WM_SETTEXT && hwndChild) {
+ return SendMessage(hwndChild, WM_SETTEXT, wParam, lParam);
+ }
+ if (uMsg == WM_GETTEXTLENGTH && hwndChild) {
+ return SendMessage(hwndChild, WM_GETTEXTLENGTH, wParam, lParam);
+ }
+ if (uMsg == WM_GETTEXT && hwndChild) {
+ return SendMessage(hwndChild, WM_GETTEXT, wParam, lParam);
+ }
+ if (uMsg == WM_SETFONT && hwndChild) {
+ SendMessage(hwndChild, WM_SETFONT, wParam, lParam);
+
+ int h = ALF__ComboItemHeight(hwnd);
+ if (h)
+ SendMessage(hwndChild, CB_SETITEMHEIGHT, (WPARAM)0, h);
+
+ return 0;
+ }
+ if (uMsg == WM_GETFONT && hwndChild) {
+ return SendMessage(hwndChild, WM_GETFONT, wParam, lParam);
+ }
+ if (uMsg == CB_ADDSTRING && hwndChild) {
+ return SendMessage(hwndChild, CB_ADDSTRING, wParam, lParam);
+ }
+
+ if (uMsg == ALF_WM_QUERYSIZE) {
+ HDC hDc = GetDC(hwnd);
+ HFONT font = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0);
+
+ if (font) {
+ HFONT oldfont = SelectFont(hDc, font);
+
+ TEXTMETRIC tm;
+ ZeroMemory(&tm, sizeof(tm));
+
+ if (GetTextMetrics(hDc, &tm)) {
+ SIZE *ps = (SIZE*)lParam;
+ if (!ps->cx) {
+ ps->cx = ALF_CentipointsToPixels(GetParent(hwnd), 12000);
+ }
+
+ if (!ps->cy) {
+ ps->cy = tm.tmHeight + 2*app->compatFn->GetSystemMetricsForDpi(
+ SM_CYEDGE, ALF_CentipointsToPixels(GetParent(hwnd), 7200))
+ + 4 /* padding internal to the edit control */
+ + 2 /* external padding to line up with themed button */;
+ }
+ }
+
+ SelectFont(hDc, oldfont);
+ }
+
+ ReleaseDC(hwnd, hDc);
+ return 0;
+ }
+
+ if (uMsg == WM_MEASUREITEM) {
+ PMEASUREITEMSTRUCT lpmis = (LPMEASUREITEMSTRUCT) lParam;
+
+ int h = ALF__ComboItemHeight(hwnd);
+ if (h)
+ lpmis->itemHeight = h;
+
+ return TRUE;
+ }
+
+ if (uMsg == WM_DRAWITEM) {
+ LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT) lParam;
+
+ if (lpdis->itemID != (UINT)-1) { // Empty item
+ DWORD clrForeground = SetTextColor(lpdis->hDC,
+ GetSysColor(lpdis->itemState & (ODS_SELECTED | ODS_FOCUS) ?
+ COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT));
+
+ DWORD clrBackground = SetBkColor(lpdis->hDC,
+ GetSysColor(lpdis->itemState & (ODS_SELECTED | ODS_FOCUS) ?
+ COLOR_HIGHLIGHT : COLOR_WINDOW));
+
+ LONG y = lpdis->rcItem.top;
+ LONG x = lpdis->rcItem.left;
+
+ // add left padding like an edit control
+ TEXTMETRIC tm;
+ ZeroMemory(&tm, sizeof(tm));
+ if (GetTextMetrics(lpdis->hDC, &tm)) {
+ if (tm.tmPitchAndFamily & (TMPF_VECTOR | TMPF_TRUETYPE))
+ x += ALF_GetAveCharWidth(lpdis->hDC) / 2;
+ }
+
+ // Get and display the text for the list item.
+ int len = (int)SendMessage(lpdis->hwndItem, CB_GETLBTEXTLEN, (WPARAM)lpdis->itemID, 0);
+ if (len != CB_ERR) {
+ TCHAR *buf = (TCHAR*)LocalAlloc(LPTR, (len+1) * sizeof(TCHAR));
+
+ SendMessage(lpdis->hwndItem, CB_GETLBTEXT,
+ lpdis->itemID, (LPARAM)buf);
+
+ ExtTextOut(lpdis->hDC, x, y,
+ ETO_CLIPPED | ETO_OPAQUE, &lpdis->rcItem,
+ buf, lstrlen(buf), NULL);
+
+ LocalFree(buf);
+ }
+
+ // Restore the previous colors.
+ SetTextColor(lpdis->hDC, clrForeground);
+ SetBkColor(lpdis->hDC, clrBackground);
+ }
+
+ // If the item has the focus, draw the focus rectangle.
+ if ((lpdis->itemState & ODS_FOCUS) && !(lpdis->itemState & ODS_NOFOCUSRECT))
+ DrawFocusRect(lpdis->hDC, &lpdis->rcItem);
+
+ return TRUE;
+ }
+
+ if (uMsg == WM_WINDOWPOSCHANGED && hwndChild) {
+ WINDOWPOS *pos = (WINDOWPOS *)lParam;
+ if (!(pos->flags & SWP_NOSIZE)) {
+ RECT r;
+ GetWindowRect(hwndChild, &r);
+ SetWindowPos(hwndChild, NULL, 0, 1, pos->cx, r.bottom - r.top, SWP_NOZORDER|SWP_NOACTIVATE);
+
+ SendMessage(hwndChild, CB_SETITEMHEIGHT, (WPARAM)-1, pos->cy - 8);
+
+ int h = ALF__ComboItemHeight(hwnd);
+ if (h)
+ SendMessage(hwndChild, CB_SETITEMHEIGHT, (WPARAM)0, h);
+ }
+ }
+
+ return DefWindowProc(hwnd, uMsg, wParam, lParam);
+}
+
+
+void
+ALF_RegisterComboClass(ALFAPP app)
+{
+ WNDCLASS cls;
+ ZeroMemory(&cls, sizeof(cls));
+
+ TCHAR classNameBuf[256];
+ ALF_BuildRandomClassName(TEXT("ALFComboBox."), classNameBuf);
+
+ cls.hInstance = app->hInstance;
+ cls.hCursor = LoadCursor(NULL, (LPTSTR)IDC_ARROW);
+ cls.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
+ cls.lpszClassName = classNameBuf;
+ cls.cbWndExtra = sizeof(void*)*2;
+ cls.lpfnWndProc = ALF__ComboWindowProc;
+
+ ATOM classatom = RegisterClass(&cls);
+ if (!classatom)
+ MessageBox(NULL, TEXT("FATAL: Could not register Combo class"), NULL, MB_OK);
+
+ app->comboClass = MAKEINTATOM(classatom);
+}
+
+static HWND
+ALF_InternalAddComboBox(HWND win, WORD id, UINT x, UINT y, DWORD style, const TCHAR *defaultText)
+{
+ ALFComboCreateParams cp;
+ cp.app = ALF_ApplicationFromWindow(win);
+ cp.comboStyle = style;
+
+ HWND hwndCombo = CreateWindowEx(WS_EX_CONTROLPARENT,
+ cp.app->comboClass,
+ TEXT(""),
+ WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN,
+ 0, 0, 0, 0,
+ win,
+ (HMENU)(int)id,
+ (HINSTANCE)GetWindowLongPtr(win, GWLP_HINSTANCE),
+ &cp);
+
+ if (defaultText)
+ SetWindowText(hwndCombo, defaultText);
+
+ ALFAddWidgetParams p;
+ ZeroMemory(&p, sizeof(p));
+ p.hwnd = hwndCombo;
+ p.x = x;
+ p.y = y;
+ p.width = 0;
+ p.height = 0;
+ p.flags = ALF_QUERYSIZE | ALF_MESSAGEFONT;
+
+ ALF_AddWidgetEx(win, &p);
+
+ return hwndCombo;
+}
+
+HWND
+ALF_AddEditableComboBox(HWND win, WORD id, UINT x, UINT y, const TCHAR *defaultText)
+{
+ return ALF_InternalAddComboBox(win, id, x, y, CBS_DROPDOWN, defaultText);
+}
+
+HWND
+ALF_AddSelectionOnlyComboBox(HWND win, WORD id, UINT x, UINT y)
+{
+ return ALF_InternalAddComboBox(win, id, x, y, CBS_DROPDOWNLIST, NULL);
+}
+
+int /* index */
+ALF_ComboBoxAddString(HWND combo, const TCHAR *text)
+{
+ return (int)SendMessage(combo, CB_ADDSTRING, 0, (LPARAM)text);
+}
+
diff --git a/alf/alfcompat.cpp b/alf/alfcompat.cpp
index e6a9851..269b9f4 100644
--- a/alf/alfcompat.cpp
+++ b/alf/alfcompat.cpp
@@ -123,3 +123,14 @@ ALF_CreateCompatFuncTable(void)
return compatfn;
}
+
+long
+ALF_GetAveCharWidth(HDC hdc)
+{
+ // see: HOWTO: Calculate Dialog Units When Not Using the System Font
+
+ SIZE s;
+ GetTextExtentPoint32A(hdc, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52, &s);
+
+ return (s.cx / 26 + 1) / 2;
+}
diff --git a/alf/alfcompat.h b/alf/alfcompat.h
index 8319dac..846590c 100644
--- a/alf/alfcompat.h
+++ b/alf/alfcompat.h
@@ -20,3 +20,5 @@ typedef struct {
ALFCompatFunctions *
ALF_CreateCompatFuncTable(void);
+long
+ALF_GetAveCharWidth(HDC hdc);
diff --git a/alf/alfpriv.h b/alf/alfpriv.h
index a398127..0d5be2f 100644
--- a/alf/alfpriv.h
+++ b/alf/alfpriv.h
@@ -61,7 +61,14 @@ typedef struct {
struct ALFAppPriv {
HINSTANCE hInstance;
ALFCompatFunctions *compatFn;
+ TCHAR *comboClass;
};
void
ALF_UpdateFontsPriv(HWND hwnd, ALFWindowPriv *priv);
+
+void
+ALF_RegisterComboClass(ALFAPP app);
+
+void
+ALF_BuildRandomClassName(const TCHAR *prefix, TCHAR *buf);
diff --git a/widgetfactory.cpp b/widgetfactory.cpp
index 6259f17..ec1c28a 100644
--- a/widgetfactory.cpp
+++ b/widgetfactory.cpp
@@ -7,6 +7,12 @@ enum {
ID_ED1,
ID_B1,
ID_B2,
+ ID_B3,
+ ID_B4,
+ ID_COMBO1,
+ ID_LBL4,
+ ID_COMBO2,
+ ID_LBL5,
ID__MAX
};
@@ -96,6 +102,22 @@ WinMain
ALF_AddButton(win, ID_B1, 2, 1, TEXT("&Go!"));
ALF_AddButton(win, ID_B2, 0, 2, TEXT("Oh m&y god,\r\nwho the hell cares?"));
+ ALF_AddLabel(win, ID_LBL4, 0, 3, TEXT("Editable Combo Box:"));
+ HWND hwndCombo1 = ALF_AddEditableComboBox(win, ID_COMBO1, 1, 3, TEXT("Hello!"));
+ ALF_AddButton(win, ID_B3, 2, 3, TEXT("Ok"));
+
+ ALF_ComboBoxAddString(hwndCombo1, TEXT("Hello World!"));
+ ALF_ComboBoxAddString(hwndCombo1, TEXT("Goodbye World!"));
+ ALF_ComboBoxAddString(hwndCombo1, TEXT("The quick brown fox jumps over the lazy dog"));
+
+ ALF_AddLabel(win, ID_LBL5, 0, 4, TEXT("Selection-Only Combo Box:"));
+ HWND hwndCombo2 = ALF_AddSelectionOnlyComboBox(win, ID_COMBO2, 1, 4);
+ ALF_AddButton(win, ID_B4, 2, 4, TEXT("Lol"));
+
+ ALF_ComboBoxAddString(hwndCombo2, TEXT("Hello World!"));
+ ALF_ComboBoxAddString(hwndCombo2, TEXT("Goodbye World!"));
+ ALF_ComboBoxAddString(hwndCombo2, TEXT("The quick brown fox jumps over the lazy dog"));
+
ALF_RecalculateLayout(win);
ALF_SetDefaultButton(win, ID_B1);