@@ -58,17 +58,22 @@ static HWND g_hwndCB;
5858static HWND g_hwndStatus ;
5959static HMENU g_hMenu ;
6060static HACCEL g_hAccel ;
61+ static HBRUSH g_hBrushWhite = NULL ;
6162static HWND g_hwndQuery ; /* SQL input */
6263static HWND g_hwndResult ; /* Results output */
64+ static HWND g_hwndLineNum ; /* Line number gutter */
6365static sqlite * g_db = NULL ;
6466static wchar_t g_szDbPath [MAX_PATH ] = {0 };
6567static HFONT g_hFontQuery = NULL ;
6668static HFONT g_hFontResult = NULL ;
6769static int g_fontSizes [] = {10 , 12 , 14 , 16 };
6870static int g_fontSizeQuery = 2 ; /* Index into g_fontSizes, default 14 */
6971static 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 */
7074static WNDPROC g_pfnQueryProc ; /* Original query edit proc */
7175static WNDPROC g_pfnResultProc ; /* Original result edit proc */
76+ static WNDPROC g_pfnLineNumProc ; /* Original line number edit proc */
7277
7378/* Output buffer */
7479static 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+
133177static 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 */
267317static void DoOpenQuery (void ); /* Forward declaration */
268318static 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 */
271329static 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