unit exportgrid; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, Menus, ComCtrls, VirtualTrees, SynExportHTML, gnugettext, ActnList, extra_controls, dbstructures, SynRegExpr, System.StrUtils, System.IOUtils; type TGridExportFormat = (efExcel, efCSV, efHTML, efXML, efSQLInsert, efSQLReplace, efSQLDeleteInsert, efSQLUpdate, efLaTeX, efTextile, efJiraTextile, efPHPArray, efMarkDown, efJSON); TfrmExportGrid = class(TExtForm) btnOK: TButton; btnCancel: TButton; grpFormat: TRadioGroup; grpSelection: TRadioGroup; grpOutput: TGroupBox; radioOutputCopyToClipboard: TRadioButton; radioOutputFile: TRadioButton; editFilename: TButtonedEdit; grpOptions: TGroupBox; chkIncludeColumnNames: TCheckBox; editSeparator: TButtonedEdit; editEncloser: TButtonedEdit; editTerminator: TButtonedEdit; lblSeparator: TLabel; lblEncloser: TLabel; lblTerminator: TLabel; popupCSVchar: TPopupMenu; menuCSVtab: TMenuItem; menuCSVunixlinebreak: TMenuItem; menuCSVmaclinebreak: TMenuItem; menuCSVwinlinebreak: TMenuItem; menuCSVnul: TMenuItem; menuCSVbackspace: TMenuItem; menuCSVcontrolz: TMenuItem; comboEncoding: TComboBox; lblEncoding: TLabel; popupRecentFiles: TPopupMenu; menuCSVsinglequote: TMenuItem; menuCSVdoublequote: TMenuItem; menuCSVcomma: TMenuItem; menuCSVsemicolon: TMenuItem; N1: TMenuItem; N2: TMenuItem; N3: TMenuItem; chkIncludeAutoIncrement: TCheckBox; chkIncludeQuery: TCheckBox; lblNull: TLabel; editNull: TButtonedEdit; btnSetClipboardDefaults: TButton; chkRemoveLinebreaks: TCheckBox; procedure FormCreate(Sender: TObject); procedure CalcSize(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure editFilenameRightButtonClick(Sender: TObject); procedure editFilenameChange(Sender: TObject); procedure popupRecentFilesPopup(Sender: TObject); procedure menuCSVClick(Sender: TObject); procedure editCSVRightButtonClick(Sender: TObject); procedure editCSVChange(Sender: TObject); procedure ValidateControls(Sender: TObject); procedure btnOKClick(Sender: TObject); procedure FormShow(Sender: TObject); procedure grpFormatClick(Sender: TObject); procedure btnSetClipboardDefaultsClick(Sender: TObject); procedure FormResize(Sender: TObject); private { Private declarations } FCSVEditor: TButtonedEdit; FCSVSeparator, FCSVEncloser, FCSVTerminator, FCSVNull: String; FGrid: TVirtualStringTree; FRecentFiles: TStringList; FHiddenCopyMode: Boolean; procedure SaveDialogTypeChange(Sender: TObject); function GetExportFormat: TGridExportFormat; procedure SetExportFormat(Value: TGridExportFormat); procedure SetExportFormatByFilename; procedure SelectRecentFile(Sender: TObject); procedure PutFilenamePlaceholder(Sender: TObject); function FormatExcelCsv(Text, Encloser: String; DataType: TDBDatatype): String; function FormatPhp(Text: String): String; function FormatLatex(Text: String): String; public { Public declarations } const FormatToFileExtension: Array[TGridExportFormat] of String = (('csv'), ('csv'), ('html'), ('xml'), ('sql'), ('sql'), ('sql'), ('sql'), ('LaTeX'), ('textile'), ('jira-textile'), ('php'), ('md'), ('json')); const FormatToDescription: Array[TGridExportFormat] of String = (('Excel CSV'), ('Delimited text'), ('HTML table'), ('XML'), ('SQL INSERTs'), ('SQL REPLACEs'), ('SQL DELETEs/INSERTs'), ('SQL UPDATEs'), ('LaTeX'), ('Textile'), ('Jira Textile'), ('PHP Array'), ('Markdown Here'), ('JSON')); const FormatToImageIndex: Array[TGridExportFormat] of Integer = (49, 50, 32, 48, 201, 201, 201, 201, 153, 154, 154, 202, 199, 200); const CopyAsActionPrefix = 'actCopyAs'; property Grid: TVirtualStringTree read FGrid write FGrid; property ExportFormat: TGridExportFormat read GetExportFormat write SetExportFormat; end; implementation uses main, apphelpers, dbconnection; {$R *.dfm} procedure TfrmExportGrid.FormCreate(Sender: TObject); var FormatDesc: String; SenderName: String; begin HasSizeGrip := True; editFilename.Text := AppSettings.ReadString(asGridExportFilename); FRecentFiles := Explode(DELIM, AppSettings.ReadString(asGridExportRecentFiles)); comboEncoding.Items.Assign(MainForm.FileEncodings); comboEncoding.Items.Delete(0); // Remove "Auto detect" comboEncoding.ItemIndex := AppSettings.ReadInt(asGridExportEncoding); grpFormat.Items.Clear; for FormatDesc in FormatToDescription do grpFormat.Items.Add(FormatDesc); SenderName := Owner.Name; FHiddenCopyMode := SenderName.StartsWith(CopyAsActionPrefix); if FHiddenCopyMode then begin radioOutputCopyToClipboard.Checked := True; grpFormat.ItemIndex := Owner.Tag; grpSelection.ItemIndex := 0; // Always use selected cells in copy mode chkIncludeColumnNames.Checked := AppSettings.ReadBool(asGridExportClpColumnNames); chkIncludeAutoIncrement.Checked := AppSettings.ReadBool(asGridExportClpIncludeAutoInc); chkIncludeQuery.Checked := False; // Always off in copy mode chkRemoveLinebreaks.Checked := AppSettings.ReadBool(asGridExportClpRemoveLinebreaks); FCSVSeparator := AppSettings.ReadString(asGridExportClpSeparator); FCSVEncloser := AppSettings.ReadString(asGridExportClpEncloser); FCSVTerminator := AppSettings.ReadString(asGridExportClpTerminator); FCSVNull := AppSettings.ReadString(asGridExportClpNull); end else begin radioOutputCopyToClipboard.Checked := AppSettings.ReadBool(asGridExportOutputCopy); radioOutputFile.Checked := AppSettings.ReadBool(asGridExportOutputFile); grpFormat.ItemIndex := AppSettings.ReadInt(asGridExportFormat); grpSelection.ItemIndex := AppSettings.ReadInt(asGridExportSelection); chkIncludeColumnNames.Checked := AppSettings.ReadBool(asGridExportColumnNames); chkIncludeAutoIncrement.Checked := AppSettings.ReadBool(asGridExportIncludeAutoInc); chkIncludeQuery.Checked := AppSettings.ReadBool(asGridExportIncludeQuery); chkRemoveLinebreaks.Checked := AppSettings.ReadBool(asGridExportRemoveLinebreaks); FCSVSeparator := AppSettings.ReadString(asGridExportSeparator); FCSVEncloser := AppSettings.ReadString(asGridExportEncloser); FCSVTerminator := AppSettings.ReadString(asGridExportTerminator); FCSVNull := AppSettings.ReadString(asGridExportNull); end; ValidateControls(Sender); end; procedure TfrmExportGrid.FormResize(Sender: TObject); begin grpFormat.Width := Width div 3; grpSelection.Left := grpFormat.Left + grpFormat.Width + 8; grpSelection.Width := Width - grpSelection.Left - 24; grpOptions.Left := grpSelection.Left; grpOptions.Width := grpSelection.Width; end; procedure TfrmExportGrid.FormShow(Sender: TObject); begin // Show dialog. Expect "Grid" property to be set now by the caller. Width := AppSettings.ReadIntDpiAware(asGridExportWindowWidth, Self); Height := AppSettings.ReadIntDpiAware(asGridExportWindowHeight, Self); chkIncludeAutoIncrement.OnClick := CalcSize; CalcSize(Sender); end; procedure TfrmExportGrid.FormClose(Sender: TObject; var Action: TCloseAction); begin // Store settings AppSettings.WriteIntDpiAware(asGridExportWindowWidth, Self, Width); AppSettings.WriteIntDpiAware(asGridExportWindowHeight, Self, Height); if ModalResult = mrOK then begin AppSettings.WriteBool(asGridExportOutputCopy, radioOutputCopyToClipboard.Checked); AppSettings.WriteBool(asGridExportOutputFile, radioOutputFile.Checked); AppSettings.WriteString(asGridExportFilename, editFilename.Text); AppSettings.WriteString(asGridExportRecentFiles, Implode(DELIM, FRecentFiles)); AppSettings.WriteInt(asGridExportEncoding, comboEncoding.ItemIndex); AppSettings.WriteInt(asGridExportFormat, grpFormat.ItemIndex); AppSettings.WriteInt(asGridExportSelection, grpSelection.ItemIndex); AppSettings.WriteBool(asGridExportColumnNames, chkIncludeColumnNames.Checked); AppSettings.WriteBool(asGridExportIncludeAutoInc, chkIncludeAutoIncrement.Checked); AppSettings.WriteBool(asGridExportIncludeQuery, chkIncludeQuery.Checked); AppSettings.WriteBool(asGridExportRemoveLinebreaks, chkRemoveLinebreaks.Checked); AppSettings.WriteString(asGridExportSeparator, FCSVSeparator); AppSettings.WriteString(asGridExportEncloser, FCSVEncloser); AppSettings.WriteString(asGridExportTerminator, FCSVTerminator); AppSettings.WriteString(asGridExportNull, FCSVNull); end; end; procedure TfrmExportGrid.ValidateControls(Sender: TObject); var Enable: Boolean; begin // Display the actually used control characters, even if they cannot be changed case ExportFormat of efExcel: begin // Tab for pasting, semicolon if comma is also the decimal separator, and comma for the rest // see http://en.wikipedia.org/wiki/Comma-separated_values if radioOutputCopyToClipboard.Checked then editSeparator.Text := '\t' else if FormatSettings.DecimalSeparator=',' then editSeparator.Text := ';' else editSeparator.Text := ','; editEncloser.Text := '"'; editTerminator.Text := '\r\n'; editNull.Text := FCSVNull; end; efCSV: begin editSeparator.Text := FCSVSeparator; editEncloser.Text := FCSVEncloser; editTerminator.Text := FCSVTerminator; editNull.Text := FCSVNull; end; efMarkDown: editNull.Text := FCSVNull; else begin editSeparator.Text := ''; editEncloser.Text := ''; editTerminator.Text := ''; editNull.Text := ''; end; end; chkIncludeQuery.Enabled := ExportFormat in [efHTML, efXML, efMarkDown, efJSON]; Enable := ExportFormat = efCSV; lblSeparator.Enabled := Enable; editSeparator.Enabled := Enable; editSeparator.RightButton.Enabled := Enable; lblEncloser.Enabled := Enable; editEncloser.Enabled := Enable; editEncloser.RightButton.Enabled := Enable; lblTerminator.Enabled := Enable; editTerminator.Enabled := Enable; editTerminator.RightButton.Enabled := Enable; lblNull.Enabled := ExportFormat in [efExcel, efCSV, efMarkDown]; editNull.Enabled := lblNull.Enabled; editNull.RightButton.Enabled := lblNull.Enabled; btnOK.Enabled := radioOutputCopyToClipboard.Checked or (radioOutputFile.Checked and (editFilename.Text <> '')); if radioOutputFile.Checked then editFilename.Font.Color := GetThemeColor(clWindowText) else editFilename.Font.Color := GetThemeColor(clGrayText); comboEncoding.Enabled := radioOutputFile.Checked; lblEncoding.Enabled := radioOutputFile.Checked; end; function TfrmExportGrid.GetExportFormat: TGridExportFormat; begin Result := TGridExportFormat(grpFormat.ItemIndex); end; procedure TfrmExportGrid.SetExportFormat(Value: TGridExportFormat); begin grpFormat.ItemIndex := Integer(Value); end; procedure TfrmExportGrid.grpFormatClick(Sender: TObject); var Filename: String; begin // Auto-modify file extension when selecting export format // Be careful about triggering editFilename.OnChange event, as we may have come here from that event! if radioOutputFile.Checked then begin Filename := ExtractFilePath(editFilename.Text) + TPath.GetFileNameWithoutExtension(editFilename.Text) + '.' + FormatToFileExtension[ExportFormat]; if CompareText(Filename, editFilename.Text) <> 0 then editFilename.Text := Filename; end; ValidateControls(Sender); end; procedure TfrmExportGrid.SetExportFormatByFilename; var ext: String; efrm: TGridExportFormat; begin // Set format by file extension ext := LowerCase(Copy(ExtractFileExt(editFilename.Text), 2, 10)); for efrm :=Low(TGridExportFormat) to High(TGridExportFormat) do begin if ext = FormatToFileExtension[ExportFormat] then break; if ext = FormatToFileExtension[efrm] then begin ExportFormat := efrm; break; end; end; end; procedure TfrmExportGrid.editFilenameChange(Sender: TObject); begin radioOutputFile.Checked := True; end; procedure TfrmExportGrid.editFilenameRightButtonClick(Sender: TObject); var Dialog: TSaveDialog; ef: TGridExportFormat; Filename: String; begin // Select file target Dialog := TSaveDialog.Create(Self); Filename := GetOutputFilename(editFilename.Text, MainForm.ActiveDbObj); Dialog.InitialDir := ExtractFilePath(Filename); Dialog.FileName := TPath.GetFileNameWithoutExtension(Filename); Dialog.Filter := ''; for ef:=Low(TGridExportFormat) to High(TGridExportFormat) do Dialog.Filter := Dialog.Filter + FormatToDescription[ef] + ' (*.'+FormatToFileExtension[ef]+')|*.'+FormatToFileExtension[ef]+'|'; Dialog.Filter := Dialog.Filter + _('All files')+' (*.*)|*.*'; Dialog.OnTypeChange := SaveDialogTypeChange; Dialog.FilterIndex := grpFormat.ItemIndex+1; Dialog.OnTypeChange(Dialog); if Dialog.Execute then begin editFilename.Text := Dialog.FileName; SetExportFormatByFilename; end; Dialog.Free; end; procedure TfrmExportGrid.popupRecentFilesPopup(Sender: TObject); var Filename: String; Menu: TPopupMenu; Item: TMenuItem; Placeholders: TStringList; i: Integer; begin // Clear and populate drop down menu with recent files and filename placeholders Menu := Sender as TPopupMenu; Menu.Items.Clear; for Filename in FRecentFiles do begin Item := TMenuItem.Create(Menu); Menu.Items.Add(Item); Item.Caption := Filename; Item.Hint := Filename; Item.OnClick := SelectRecentFile; Item.Checked := Filename = editFilename.Text; end; Item := TMenuItem.Create(Menu); Menu.Items.Add(Item); Item.Caption := '-'; Placeholders := GetOutputFilenamePlaceholders; for i:=0 to Placeholders.Count-1 do begin Item := TMenuItem.Create(Menu); Menu.Items.Add(Item); Item.Caption := '%' + Placeholders.Names[i] + ': ' + Placeholders.ValueFromIndex[i]; Item.Hint := '%' + Placeholders.Names[i]; Item.OnClick := PutFilenamePlaceholder; end; Placeholders.Free; end; procedure TfrmExportGrid.SelectRecentFile(Sender: TObject); begin // Select file from recently used files editFilename.Text := (Sender as TMenuItem).Hint; SetExportFormatByFilename; end; procedure TfrmExportGrid.PutFilenamePlaceholder(Sender: TObject); begin // Put filename placeholder editFilename.SelText := (Sender as TMenuItem).Hint; end; procedure TfrmExportGrid.btnSetClipboardDefaultsClick(Sender: TObject); begin // Store copy-to-clipboard settings AppSettings.ResetPath; AppSettings.WriteBool(asGridExportClpColumnNames, chkIncludeColumnNames.Checked); AppSettings.WriteBool(asGridExportClpIncludeAutoInc, chkIncludeAutoIncrement.Checked); AppSettings.WriteBool(asGridExportRemoveLinebreaks, chkRemoveLinebreaks.Checked); AppSettings.WriteString(asGridExportClpSeparator, FCSVSeparator); AppSettings.WriteString(asGridExportClpEncloser, FCSVEncloser); AppSettings.WriteString(asGridExportClpTerminator, FCSVTerminator); AppSettings.WriteString(asGridExportClpNull, FCSVNull); MessageDialog(_('Clipboard settings changed.'), mtInformation, [mbOK]); end; procedure TfrmExportGrid.CalcSize(Sender: TObject); var GridData: TDBQuery; Node: PVirtualNode; Col, ExcludeCol: TColumnIndex; RowNum: PInt64; SelectionSize, AllSize, RowsCalculated: Int64; begin GridData := Mainform.GridResult(Grid); AllSize := 0; SelectionSize := 0; chkIncludeAutoIncrement.Enabled := GridData.AutoIncrementColumn > -1; ExcludeCol := -1; if chkIncludeAutoIncrement.Enabled and (not chkIncludeAutoIncrement.Checked) then ExcludeCol := GridData.AutoIncrementColumn; Node := GetNextNode(Grid, nil, False); RowsCalculated := 0; while Assigned(Node) do begin RowNum := Grid.GetNodeData(Node); GridData.RecNo := RowNum^; Col := Grid.Header.Columns.GetFirstVisibleColumn; while Col > NoColumn do begin if Col <> ExcludeCol then begin Inc(AllSize, GridData.ColumnLengths(Col)); if vsSelected in Node.States then Inc(SelectionSize, GridData.ColumnLengths(Col)); end; Col := Grid.Header.Columns.GetNextVisibleColumn(Col); end; // Performance: use first rows only, and interpolate the rest, see issue #804 Inc(RowsCalculated); if RowsCalculated >= 1000 then Break; Node := GetNextNode(Grid, Node, False); end; if GridData.RecordCount > RowsCalculated then begin AllSize := Round(AllSize / RowsCalculated * GridData.RecordCount); end; grpSelection.Items[0] := f_('Selection (%s rows, %s)', [FormatNumber(Grid.SelectedCount), FormatByteNumber(SelectionSize)]); grpSelection.Items[1] := f_('Complete (%s rows, %s)', [FormatNumber(Grid.RootNodeCount), FormatByteNumber(AllSize)]); end; procedure TfrmExportGrid.editCSVChange(Sender: TObject); var Edit: TButtonedEdit; begin // Remember csv settings Edit := Sender as TButtonedEdit; case ExportFormat of efExcel, efMarkDown: begin if Edit = editNull then FCSVNull := Edit.Text; end; efCSV: begin if Edit = editSeparator then FCSVSeparator := Edit.Text else if Edit = editEncloser then FCSVEncloser := Edit.Text else if Edit = editTerminator then FCSVTerminator := Edit.Text else if Edit = editNull then FCSVNull := Edit.Text; end; end; end; procedure TfrmExportGrid.SaveDialogTypeChange(Sender: TObject); var Dialog: TSaveDialog; ef: TGridExportFormat; begin // Set default file-extension of saved file and options on the dialog to show Dialog := Sender as TSaveDialog; for ef:=Low(TGridExportFormat) to High(TGridExportFormat) do begin if Dialog.FilterIndex = Integer(ef)+1 then Dialog.DefaultExt := FormatToFileExtension[ef]; end; end; procedure TfrmExportGrid.editCSVRightButtonClick(Sender: TObject); var p: TPoint; Item: TMenuItem; begin // Remember editor and prepare popup menu items FCSVEditor := Sender as TButtonedEdit; p := FCSVEditor.ClientToScreen(FCSVEditor.ClientRect.BottomRight); for Item in popupCSVchar.Items do begin Item.Checked := FCSVEditor.Text = Item.Hint; end; popupCSVchar.Popup(p.X-16, p.Y); end; procedure TfrmExportGrid.menuCSVClick(Sender: TObject); begin // Insert char from menu FCSVEditor.Text := TMenuItem(Sender).Hint; end; function TfrmExportGrid.FormatExcelCsv(Text, Encloser: String; DataType: TDBDatatype): String; begin Result := Text; // Escape encloser characters inside data per de-facto CSV. if not Encloser.IsEmpty then Result := StringReplace(Result, Encloser, Encloser+Encloser, [rfReplaceAll]); if DataType.Category = dtcTemporal then begin Result := ReplaceRegExpr('\.(\d+)$', Result, FormatSettings.DecimalSeparator + '$1', True); end; end; function TfrmExportGrid.FormatPhp(Text: String): String; begin // String escaping for PHP output. Incompatible to TDBConnection.EscapeString. Result := StringReplace(Text, '\', '\\', [rfReplaceAll]); Result := StringReplace(Result, #13, '\r', [rfReplaceAll]); Result := StringReplace(Result, #10, '\n', [rfReplaceAll]); Result := StringReplace(Result, #9, '\t', [rfReplaceAll]); Result := StringReplace(Result, '"', '\"', [rfReplaceAll]); Result := '"' + Result + '"'; end; function TfrmExportGrid.FormatLatex(Text: String): String; var TextChr: Char; const NeedBackslash: TSysCharset = ['_', '$', '%', '&']; begin // String escaping for LaTeX output. Mostly uses backslash. Probably incomplete. // See pm from H. Flick // See https://tex.stackexchange.com/a/301984 Result := Text; for TextChr in NeedBackslash do begin Result := StringReplace(Result, TextChr, '\'+TextChr, [rfReplaceAll]); end; end; procedure TfrmExportGrid.btnOKClick(Sender: TObject); var Col, ExcludeCol: TColumnIndex; Header, Data, tmp, Encloser, Separator, Terminator, TableName, Filename: String; Node: PVirtualNode; GridData: TDBQuery; SelectionOnly, HasNulls: Boolean; i: Integer; NodeCount: Cardinal; RowNum: PInt64; HTML: TStream; S: TStringStream; Exporter: TSynExporterHTML; Encoding: TEncoding; Bom: TBytes; begin Filename := GetOutputFilename(editFilename.Text, MainForm.ActiveDbObj); // Confirmation dialog if file exists if radioOutputFile.Checked and FileExists(Filename) and (MessageDialog(_('File exists'), f_('Overwrite file %s?', [Filename]), mtConfirmation, [mbYes, mbCancel]) = mrCancel) then begin ModalResult := mrNone; Exit; end; try Screen.Cursor := crHourglass; SelectionOnly := grpSelection.ItemIndex = 0; Mainform.DataGridEnsureFullRows(Grid, SelectionOnly); GridData := Mainform.GridResult(Grid); if SelectionOnly then NodeCount := Grid.SelectedCount else NodeCount := Grid.RootNodeCount; MainForm.EnableProgress(NodeCount); try TableName := GridData.TableName; except TableName := _('UnknownTable'); end; ExcludeCol := NoColumn; if (not chkIncludeAutoIncrement.Checked) or (not chkIncludeAutoIncrement.Enabled) then ExcludeCol := GridData.AutoIncrementColumn; if radioOutputCopyToClipboard.Checked then Encoding := TEncoding.UTF8 else begin Encoding := MainForm.GetEncodingByName(comboEncoding.Text); // Add selected file to file list, and sort it onto the top of the list i := FRecentFiles.IndexOf(editFilename.Text); if i > -1 then FRecentFiles.Delete(i); FRecentFiles.Insert(0, editFilename.Text); for i:=FRecentFiles.Count-1 downto 10 do FRecentFiles.Delete(i); end; // Prepare stream // Note that TStringStream + TEncoding.UTF8 do not write a BOM (which is nice), // although it should do so according to TUTF8Encoding.GetPreamble. // Now, only newer Excel versions need that BOM, so we add it explicitly here S := TStringStream.Create(Header, Encoding); if (ExportFormat = efExcel) and (Encoding = TEncoding.UTF8) and radioOutputFile.Checked then begin Bom := TBytes.Create($EF, $BB, $BF); S.Write(Bom, 3); end; Header := ''; case ExportFormat of efHTML: begin Header := '' + CRLF + CRLF + '' + CRLF + '
' + CRLF + '' + GridData.SQL + '
' + CRLF + CRLF; Header := Header + '' + Grid.Header.Columns[Col].Text + ' | ' + CRLF; Col := Grid.Header.Columns.GetNextVisibleColumn(Col); end; Header := Header + '
---|
' + Data + ' | ' + CRLF; end; efExcel, efCSV: begin if GridData.IsNull(Col) then Data := editNull.Text else begin Data := FormatExcelCsv(Data, Encloser, GridData.DataType(Col)); Data := Encloser + Data + Encloser; end; tmp := tmp + Data + Separator; end; efLaTeX: begin Data := FormatLatex(Data); if (not GridData.IsNull(Col)) and (GridData.DataType(Col).Category in [dtcInteger, dtcReal]) then // Special encloser for numeric values, see https://www.heidisql.com/forum.php?t=36530 Data := '$' + Data + '$'; tmp := tmp + Data + Separator; end; efTextile, efJiraTextile: begin tmp := tmp + Data + Separator; end; efMarkDown: begin if GridData.IsNull(Col) then Data := editNull.Text; tmp := tmp + Data + Separator; end; efXML: begin // Print cell start tag. tmp := tmp + #9#9'
' + CRLF + ' generated ' + DateToStr(now) + ' ' + TimeToStr(now) + ' by ' + APPNAME + ' ' + Mainform.AppVersion + '' + CRLF + '
' + CRLF + CRLF + ' ' + CRLF + '' + CRLF; end; efXML: begin if chkIncludeQuery.Checked then tmp := '' + CRLF else tmp := '' + CRLF; end; efLaTeX: tmp := '\end{tabular}' + CRLF; efPHPArray: begin tmp := ');' + CRLF; end; efJSON: begin S.Size := S.Size - 1; tmp := CRLF + #9 + ']' + CRLF + '}'; end; else tmp := ''; end; S.WriteString(tmp); if radioOutputCopyToClipboard.Checked then begin HTML := nil; // SynEdit's exporter is slow on large strings, see issue #2903 if S.Size < 100*SIZE_KB then begin case ExportFormat of efSQLInsert, efSQLReplace, efSQLDeleteInsert: begin Exporter := TSynExporterHTML.Create(Self); Exporter.Highlighter := MainForm.SynSQLSynUsed; Exporter.ExportAll(Explode(CRLF, S.DataString)); HTML := TMemoryStream.Create; Exporter.SaveToStream(HTML); Exporter.Free; end; efHTML: HTML := S; end; end; StreamToClipboard(S, HTML, (ExportFormat=efHTML) and (HTML <> nil)); end else begin try S.SaveToFile(Filename); except on E:EFCreateError do begin // Keep form open if file cannot be created ModalResult := mrNone; MainForm.SetProgressState(pbsError); ErrorDialog(E.Message); end; end; end; Mainform.ShowStatusMsg(_('Freeing data...')); FreeAndNil(S); except // Whole export code wrapped here on E:EDbError do begin Screen.Cursor := crDefault; ErrorDialog(E.Message); end else raise; end; Mainform.DisableProgress; Mainform.ShowStatusMsg; Screen.Cursor := crDefault; end; end.