Skip to content

Commit 3f20704

Browse files
committed
Added line numbering to SQLite/CEdit. Minor UI enhancements such as disabling the execute button while a query is executing, as well as the addition of the Alt+X keyboard shortcut to exit the application. Ctrl+O rebind to Open Database on application start temporarily reverted back to Open Query pending investigation into smarter behavior. Incremented internal version to 0.3.0 in preparation for eventual release.
1 parent 1d3f3ef commit 3f20704

1 file changed

Lines changed: 129 additions & 16 deletions

File tree

src/sqlite-ce-edit/main.c

Lines changed: 129 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -58,17 +58,22 @@ static HWND g_hwndCB;
5858
static HWND g_hwndStatus;
5959
static HMENU g_hMenu;
6060
static HACCEL g_hAccel;
61+
static HBRUSH g_hBrushWhite = NULL;
6162
static HWND g_hwndQuery; /* SQL input */
6263
static HWND g_hwndResult; /* Results output */
64+
static HWND g_hwndLineNum; /* Line number gutter */
6365
static sqlite *g_db = NULL;
6466
static wchar_t g_szDbPath[MAX_PATH] = {0};
6567
static HFONT g_hFontQuery = NULL;
6668
static HFONT g_hFontResult = NULL;
6769
static int g_fontSizes[] = {10, 12, 14, 16};
6870
static int g_fontSizeQuery = 2; /* Index into g_fontSizes, default 14 */
6971
static int g_fontSizeResult = 2;
72+
static int g_showLineNumbers = 1; /* 1 = show line number gutter */
73+
static int g_lineNumWidth = 0; /* Width of line number gutter, calculated on first use */
7074
static WNDPROC g_pfnQueryProc; /* Original query edit proc */
7175
static WNDPROC g_pfnResultProc; /* Original result edit proc */
76+
static WNDPROC g_pfnLineNumProc; /* Original line number edit proc */
7277

7378
/* Output buffer */
7479
static char g_szOutput[32000];
@@ -78,7 +83,7 @@ static int g_nOutput = 0;
7883
** Version
7984
**============================================================================*/
8085

81-
#define SQLITECEDIT_VERSION L"0.2.0"
86+
#define SQLITECEDIT_VERSION L"0.3.0"
8287

8388
/*============================================================================
8489
** Menu IDs
@@ -130,14 +135,55 @@ static void UpdateLineCount(void) {
130135
SendMessageW(g_hwndStatus, SB_SETTEXTW, 1, (LPARAM)buf);
131136
}
132137

138+
static void UpdateLineNumbers(void) {
139+
wchar_t buf[4096];
140+
int i, total, firstVisible, pos = 0;
141+
if (!g_showLineNumbers || !g_hwndLineNum || !g_hFontQuery) return;
142+
total = (int)SendMessage(g_hwndQuery, EM_GETLINECOUNT, 0, 0);
143+
144+
/* Auto-size gutter width based on line count */
145+
{
146+
HDC hdc = GetDC(g_hwndLineNum);
147+
HFONT hOld = (HFONT)SelectObject(hdc, g_hFontQuery);
148+
SIZE sz;
149+
wchar_t numBuf[16];
150+
int newWidth;
151+
wsprintfW(numBuf, L"%d", total);
152+
GetTextExtentPoint32W(hdc, numBuf, lstrlenW(numBuf), &sz);
153+
newWidth = sz.cx + 10; /* padding + border */
154+
if (newWidth < 20) newWidth = 20; /* minimum width */
155+
SelectObject(hdc, hOld);
156+
ReleaseDC(g_hwndLineNum, hdc);
157+
if (newWidth != g_lineNumWidth) {
158+
g_lineNumWidth = newWidth;
159+
SendMessage(g_hwndMain, WM_SIZE, 0, 0);
160+
UpdateWindow(g_hwndMain);
161+
}
162+
}
163+
164+
firstVisible = (int)SendMessage(g_hwndQuery, EM_GETFIRSTVISIBLELINE, 0, 0);
165+
for (i = firstVisible + 1; i <= total && pos < 4000; i++) {
166+
pos += wsprintfW(buf + pos, L"%d\r\n", i);
167+
}
168+
buf[pos] = 0;
169+
SetWindowTextW(g_hwndLineNum, buf);
170+
}
171+
172+
static void SyncLineNumScroll(void) {
173+
if (!g_showLineNumbers || !g_hwndLineNum) return;
174+
UpdateLineNumbers();
175+
}
176+
133177
static void SwitchView(int mode) {
134178
g_viewMode = mode;
135179
ShowWindow(g_hwndQuery, mode == 0 ? SW_SHOW : SW_HIDE);
180+
if (g_hwndLineNum) ShowWindow(g_hwndLineNum, (mode == 0 && g_showLineNumbers) ? SW_SHOW : SW_HIDE);
136181
ShowWindow(g_hwndResult, mode == 1 ? SW_SHOW : SW_HIDE);
137182
SetFocus(mode == 0 ? g_hwndQuery : g_hwndResult);
138-
if (mode == 0)
183+
if (mode == 0) {
139184
UpdateLineCount();
140-
else
185+
UpdateLineNumbers();
186+
} else
141187
SendMessageW(g_hwndStatus, SB_SETTEXTW, 1, (LPARAM)g_lastResultStatus);
142188
}
143189

