@@ -194,6 +194,10 @@ void OpenQueryFile(const wchar_t *path) {
194194 UpdateTitle ();
195195 UpdateLineNumbers ();
196196 LocalFree (wbuf );
197+ /* Switch to query view */
198+ if (g_viewMode != 0 ) {
199+ SendMessage (g_hwndMain , WM_COMMAND , IDM_VIEWQUERY , 0 );
200+ }
197201 }
198202 }
199203 if (buf ) LocalFree (buf );
@@ -274,7 +278,108 @@ void DoSaveQuery(void) {
274278}
275279
276280/*============================================================================
277- ** Export Results to CSV
281+ ** Export Results (CSV or Text based on filter selection)
282+ **============================================================================*/
283+
284+ void DoExportResults (void ) {
285+ CE_OPENFILENAME ofn ;
286+ wchar_t szFile [MAX_PATH ] = L"results" ;
287+ HANDLE hFile ;
288+ DWORD dwLen , dwWritten ;
289+ wchar_t * wbuf , * wp ;
290+ char * buf , * bp ;
291+ int needQuote , isCSV ;
292+
293+ memset (& ofn , 0 , sizeof (ofn ));
294+ ofn .lStructSize = sizeof (ofn );
295+ ofn .hwndOwner = g_hwndMain ;
296+ ofn .lpstrFile = szFile ;
297+ ofn .nMaxFile = MAX_PATH ;
298+ ofn .lpstrFilter = L"CSV Files (*.csv)\0*.csv\0Text Files (*.txt)\0*.txt\0All Files (*.*)\0*.*\0" ;
299+ ofn .lpstrDefExt = NULL ; /* We'll handle extension ourselves */
300+ ofn .lpstrTitle = L"Export Results" ;
301+ ofn .Flags = OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST ;
302+ ofn .nFilterIndex = 1 ;
303+
304+ if (!GetSaveFileNameW (& ofn )) return ;
305+
306+ /* Check if CSV (filter 1) or Text (filter 2+) */
307+ isCSV = (ofn .nFilterIndex == 1 );
308+
309+ /* Append extension if none present */
310+ {
311+ wchar_t * p = szFile + lstrlenW (szFile );
312+ wchar_t * dot = NULL ;
313+ while (p > szFile && * p != '\\' ) {
314+ if (* p == '.' ) dot = p ;
315+ p -- ;
316+ }
317+ if (!dot ) {
318+ lstrcatW (szFile , isCSV ? L".csv" : L".txt" );
319+ }
320+ }
321+
322+ dwLen = GetWindowTextLengthW (g_hwndResult );
323+ if (dwLen == 0 ) return ;
324+
325+ wbuf = (wchar_t * )LocalAlloc (LMEM_FIXED , (dwLen + 1 ) * sizeof (wchar_t ));
326+ buf = (char * )LocalAlloc (LMEM_FIXED , (dwLen * 2 ) + 1 );
327+ if (wbuf && buf ) {
328+ GetWindowTextW (g_hwndResult , wbuf , dwLen + 1 );
329+
330+ if (isCSV ) {
331+ /* Convert tabs to commas, handle quoting */
332+ bp = buf ;
333+ wp = wbuf ;
334+ while (* wp ) {
335+ if (* wp == '\t' ) {
336+ * bp ++ = ',' ;
337+ wp ++ ;
338+ } else if (* wp == '\r' ) {
339+ wp ++ ;
340+ } else if (* wp == '\n' ) {
341+ * bp ++ = '\r' ;
342+ * bp ++ = '\n' ;
343+ wp ++ ;
344+ } else {
345+ wchar_t * fieldStart = wp ;
346+ needQuote = 0 ;
347+ while (* wp && * wp != '\t' && * wp != '\r' && * wp != '\n' ) {
348+ if (* wp == ',' || * wp == '"' ) needQuote = 1 ;
349+ wp ++ ;
350+ }
351+ if (needQuote ) {
352+ * bp ++ = '"' ;
353+ while (fieldStart < wp ) {
354+ if (* fieldStart == '"' ) * bp ++ = '"' ;
355+ * bp ++ = (char )* fieldStart ++ ;
356+ }
357+ * bp ++ = '"' ;
358+ } else {
359+ while (fieldStart < wp ) * bp ++ = (char )* fieldStart ++ ;
360+ }
361+ }
362+ }
363+ * bp = '\0' ;
364+ dwLen = (DWORD )(bp - buf );
365+ } else {
366+ /* Plain text - just convert to ANSI */
367+ WideCharToMultiByte (CP_ACP , 0 , wbuf , -1 , buf , (dwLen * 2 ) + 1 , NULL , NULL );
368+ dwLen = (DWORD )strlen (buf );
369+ }
370+
371+ hFile = CreateFileW (szFile , GENERIC_WRITE , 0 , NULL , CREATE_ALWAYS , FILE_ATTRIBUTE_NORMAL , NULL );
372+ if (hFile != INVALID_HANDLE_VALUE ) {
373+ WriteFile (hFile , buf , dwLen , & dwWritten , NULL );
374+ CloseHandle (hFile );
375+ }
376+ }
377+ if (wbuf ) LocalFree (wbuf );
378+ if (buf ) LocalFree (buf );
379+ }
380+
381+ /*============================================================================
382+ ** Export Results to CSV (legacy, kept for File menu)
278383**============================================================================*/
279384
280385void DoExportCSV (void ) {
@@ -391,6 +496,208 @@ void DoExportTxt(void) {
391496 if (buf ) LocalFree (buf );
392497}
393498
499+ /*============================================================================
500+ ** Export Single Table to CSV
501+ **============================================================================*/
502+
503+ /* Table picker dialog state */
504+ static HWND g_hwndTblList ;
505+ static char g_selectedTable [128 ];
506+ static char * * g_tableList ;
507+ static int g_tableCount ;
508+
509+ static LRESULT CALLBACK TablePickerProc (HWND hwnd , UINT msg , WPARAM wParam , LPARAM lParam ) {
510+ switch (msg ) {
511+ case WM_INITDIALOG : {
512+ RECT rc ;
513+ int i ;
514+ wchar_t wname [128 ];
515+ SetWindowTextW (hwnd , L"Select Table" );
516+ GetClientRect (hwnd , & rc );
517+ g_hwndTblList = CreateWindowW (L"LISTBOX" , NULL ,
518+ WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VSCROLL | LBS_NOTIFY ,
519+ 10 , 10 , rc .right - 20 , rc .bottom - 56 ,
520+ hwnd , (HMENU )101 , g_hInst , NULL );
521+ for (i = 0 ; i < g_tableCount ; i ++ ) {
522+ MultiByteToWideChar (CP_ACP , 0 , g_tableList [i ], -1 , wname , 128 );
523+ SendMessageW (g_hwndTblList , LB_ADDSTRING , 0 , (LPARAM )wname );
524+ }
525+ SendMessage (g_hwndTblList , LB_SETCURSEL , 0 , 0 );
526+ CreateWindowW (L"BUTTON" , L"Export" ,
527+ WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON ,
528+ 10 , rc .bottom - 36 , 60 , 26 , hwnd , (HMENU )IDOK , g_hInst , NULL );
529+ CreateWindowW (L"BUTTON" , L"Cancel" ,
530+ WS_CHILD | WS_VISIBLE ,
531+ 80 , rc .bottom - 36 , 60 , 26 , hwnd , (HMENU )IDCANCEL , g_hInst , NULL );
532+ SetFocus (g_hwndTblList );
533+ return FALSE;
534+ }
535+ case WM_COMMAND :
536+ if (LOWORD (wParam ) == IDOK || (LOWORD (wParam ) == 101 && HIWORD (wParam ) == LBN_DBLCLK )) {
537+ int sel = (int )SendMessage (g_hwndTblList , LB_GETCURSEL , 0 , 0 );
538+ if (sel >= 0 && sel < g_tableCount ) {
539+ strcpy (g_selectedTable , g_tableList [sel ]);
540+ EndDialog (hwnd , IDOK );
541+ }
542+ } else if (LOWORD (wParam ) == IDCANCEL ) {
543+ EndDialog (hwnd , IDCANCEL );
544+ }
545+ return TRUE;
546+ case WM_CLOSE :
547+ EndDialog (hwnd , IDCANCEL );
548+ return TRUE;
549+ }
550+ return FALSE;
551+ }
552+
553+ static int PickTable (char * tblName ) {
554+ DLGTEMPLATE dlg ;
555+ char * * results = NULL ;
556+ int nRows = 0 , nCols = 0 , ret ;
557+
558+ /* Get table list */
559+ sqlite_get_table (g_db ,
560+ "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name" ,
561+ & results , & nRows , & nCols , NULL );
562+
563+ if (!results || nRows < 1 ) {
564+ if (results ) sqlite_free_table (results );
565+ MessageBoxW (g_hwndMain , L"No tables in database" , L"Export Table" , MB_OK );
566+ return 0 ;
567+ }
568+
569+ /* Store for dialog */
570+ g_tableList = & results [1 ]; /* Skip header */
571+ g_tableCount = nRows ;
572+ g_selectedTable [0 ] = 0 ;
573+
574+ /* Create dialog */
575+ memset (& dlg , 0 , sizeof (dlg ));
576+ dlg .style = WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_CENTER ;
577+ dlg .cx = 120 ; dlg .cy = 100 ;
578+
579+ ret = DialogBoxIndirectW (g_hInst , & dlg , g_hwndMain , (DLGPROC )TablePickerProc );
580+
581+ sqlite_free_table (results );
582+
583+ if (ret == IDOK && g_selectedTable [0 ]) {
584+ strcpy (tblName , g_selectedTable );
585+ return 1 ;
586+ }
587+ return 0 ;
588+ }
589+
590+ void DoExportTable (void ) {
591+ CE_OPENFILENAME ofn ;
592+ wchar_t szFile [MAX_PATH ];
593+ char tblName [128 ];
594+ char sql [512 ];
595+ char * * result ;
596+ int nRow , nCol , i , j ;
597+ HANDLE hFile ;
598+ DWORD written ;
599+ char line [4096 ];
600+ char * lp , * p ;
601+ const char * t ;
602+
603+ if (!g_db ) return ;
604+
605+ /* Get table name from schema selection or picker */
606+ tblName [0 ] = 0 ;
607+ if (g_viewMode == 2 && g_hwndSchema ) {
608+ HTREEITEM hItem = TreeView_GetSelection (g_hwndSchema );
609+ if (hItem ) {
610+ TV_ITEMW item ;
611+ wchar_t text [128 ];
612+ item .mask = TVIF_TEXT | TVIF_IMAGE ;
613+ item .hItem = hItem ;
614+ item .pszText = text ;
615+ item .cchTextMax = 128 ;
616+ TreeView_GetItem (g_hwndSchema , & item );
617+ if (item .iImage == 1 ) { /* IMG_TABLE */
618+ wchar_t * wp = text ;
619+ while (* wp && * wp != ' ' && * wp != '(' ) wp ++ ;
620+ * wp = 0 ;
621+ WideCharToMultiByte (CP_ACP , 0 , text , -1 , tblName , 128 , NULL , NULL );
622+ }
623+ }
624+ }
625+
626+ /* If no table selected, show picker */
627+ if (!tblName [0 ]) {
628+ if (!PickTable (tblName )) return ;
629+ }
630+
631+ /* Default filename from table name */
632+ MultiByteToWideChar (CP_ACP , 0 , tblName , -1 , szFile , MAX_PATH );
633+ lstrcatW (szFile , L".csv" );
634+
635+ memset (& ofn , 0 , sizeof (ofn ));
636+ ofn .lStructSize = sizeof (ofn );
637+ ofn .hwndOwner = g_hwndMain ;
638+ ofn .lpstrFile = szFile ;
639+ ofn .nMaxFile = MAX_PATH ;
640+ ofn .lpstrFilter = L"CSV Files (*.csv)\0*.csv\0All Files (*.*)\0*.*\0" ;
641+ ofn .lpstrDefExt = L"csv" ;
642+ ofn .lpstrTitle = L"Export Table" ;
643+ ofn .Flags = OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST ;
644+
645+ if (!GetSaveFileNameW (& ofn )) return ;
646+
647+ /* Query table data */
648+ p = sql ;
649+ for (t = "SELECT * FROM \"" ; * t ; ) * p ++ = * t ++ ;
650+ for (t = tblName ; * t ; ) * p ++ = * t ++ ;
651+ * p ++ = '"' ; * p = 0 ;
652+
653+ if (sqlite_get_table (g_db , sql , & result , & nRow , & nCol , NULL ) != SQLITE_OK ) return ;
654+
655+ hFile = CreateFileW (szFile , GENERIC_WRITE , 0 , NULL , CREATE_ALWAYS , FILE_ATTRIBUTE_NORMAL , NULL );
656+ if (hFile == INVALID_HANDLE_VALUE ) {
657+ sqlite_free_table (result );
658+ return ;
659+ }
660+
661+ /* Write header row */
662+ lp = line ;
663+ for (j = 0 ; j < nCol ; j ++ ) {
664+ char * val = result [j ];
665+ if (j > 0 ) * lp ++ = ',' ;
666+ if (val ) while (* val ) * lp ++ = * val ++ ;
667+ }
668+ * lp ++ = '\r' ; * lp ++ = '\n' ;
669+ WriteFile (hFile , line , (DWORD )(lp - line ), & written , NULL );
670+
671+ /* Write data rows */
672+ for (i = 1 ; i <= nRow ; i ++ ) {
673+ lp = line ;
674+ for (j = 0 ; j < nCol ; j ++ ) {
675+ char * val = result [i * nCol + j ];
676+ int needQuote = 0 ;
677+ char * v ;
678+ if (j > 0 ) * lp ++ = ',' ;
679+ if (val ) {
680+ for (v = val ; * v ; v ++ ) if (* v == ',' || * v == '"' || * v == '\n' ) needQuote = 1 ;
681+ if (needQuote ) {
682+ * lp ++ = '"' ;
683+ for (v = val ; * v ; v ++ ) {
684+ if (* v == '"' ) * lp ++ = '"' ;
685+ * lp ++ = * v ;
686+ }
687+ * lp ++ = '"' ;
688+ } else {
689+ while (* val ) * lp ++ = * val ++ ;
690+ }
691+ }
692+ }
693+ * lp ++ = '\r' ; * lp ++ = '\n' ;
694+ WriteFile (hFile , line , (DWORD )(lp - line ), & written , NULL );
695+ }
696+
697+ CloseHandle (hFile );
698+ sqlite_free_table (result );
699+ }
700+
394701/*============================================================================
395702** Export Table to CSV (helper for DoExportDb)
396703**============================================================================*/
0 commit comments