@@ -25,7 +25,7 @@ interface
2525 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, CodeCompletion, CppParser, SynExportTeX,
2626 SynEditExport, SynExportRTF, Menus, ImgList, ComCtrls, StdCtrls, ExtCtrls, SynEdit, SynEditKeyCmds, version,
2727 SynEditCodeFolding, SynExportHTML, SynEditTextBuffer, Math, StrUtils, SynEditTypes, SynEditHighlighter, DateUtils,
28- CodeToolTip, CBUtils, System.UITypes, System.Contnrs, SynEditPrint;
28+ CodeToolTip, CBUtils, System.UITypes, System.Contnrs, SynEditPrint, Vcl.ExtDlgs ;
2929
3030type
3131 TCloseTabSheet = class (TTabSheet)
@@ -198,9 +198,9 @@ TEditor = class(TObject)
198198 procedure DebugAfterPaint (ACanvas: TCanvas; AClip: TRect; FirstLine, LastLine: integer);
199199 function GetPageControl : TPageControl;
200200 procedure SetPageControl (Value : TPageControl);
201- procedure UpdateEncoding (const FileName: string) ;
201+ function UpdateEncoding (const FileName: string; AEncoding: TEncoding = nil ): TEncoding ;
202202 public
203- constructor Create(const Filename: String; InProject, NewFile: boolean; ParentPageControl: TPageControl);
203+ constructor Create(const Filename: String; InProject, NewFile: boolean; ParentPageControl: TPageControl; AEncoding: TEncoding );
204204 destructor Destroy; override;
205205 function Save : boolean;
206206 function SaveAs : boolean;
@@ -235,6 +235,8 @@ TEditor = class(TObject)
235235 property PageControl: TPageControl read GetPageControl write SetPageControl;
236236 property EncodingCorrection: TSymbolCorrectionEncoding read fEncodingCorrection write fEncodingCorrection;
237237 end ;
238+ const
239+ CTAB_PADDING = ' ' ;
238240
239241implementation
240242
@@ -342,13 +344,118 @@ procedure TDebugGutter.LinesDeleted(FirstLine, Count: integer);
342344 LinesDeletedList(MainForm.FindOutput.Items);
343345end ;
344346
347+ { Encoding }
348+
349+ function IsStrUTF8 (const Tex : AnsiString): boolean;
350+ begin
351+ result := (Tex <> ' ' ) and (UTF8Decode(Tex) <> ' ' );
352+ end ;
353+
354+ function UTF8FileBOM (const FileName: string): boolean;
355+ var
356+ txt: file ;
357+ bytes: array [0 ..2 ] of byte;
358+ amt: integer;
359+ begin
360+
361+ FileMode := fmOpenRead;
362+ AssignFile(txt, FileName);
363+ Reset(txt, 1 );
364+
365+ try
366+ BlockRead(txt, bytes, 3 , amt);
367+ result := (amt=3 ) and (bytes[0 ] = $EF) and (bytes[1 ] = $BB) and (bytes[2 ] = $BF);
368+ finally
369+ CloseFile(txt);
370+ end ;
371+
372+ end ;
373+
374+ function FileMayBeUTF8 (FileName: WideString): Boolean;
375+ var
376+ Stream: TMemoryStream;
377+ BytesRead: integer;
378+ ArrayBuff: array [0 ..127 ] of byte;
379+ PreviousByte: byte;
380+ i: integer;
381+ YesSequences, NoSequences: integer;
382+
383+ begin
384+ if not FileExists(FileName) then
385+ Exit;
386+ YesSequences := 0 ;
387+ NoSequences := 0 ;
388+ Stream := TMemoryStream.Create;
389+ try
390+ Stream.LoadFromFile(FileName);
391+ repeat
392+
393+ { read from the TMemoryStream}
394+
395+ BytesRead := Stream.Read(ArrayBuff, High(ArrayBuff) + 1 );
396+ { Do the work on the bytes in the buffer}
397+ if BytesRead > 1 then
398+ begin
399+ for i := 1 to BytesRead-1 do
400+ begin
401+ PreviousByte := ArrayBuff[i-1 ];
402+ if ((ArrayBuff[i] and $c0) = $80 ) then
403+ begin
404+ if ((PreviousByte and $c0) = $c0) then
405+ begin
406+ inc(YesSequences)
407+ end
408+ else
409+ begin
410+ if ((PreviousByte and $80 ) = $0 ) then
411+ inc(NoSequences);
412+ end ;
413+ end ;
414+ end ;
415+ end ;
416+ until (BytesRead < (High(ArrayBuff) + 1 ));
417+ // Below, >= makes ASCII files = UTF-8, which is no problem.
418+ // Simple > would catch only UTF-8;
419+ Result := (YesSequences >= NoSequences);
420+
421+ finally
422+ Stream.Free;
423+ end ;
424+ end ;
425+
426+
427+ type
428+ TSynEditStringListEx = class (TSynEditStringList);
429+
430+ // similar functions "IsWideCharMappableToAnsi, IsUnicodeStringMappableToAnsi)
431+ // are placed in the module SynUnicode.pas. But those functions, without WC_NO_BEST_FIT_CHARS flag,
432+ // return incorrect results sometimes
433+ function _IsWideCharMappableToAnsi (const WC: WideChar): Boolean;
434+ var
435+ UsedDefaultChar: BOOL;
436+ begin
437+ WideCharToMultiByte(DefaultSystemCodePage, WC_NO_BEST_FIT_CHARS, PWideChar(@WC), 1 , nil , 0 , nil ,
438+ @UsedDefaultChar);
439+ Result := not UsedDefaultChar;
440+ end ;
441+
442+ function _IsUnicodeStringMappableToAnsi (const WS: UnicodeString): Boolean;
443+ var
444+ UsedDefaultChar: BOOL;
445+ begin
446+ WideCharToMultiByte(DefaultSystemCodePage, WC_NO_BEST_FIT_CHARS, PWideChar(WS), Length(WS), nil , 0 ,
447+ nil , @UsedDefaultChar);
448+ Result := not UsedDefaultChar;
449+ end ;
450+
451+
345452{ TEditor }
346453
347454type
348455 TSynEditPluginInternal = class (TSynEditPlugin)
349456 end ;
350457
351- constructor TEditor.Create(const Filename: String; InProject, NewFile: boolean; ParentPageControl: TPageControl);
458+ constructor TEditor.Create(const Filename: String; InProject, NewFile: boolean; ParentPageControl: TPageControl; AEncoding: TEncoding );
352459var
353460 s: String;
354461 I: integer;
@@ -374,7 +481,7 @@ constructor TEditor.Create(const Filename: String; InProject, NewFile: boolean;
374481
375482 // Create a new tab
376483 fTabSheet := TCloseTabSheet.Create(ParentPageControl);
377- fTabSheet.Caption := ExtractFileName(fFilename); // UntitlexX or main.cpp
484+ fTabSheet.Caption := ExtractFileName(fFilename) + CTAB_PADDING ; // UntitlexX or main.cpp
378485 fTabSheet.PageControl := ParentPageControl;
379486 fTabSheet.Tag := integer(Self); // Define an index for each tab
380487 fTabSheet.TabVisible := True;
@@ -385,7 +492,22 @@ constructor TEditor.Create(const Filename: String; InProject, NewFile: boolean;
385492
386493 // Load the file using Lines
387494 if not NewFile and FileExists(FileName) then begin
388- fText.Lines.LoadFromFile(FileName);
495+
496+ if (AEncoding = nil ) then
497+ begin
498+ var IsUTF8 := UTF8FileBOM(FileName);
499+
500+ if not IsUTF8 then
501+ IsUTF8 := FileMayBeUTF8(FileName);
502+
503+ if IsUTF8 then
504+ begin
505+ AEncoding := TEncoding.UTF8;
506+ // TSynEditStringListEx(fText.Lines).SetEncoding(AEncoding);
507+ end ;
508+ end ;
509+
510+ fText.Lines.LoadFromFile(FileName, AEncoding);
389511 fNew := False;
390512
391513 // Save main.cpp as main.123456789.cpp
@@ -958,61 +1080,40 @@ procedure TEditor.UpdateCaption(const NewCaption: String);
9581080begin
9591081 if Assigned(fTabSheet) then begin
9601082 if NewCaption <> fTabSheet.Caption then begin
961- fTabSheet.Caption := NewCaption;
1083+ fTabSheet.Caption := NewCaption + CTAB_PADDING ;
9621084 end ;
9631085 end ;
9641086end ;
9651087
966- type
967- TSynEditStringListEx = class (TSynEditStringList);
968-
969- // similar functions "IsWideCharMappableToAnsi, IsUnicodeStringMappableToAnsi)
970- // are placed in the module SynUnicode.pas. But those functions, without WC_NO_BEST_FIT_CHARS flag,
971- // return incorrect results sometimes
972- function _IsWideCharMappableToAnsi (const WC: WideChar): Boolean;
1088+ function TEditor.UpdateEncoding (const FileName: string; AEncoding: TEncoding = nil ): TEncoding;
9731089var
974- UsedDefaultChar: BOOL;
1090+ LLines: TSynEditStringListEx;
1091+ I: Integer;
9751092begin
976- WideCharToMultiByte(DefaultSystemCodePage, WC_NO_BEST_FIT_CHARS, PWideChar(@WC), 1 , nil , 0 , nil ,
977- @UsedDefaultChar);
978- Result := not UsedDefaultChar;
979- end ;
980-
981- function _IsUnicodeStringMappableToAnsi (const WS: UnicodeString): Boolean;
982- var
983- UsedDefaultChar: BOOL;
984- begin
985- WideCharToMultiByte(DefaultSystemCodePage, WC_NO_BEST_FIT_CHARS, PWideChar(WS), Length(WS), nil , 0 ,
986- nil , @UsedDefaultChar);
987- Result := not UsedDefaultChar;
988- end ;
989-
990-
991- procedure TEditor.UpdateEncoding (const FileName: string);
992- var
993- lines: TSynEditStringListEx;
994- i: Integer;
995- begin
996- lines := TSynEditStringListEx(fText.Lines);
1093+ Result := AEncoding;
1094+ LLines := TSynEditStringListEx(fText.Lines);
9971095
9981096 if (fEncodingCorrection = sceNever)
9991097 or not fText.Modified
1000- or (lines .Encoding.CodePage = CP_UTF8) then exit ;
1098+ or (LLines .Encoding.CodePage = CP_UTF8) then Exit ;
10011099
10021100 if fEncodingCorrection = sceFixedUTF8 then
10031101 begin
1004- lines.SetEncoding(TEncoding.UTF8);
1005- exit;
1102+ LLines.SetEncoding(TEncoding.UTF8);
1103+ Result := TEncoding.UTF8;
1104+ Exit;
10061105 end ;
10071106
1008- for I := 0 to Lines.Count-1 do
1009- if _IsUnicodeStringMappableToAnsi(lines[i]) then
1107+ for I := 0 to LLines.Count-1 do
1108+ if IsStrUTF8(LLines[i]) then
1109+ // if _IsUnicodeStringMappableToAnsi(LLines[i]) then
10101110 begin
10111111 if (fEncodingCorrection = sceAuto)
10121112 or (MessageDlg(Format(Lang[ID_MSG_FILESAVEENCODING], [FileName]),
10131113 mtConfirmation, [mbYes, mbNo], 0 ) = mrYes) then
1014- lines.SetEncoding(TEncoding.UTF8);
1015- break;
1114+ LLines.SetEncoding(TEncoding.UTF8);
1115+ Result := TEncoding.UTF8;
1116+ Break;
10161117 end ;
10171118end ;
10181119
@@ -1673,7 +1774,7 @@ procedure TEditor.EditorClick(Sender: TObject);
16731774 (fTripleClickMousePos.Line = fDblClickMousePos.Line) then begin
16741775
16751776 // Don't let the editor change the caret
1676- fText.StateFlags := fText.StateFlags - [sfWaitForDragging ];
1777+ fText.StateFlags := fText.StateFlags - [sfOleDragSource ];
16771778
16781779 // Select the current line
16791780 if fText.CaretY < fText.Lines.Count then begin
@@ -2036,13 +2137,28 @@ function TEditor.SaveAs: boolean;
20362137var
20372138 UnitIndex: integer;
20382139 SaveFileName: String;
2140+ LEncIndex: Integer;
2141+ LEncoding: TEncoding;
20392142begin
20402143 Result := True;
2041- with TSaveDialog .Create(nil ) do try
2144+ with TSaveTextFileDialog .Create(nil ) do try
20422145 Title := Lang[ID_NV_SAVEAS];
20432146 Filter := BuildFilter([FLT_CS, FLT_CPPS, FLT_HEADS, FLT_RES]);
20442147 Options := Options + [ofOverwritePrompt];
20452148
2149+ if fText.Lines.Encoding=TEncoding.UTF8 then
2150+ EncodingIndex := 4 ;
2151+ if fText.Lines.Encoding=TEncoding.UTF7 then
2152+ EncodingIndex := 5 ;
2153+ if fText.Lines.Encoding=TEncoding.BigEndianUnicode then
2154+ EncodingIndex := 3 ;
2155+ if fText.Lines.Encoding=TEncoding.Unicode then
2156+ EncodingIndex := 2 ;
2157+ if fText.Lines.Encoding=TEncoding.ASCII then
2158+ EncodingIndex := 1 ;
2159+ if fText.Lines.Encoding=TEncoding.ANSI then
2160+ EncodingIndex := 0 ;
2161+
20462162 // select appropriate filter
20472163 if GetFileTyp(fFileName) in [utcHead, utcppHead] then begin
20482164 FilterIndex := 4 ; // .h
@@ -2072,8 +2188,14 @@ function TEditor.SaveAs: boolean;
20722188
20732189 // Open the save box
20742190 if Execute then
2191+ begin
2192+ LEncIndex := EncodingIndex;
2193+ LEncoding := StandardEncodingFromName(Encodings[LEncIndex]);
2194+
20752195 SaveFileName := FileName // prevent collision between TEditor.FileName and Dialog.FileName
2076- else begin
2196+ end
2197+ else
2198+ begin
20772199 Result := False;
20782200 Exit;
20792201 end ;
@@ -2086,8 +2208,7 @@ function TEditor.SaveAs: boolean;
20862208
20872209 // Try to save to disk
20882210 try
2089- UpdateEncoding(fFileName);
2090- fText.Lines.SaveToFile(SaveFileName);
2211+ fText.Lines.SaveToFile(SaveFileName, UpdateEncoding(SaveFileName, LEncoding));
20912212 fText.Modified := False;
20922213 fNew := False;
20932214 except
@@ -2104,7 +2225,7 @@ function TEditor.SaveAs: boolean;
21042225 if UnitIndex <> -1 then
21052226 MainForm.Project.SaveUnitAs(UnitIndex, SaveFileName); // save as new filename
21062227 end else
2107- fTabSheet.Caption := ExtractFileName(SaveFileName);
2228+ fTabSheet.Caption := ExtractFileName(SaveFileName) + CTAB_PADDING ;
21082229
21092230 // Update window captions
21102231 MainForm.UpdateAppTitle;
@@ -2336,7 +2457,7 @@ procedure TSynEditEx.ExecuteCommand(Command: TSynEditorCommand; AChar: WideChar;
23362457 OrigBlockEnd,
23372458 ' ' ,
23382459 smNormal);
2339- UndoList.AddChange(crDragDropInsert,
2460+ UndoList.AddChange(crInsert, // crDragDropInsert,
23402461 MoveDelim, // put at start of line me moved down
23412462 BlockEnd, // modified
23422463 SelText + #13 #10 + S,
@@ -2380,7 +2501,7 @@ procedure TSynEditEx.ExecuteCommand(Command: TSynEditorCommand; AChar: WideChar;
23802501 try
23812502 // backup original selection
23822503 UndoList.AddChange(crSelection, OrigBlockBegin, OrigBlockEnd, ' ' , smNormal);
2383- UndoList.AddChange(crDragDropInsert,
2504+ UndoList.AddChange(crInsert, // crDragDropInsert,
23842505 BlockBegin, // modified
23852506 MoveDelim, // put at end of line me moved up
23862507 S + #13 #10 + SelText,
0 commit comments