@@ -167,6 +213,8 @@ static void UpdateQueryFont(void) {
167213
lstrcpyW(lf.lfFaceName, L"Courier New");
168214
g_hFontQuery = CreateFontIndirectW(&lf);
169215
SendMessage(g_hwndQuery, WM_SETFONT, (WPARAM)g_hFontQuery, TRUE);
216+
if (g_hwndLineNum)
217+
SendMessage(g_hwndLineNum, WM_SETFONT, (WPARAM)g_hFontQuery, TRUE);
170218
if (hOld) DeleteObject(hOld);
171219
}
172220

@@ -186,6 +234,8 @@ static void CycleFontSize(void) {
186234
if (g_viewMode == 0) {
187235
g_fontSizeQuery = (g_fontSizeQuery + 1) % 4;
188236
UpdateQueryFont();
237+
SendMessage(g_hwndQuery, EM_SCROLLCARET, 0, 0);
238+
UpdateLineNumbers();
189239
} else {
190240
g_fontSizeResult = (g_fontSizeResult + 1) % 4;
191241
UpdateResultFont();
@@ -267,8 +317,21 @@ static void DoFindNext(void); /* Forward declaration */
267317
static void DoOpenQuery(void); /* Forward declaration */
268318
static void DoSaveQuery(void); /* Forward declaration */
269319

320+
/* Subclass proc for line number gutter - blocks all input */
321+
static LRESULT CALLBACK LineNumProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
322+
if (msg == WM_CHAR || msg == WM_KEYDOWN || msg == WM_LBUTTONDOWN ||
323+
msg == WM_RBUTTONDOWN || msg == WM_LBUTTONDBLCLK)
324+
return 0;
325+
return CallWindowProc(g_pfnLineNumProc, hwnd, msg, wParam, lParam);
326+
}
327+
270328
/* Subclass proc for query edit - catches Ctrl+Enter */
271329
static LRESULT CALLBACK QueryEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
330+
/* Alt+X - Exit */
331+
if (msg == WM_SYSKEYDOWN && wParam == 'X') {
332+
DestroyWindow(g_hwndMain);
333+
return 0;
334+
}
272335
/* Clear hint on focus */
273336
if (msg == WM_SETFOCUS && g_showingHint) {
274337
SetWindowTextW(hwnd, L"");
@@ -364,11 +427,19 @@ static LRESULT CALLBACK QueryEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM
364427
/* Update line count on keyup and scroll caret into view for navigation keys */
365428
if (msg == WM_KEYUP) {
366429
UpdateLineCount();
430+
UpdateLineNumbers();
367431
if (wParam == VK_PRIOR || wParam == VK_NEXT || wParam == VK_HOME || wParam == VK_END)
368432
SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
369433
}
370-
if (msg == WM_LBUTTONUP)
434+
if (msg == WM_LBUTTONUP) {
371435
UpdateLineCount();
436+
UpdateLineNumbers();
437+
}
438+
if (msg == WM_VSCROLL) {
439+
LRESULT r = CallWindowProc(g_pfnQueryProc, hwnd, msg, wParam, lParam);
440+
SyncLineNumScroll();
441+
return r;
442+
}
372443
/* Clear search mode and suppress beeps on typing */
373444
if (msg == WM_CHAR) {
374445
if (GetKeyState(VK_CONTROL) < 0) {
@@ -377,6 +448,7 @@ static LRESULT CALLBACK QueryEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM
377448
LRESULT r = CallWindowProc(g_pfnQueryProc, hwnd, msg, wParam, lParam);
378449
SendMessage(hwnd, EM_SCROLLCARET, 0, 0);
379450
UpdateLineCount();
451+
UpdateLineNumbers();
380452
return r;
381453
}
382454
/* Ctrl+C=3, Ctrl+X=24 - pass through */
@@ -389,6 +461,9 @@ static LRESULT CALLBACK QueryEditProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM
389461
return 0;
390462
g_searchMode = 0; /* Any typing exits search mode */
391463
}
464+
/* Suppress beep for Alt+X (handled by accelerator) */
465+
if (msg == WM_SYSCHAR && (wParam == 'x' || wParam == 'X'))
466+
return 0;
392467
return CallWindowProc(g_pfnQueryProc, hwnd, msg, wParam, lParam);
393468
}
394469

@@ -620,6 +695,11 @@ static void ExecuteQuery(void) {
620695
return;
621696
}
622697

698+
/* Disable Execute while running */
699+
EnableMenuItem(g_hMenu, IDM_EXECUTE, MF_GRAYED);
700+
SendMessage(g_hwndCB, TB_ENABLEBUTTON, IDM_EXECUTE, FALSE);
701+
UpdateWindow(g_hwndCB);
702+
623703
/* Check for selection */
624704
SendMessage(g_hwndQuery, EM_GETSEL, (WPARAM)&selStart, (LPARAM)&selEnd);
625705

@@ -799,6 +879,10 @@ static void ExecuteQuery(void) {
799879
FlushOutput();
800880
UpdateDbSize();
801881

882+
/* Re-enable Execute */
883+
EnableMenuItem(g_hMenu, IDM_EXECUTE, MF_ENABLED);
884+
SendMessage(g_hwndCB, TB_ENABLEBUTTON, IDM_EXECUTE, TRUE);
885+
802886
/* Switch to results view (unless error - stay in query to show cursor) */
803887
if (!hadError) {
804888
SwitchView(1);
@@ -1880,6 +1964,8 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
18801964
HMENU hMenu, hFile, hQuery, hView;
18811965
TBBUTTON tbButtons[11];
18821966

1967+
g_hBrushWhite = CreateSolidBrush(RGB(255, 255, 255));
1968+
18831969
/* Command bar with menus */
18841970
g_hwndCB = CommandBar_Create(g_hInst, hwnd, 1);
18851971

@@ -1896,7 +1982,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
18961982
AppendMenuW(hFile, MF_STRING, IDM_EXPORTDB, L"Export &Database...");
18971983
AppendMenuW(hFile, MF_STRING, IDM_IMPORTCSV, L"&Import CSV...");
18981984
AppendMenuW(hFile, MF_SEPARATOR, 0, NULL);
1899-
AppendMenuW(hFile, MF_STRING, IDM_EXIT, L"E&xit");
1985+
AppendMenuW(hFile, MF_STRING, IDM_EXIT, L"E&xit\tAlt+X");
19001986
AppendMenuW(hMenu, MF_POPUP, (UINT)hFile, L"&File");
19011987

19021988
hView = CreatePopupMenu();
@@ -2000,18 +2086,28 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
20002086
{
20012087
int parts[2] = {120, -1};
20022088
RECT rcStatus;
2003-
int sbHeight, editHeight;
2089+
int sbHeight, editHeight, queryLeft;
20042090
SendMessage(g_hwndStatus, SB_SETPARTS, 2, (LPARAM)parts);
20052091
GetWindowRect(g_hwndStatus, &rcStatus);
20062092
sbHeight = rcStatus.bottom - rcStatus.top;
20072093
editHeight = rc.bottom - cbHeight - sbHeight;
2094+
queryLeft = g_showLineNumbers ? g_lineNumWidth : 0;
2095+
2096+
/* Line number gutter */
2097+
g_hwndLineNum = CreateWindowW(
2098+
L"EDIT", L"",
2099+
WS_CHILD | WS_BORDER | (g_showLineNumbers ? WS_VISIBLE : 0) | ES_MULTILINE | ES_RIGHT,
2100+
0, cbHeight, g_lineNumWidth, editHeight,
2101+
hwnd, (HMENU)1004, g_hInst, NULL);
2102+
SendMessage(g_hwndLineNum, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(0, 2));
2103+
g_pfnLineNumProc = (WNDPROC)SetWindowLong(g_hwndLineNum, GWL_WNDPROC, (LONG)LineNumProc);
20082104

2009-
/* Query input - full height */
2105+
/* Query input */
20102106
g_hwndQuery = CreateWindowW(
20112107
L"EDIT", L"",
20122108
WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL |
20132109
ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN,
2014-
0, cbHeight, rc.right, editHeight,
2110+
queryLeft, cbHeight, rc.right - queryLeft, editHeight,
20152111
hwnd, (HMENU)1001, g_hInst, NULL);
20162112

20172113
/* Subclass to catch Ctrl+Enter */
@@ -2035,13 +2131,15 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
20352131

20362132
/* Start in query view */
20372133
g_viewMode = 0;
2134+
UpdateLineNumbers();
2135+
SendMessage(hwnd, WM_SIZE, 0, 0); /* Force layout after line number width calculated */
20382136

20392137
return 0;
20402138
}
20412139

20422140
case WM_SIZE: {
20432141
RECT rc, rcStatus;
2044-
int cbHeight, sbHeight, editHeight;
2142+
int cbHeight, sbHeight, editHeight, queryLeft;
20452143

20462144
SendMessage(g_hwndStatus, WM_SIZE, 0, 0); /* Auto-position status bar */
20472145
GetWindowRect(g_hwndStatus, &rcStatus);
@@ -2050,9 +2148,14 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
20502148
cbHeight = CommandBar_Height(g_hwndCB);
20512149
GetClientRect(hwnd, &rc);
20522150
editHeight = rc.bottom - cbHeight - sbHeight;
2151+
queryLeft = g_showLineNumbers ? g_lineNumWidth : 0;
20532152

2054-
/* Both panes same size, only one visible at a time */
2055-
MoveWindow(g_hwndQuery, 0, cbHeight, rc.right, editHeight, TRUE);
2153+
/* Line number gutter */
2154+
if (g_hwndLineNum)
2155+
MoveWindow(g_hwndLineNum, 0, cbHeight, g_lineNumWidth, editHeight, TRUE);
2156+
2157+
/* Query pane - offset if line numbers shown */
2158+
MoveWindow(g_hwndQuery, queryLeft, cbHeight, rc.right - queryLeft, editHeight, TRUE);
20562159
MoveWindow(g_hwndResult, 0, cbHeight, rc.right, editHeight, TRUE);
20572160
return 0;
20582161
}
@@ -2062,9 +2165,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
20622165
case IDM_NEW: DoFileNew(); break;
20632166
case IDM_OPEN: DoFileOpen(); break;
20642167
case IDM_CLOSE: CloseDatabase(); break;
2065-
case IDM_OPENQUERY:
2066-
if (g_showingHint) DoFileOpen(); else DoOpenQuery();
2067-
break;
2168+
case IDM_OPENQUERY: DoOpenQuery(); break;
20682169
case IDM_SAVEQUERY: DoSaveQuery(); break;
20692170
case IDM_EXPORTCSV: DoExportCSV(); break;
20702171
case IDM_EXPORTDB: DoExportDb(); break;
@@ -2102,10 +2203,20 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
21022203
return 0;
21032204

21042205
case WM_CTLCOLOREDIT:
2105-
SetBkColor((HDC)wParam, RGB(255, 255, 255));
2106-
return (LRESULT)GetStockObject(WHITE_BRUSH);
2206+
case WM_CTLCOLORSTATIC:
2207+
if ((HWND)lParam == g_hwndLineNum || (HWND)lParam == g_hwndQuery || (HWND)lParam == g_hwndResult) {
2208+
SetBkColor((HDC)wParam, RGB(255, 255, 255));
2209+
return (LRESULT)g_hBrushWhite;
2210+
}
2211+
break;
21072212

21082213
case WM_KEYDOWN:
2214+
case WM_SYSKEYDOWN:
2215+
/* Alt+X - Exit */
2216+
if (wParam == 'X' && GetKeyState(VK_MENU) < 0) {
2217+
DestroyWindow(hwnd);
2218+
return 0;
2219+
}
21092220
/* Global shortcuts (when command bar has focus) */
21102221
if (GetKeyState(VK_CONTROL) < 0) {
21112222
if (wParam == 'O') { DoOpenQuery(); return 0; }
@@ -2124,6 +2235,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
21242235
CloseDatabase();
21252236
if (g_hFontQuery) DeleteObject(g_hFontQuery);
21262237
if (g_hFontResult) DeleteObject(g_hFontResult);
2238+
if (g_hBrushWhite) DeleteObject(g_hBrushWhite);
21272239
CommandBar_Destroy(g_hwndCB);
21282240
PostQuitMessage(0);
21292241
return 0;
@@ -2160,6 +2272,7 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPWSTR lpCmd, int nShow) {
21602272
accel[nAccel].fVirt = FVIRTKEY; accel[nAccel].key = VK_F3; accel[nAccel].cmd = IDM_FINDNEXT; nAccel++;
21612273
accel[nAccel].fVirt = FVIRTKEY; accel[nAccel].key = VK_F5; accel[nAccel].cmd = IDM_EXECUTE; nAccel++;
21622274
accel[nAccel].fVirt = FVIRTKEY; accel[nAccel].key = VK_F6; accel[nAccel].cmd = IDM_VIEWRESULT; nAccel++;
2275+
accel[nAccel].fVirt = FALT | FVIRTKEY; accel[nAccel].key = 'X'; accel[nAccel].cmd = IDM_EXIT; nAccel++;
21632276
g_hAccel = CreateAcceleratorTableW(accel, nAccel);
21642277

21652278
SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWork, 0);

0 commit comments

Comments
 (0)