mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-05 17:37:53 +02:00
406 lines
13 KiB
ObjectPascal
406 lines
13 KiB
ObjectPascal
{
|
|
***************************************************************************
|
|
* *
|
|
* This source is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This code is distributed in the hope that it will be useful, but *
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
|
* General Public License for more details. *
|
|
* *
|
|
* A copy of the GNU General Public License is available on the World *
|
|
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
|
|
* obtain it by writing to the Free Software Foundation, *
|
|
* Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
|
|
* *
|
|
***************************************************************************
|
|
|
|
Author: Mattias Gaertner
|
|
|
|
Abstract:
|
|
Dialog and functions to change encodings (e.g. UTF-8) of projects and packages.
|
|
}
|
|
unit ChgEncodingDlg;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
// RTL + FCL
|
|
Classes, SysUtils, RegExpr, Laz_AVL_Tree,
|
|
// LCL
|
|
Forms, Controls, ExtCtrls, StdCtrls, ComCtrls, Buttons,
|
|
// CodeTools
|
|
CodeCache, CodeToolManager, FileProcs,
|
|
// LazUtils
|
|
LConvEncoding, LazFileUtils, LazFileCache, LazStringUtils, LazUTF8, AvgLvlTree,
|
|
// IDEIntf
|
|
IdeIntfStrConsts, IDEWindowIntf, SrcEditorIntf, IDEHelpIntf, IDEImagesIntf,
|
|
// IDE
|
|
IDEProcs, PackageDefs, PackageSystem, Project, LazarusIDEStrConsts,
|
|
EnvironmentOpts, SearchPathProcs;
|
|
|
|
type
|
|
|
|
{ TChgEncodingDialog }
|
|
|
|
TChgEncodingDialog = class(TForm)
|
|
ApplyButton: TBitBtn;
|
|
HelpButton: TBitBtn;
|
|
BtnPanel: TPanel;
|
|
CloseButton: TBitBtn;
|
|
LabelNoPreview: TLabel;
|
|
RegExprErrorLabel: TLabel;
|
|
NewEncodingComboBox: TComboBox;
|
|
FileFilterCombobox: TComboBox;
|
|
NewEncodingLabel: TLabel;
|
|
PreviewListView: TListView;
|
|
PreviewGroupBox: TGroupBox;
|
|
OwnerComboBox: TComboBox;
|
|
ScopeGroupBox: TGroupBox;
|
|
RegExprCheckBox: TCheckBox;
|
|
FileFilterLabel: TLabel;
|
|
NonUTF8FilesCheckBox: TCheckBox;
|
|
UTF8FilesCheckBox: TCheckBox;
|
|
FilesGroupBox: TGroupBox;
|
|
procedure ApplyButtonClick(Sender: TObject);
|
|
procedure FormCreate(Sender: TObject);
|
|
procedure FormDestroy(Sender: TObject);
|
|
procedure HelpButtonClick(Sender: TObject);
|
|
procedure PreviewClick(Sender: TObject);
|
|
private
|
|
FFiles: TFilenameToStringTree;
|
|
function GetFiles: Boolean;
|
|
procedure UpdatePreview;
|
|
public
|
|
end;
|
|
|
|
function ShowConvertEncodingDlg: TModalResult;
|
|
|
|
implementation
|
|
|
|
{$R *.lfm}
|
|
|
|
function ShowConvertEncodingDlg: TModalResult;
|
|
var
|
|
ChgEncodingDialog: TChgEncodingDialog;
|
|
begin
|
|
ChgEncodingDialog:=TChgEncodingDialog.Create(nil);
|
|
Result:=ChgEncodingDialog.ShowModal;
|
|
ChgEncodingDialog.Free;
|
|
end;
|
|
|
|
{ TChgEncodingDialog }
|
|
|
|
procedure TChgEncodingDialog.FormCreate(Sender: TObject);
|
|
var
|
|
List: TStringListUTF8Fast;
|
|
Encoding: string;
|
|
i: Integer;
|
|
begin
|
|
IDEDialogLayoutList.ApplyLayout(Self);
|
|
|
|
Caption:=lisConvertEncoding;
|
|
|
|
ScopeGroupBox.Caption:=lisConvertProjectOrPackage;
|
|
NewEncodingLabel.Caption:=lisNewEncoding;
|
|
FilesGroupBox.Caption:=lisFileFilter;
|
|
UTF8FilesCheckBox.Caption:=lisFilesInASCIIOrUTF8Encoding;
|
|
NonUTF8FilesCheckBox.Caption:=lisFilesNotInASCIINorUTF8Encoding;
|
|
FileFilterLabel.Caption:=lisFilter;
|
|
RegExprCheckBox.Caption:=lisRegularExpression;
|
|
|
|
CloseButton.Caption:=lisClose;
|
|
ApplyButton.Caption:=lisConvert;
|
|
HelpButton.Caption:=lisHelp;
|
|
IDEImages.AssignImage(CloseButton, 'btn_close');
|
|
IDEImages.AssignImage(ApplyButton, 'btn_ok');
|
|
IDEImages.AssignImage(HelpButton, 'btn_help');
|
|
|
|
PreviewGroupBox.Caption:=dlgWRDPreview;
|
|
PreviewListView.Column[0].Caption:=dlgEnvFiles;
|
|
PreviewListView.Column[0].Width:=350;
|
|
PreviewListView.Column[1].Caption:=uemEncoding;
|
|
|
|
// get possible encodings
|
|
List:=TStringListUTF8Fast.Create;
|
|
GetSupportedEncodings(List);
|
|
for i:=List.Count-1 downto 0 do begin
|
|
Encoding:=List[i];
|
|
if (Encoding='') or (SysUtils.CompareText(Encoding,EncodingAnsi)=0) then
|
|
List.Delete(i);
|
|
end;
|
|
List.Sort;
|
|
NewEncodingComboBox.Items.Assign(List);
|
|
List.Free;
|
|
NewEncodingComboBox.Text:='UTF-8';
|
|
|
|
// get possible filters
|
|
List:=TStringListUTF8Fast.Create;
|
|
List.Add('*.pas;*.pp;*.p;*.inc;*.lpr;*.lfm;*.lrs;*.txt');
|
|
List.Sort;
|
|
FileFilterCombobox.Items.Assign(List);
|
|
List.Free;
|
|
FileFilterCombobox.Text:=FileFilterCombobox.Items[0];
|
|
|
|
// get possible projects and packages
|
|
List:=TStringListUTF8Fast.Create;
|
|
for i:=0 to PackageGraph.Count-1 do
|
|
if (List.IndexOf(PackageGraph[i].Name)<0)
|
|
and (not PackageGraph[i].ReadOnly)
|
|
and (not PackageGraph[i].IsVirtual)
|
|
then
|
|
List.Add(PackageGraph[i].Name);
|
|
List.Sort;
|
|
if not Project1.IsVirtual then
|
|
List.Insert(0,lisEdtDefCurrentProject);
|
|
OwnerComboBox.Items.Assign(List);
|
|
List.Free;
|
|
if OwnerComboBox.Items.Count>0 then
|
|
OwnerComboBox.Text:=OwnerComboBox.Items[0]
|
|
else
|
|
OwnerComboBox.Text:='';
|
|
FFiles:=TFilenameToStringTree.Create(FilenamesCaseSensitive);
|
|
UpdatePreview;
|
|
OwnerComboBox.DropDownCount:=EnvironmentOptions.DropDownCount;
|
|
NewEncodingComboBox.DropDownCount:=EnvironmentOptions.DropDownCount;
|
|
FileFilterCombobox.DropDownCount:=EnvironmentOptions.DropDownCount;
|
|
end;
|
|
|
|
procedure TChgEncodingDialog.FormDestroy(Sender: TObject);
|
|
begin
|
|
IDEDialogLayoutList.SaveLayout(Self);
|
|
FFiles.Free;
|
|
end;
|
|
|
|
procedure TChgEncodingDialog.ApplyButtonClick(Sender: TObject);
|
|
var
|
|
Buf: TCodeBuffer;
|
|
SrcEdit: TSourceEditorInterface;
|
|
Encoding, OldEncoding, NewEncoding: String;
|
|
Node: TAVLTreeNode;
|
|
Item: PStringToStringItem;
|
|
Filename: String;
|
|
HasChanged: boolean;
|
|
li, Cur: TListItem;
|
|
OldCount, i: Integer;
|
|
begin
|
|
HasChanged:=False;
|
|
NewEncoding:=NormalizeEncoding(NewEncodingComboBox.Text);
|
|
PreviewListView.BeginUpdate;
|
|
OldCount := PreviewListView.Items.Count;
|
|
Node:=FFiles.Tree.FindLowest;
|
|
while Node<>nil do begin
|
|
Item:=PStringToStringItem(Node.Data);
|
|
Filename:=Item^.Name;
|
|
Encoding:=Item^.Value;
|
|
|
|
Cur := PreviewListView.Items.FindCaption(0, Filename, False, True, False);
|
|
if Assigned(Cur) and Cur.Checked then
|
|
begin
|
|
DebugLn(['TChgEncodingDialog.ApplyButtonClick Filename=',Filename,' Encoding=',Encoding]);
|
|
Buf:=CodeToolBoss.LoadFile(Filename,true,false);
|
|
if (Buf<>nil) and (not Buf.ReadOnly) then begin
|
|
OldEncoding:=Buf.DiskEncoding;
|
|
SrcEdit:=SourceEditorManagerIntf.SourceEditorIntfWithFilename(Filename);
|
|
HasChanged:=true;
|
|
if SrcEdit<>nil then begin
|
|
DebugLn(['TChgEncodingDialog.ApplyButtonClick changing in source editor: ',Filename]);
|
|
Buf.DiskEncoding:=NewEncoding;
|
|
SrcEdit.Modified:=true;
|
|
end else begin
|
|
DebugLn(['TChgEncodingDialog.ApplyButtonClick changing on disk: ',Filename]);
|
|
// Buf:=CodeToolBoss.LoadFile(Filename,true,false);
|
|
Buf.DiskEncoding:=NewEncoding;
|
|
HasChanged:=Buf.Save;
|
|
if not HasChanged then
|
|
Buf.DiskEncoding:=OldEncoding;
|
|
end;
|
|
end;
|
|
if not HasChanged then begin
|
|
li:=PreviewListView.Items.Add;
|
|
li.Caption:=Filename;
|
|
li.SubItems.Add(Encoding);
|
|
li.Checked := True;
|
|
end;
|
|
|
|
end;
|
|
Node:=FFiles.Tree.FindSuccessor(Node);
|
|
end;
|
|
// Now delete all old nodes in PreviewListView
|
|
for i := OldCount - 1 downto 0 do PreviewListView.Items.Delete(i);
|
|
PreviewListView.EndUpdate;
|
|
PreviewGroupBox.Caption:=Format(lisEncodingNumberOfFilesFailed, [PreviewListView.Items.Count]);
|
|
end;
|
|
|
|
procedure TChgEncodingDialog.HelpButtonClick(Sender: TObject);
|
|
begin
|
|
LazarusHelp.ShowHelpForIDEControl(Self);
|
|
end;
|
|
|
|
procedure TChgEncodingDialog.PreviewClick(Sender: TObject);
|
|
begin
|
|
UpdatePreview;
|
|
end;
|
|
|
|
function TChgEncodingDialog.GetFiles: Boolean;
|
|
// Returns true if some files were found, even if they were not added to list.
|
|
var
|
|
AProject: TProject;
|
|
SearchPath: String;
|
|
APackage: TLazPackage;
|
|
Dir: String;
|
|
FileInfo: TSearchRec;
|
|
CurFilename: String;
|
|
Buf: TCodeBuffer;
|
|
CurEncoding, NewEncoding: String;
|
|
IncludeFilterRegExpr: TRegExpr;
|
|
CurOwner: TObject;
|
|
Expr: String;
|
|
ok: Boolean;
|
|
p: Integer;
|
|
begin
|
|
FFiles.Clear;
|
|
Result:=False;
|
|
// check owner
|
|
if OwnerComboBox.Text=lisEdtDefCurrentProject then
|
|
CurOwner:=Project1
|
|
else
|
|
CurOwner:=PackageGraph.FindPackageWithName(OwnerComboBox.Text,nil);
|
|
if CurOwner=nil then begin
|
|
DebugLn(['TChgEncodingDialog.UpdatePreview package not found: ',OwnerComboBox.Text]);
|
|
exit;
|
|
end;
|
|
|
|
// find search paths
|
|
if CurOwner is TProject then begin
|
|
AProject:=TProject(CurOwner);
|
|
SearchPath:=AProject.SourceDirectories.CreateSearchPathFromAllFiles;
|
|
SearchPath:=MergeSearchPaths(SearchPath,AProject.CompilerOptions.GetIncludePath(false));
|
|
end else begin
|
|
APackage:=TLazPackage(CurOwner);
|
|
SearchPath:=APackage.SourceDirectories.CreateSearchPathFromAllFiles;
|
|
SearchPath:=MergeSearchPaths(SearchPath,APackage.CompilerOptions.GetIncludePath(false));
|
|
end;
|
|
|
|
// find files
|
|
IncludeFilterRegExpr:=TRegExpr.Create;
|
|
try
|
|
Expr:=FileFilterCombobox.Text;
|
|
if not RegExprCheckBox.Checked then
|
|
Expr:=SimpleSyntaxToRegExpr(Expr);
|
|
ok:=false;
|
|
try
|
|
IncludeFilterRegExpr.Expression:=Expr;
|
|
IncludeFilterRegExpr.Compile;
|
|
ok:=true;
|
|
except
|
|
on E: Exception do begin
|
|
DebugLn('Invalid Include File Expression ',Expr,' ',E.Message);
|
|
RegExprErrorLabel.Caption:=E.Message;
|
|
end;
|
|
end;
|
|
RegExprErrorLabel.Visible := not ok;
|
|
if not ok then exit;
|
|
|
|
NewEncoding:=NormalizeEncoding(NewEncodingComboBox.Text);
|
|
p:=1;
|
|
while (p<=length(SearchPath)) do begin
|
|
Dir:=GetNextDirectoryInSearchPath(SearchPath,p);
|
|
if Dir='' then continue;
|
|
Dir:=AppendPathDelim(Dir);
|
|
DebugLn(['TChgEncodingDialog.GetFiles Dir=',Dir]);
|
|
if FindFirstUTF8(Dir+FileMask,faAnyFile,FileInfo)=0 then
|
|
try
|
|
repeat
|
|
// check if special file
|
|
if (FileInfo.Name='.') or (FileInfo.Name='..') or (FileInfo.Name='') then
|
|
continue;
|
|
CurFilename:=Dir+FileInfo.Name;
|
|
if FFiles.Contains(CurFilename) then
|
|
continue;
|
|
if not IncludeFilterRegExpr.Exec(CurFilename) then
|
|
continue;
|
|
if not FileIsTextCached(CurFilename) then
|
|
continue;
|
|
|
|
if (FileInfo.Attr and faDirectory)>0 then begin
|
|
// skip directory
|
|
end else begin
|
|
Buf:=CodeToolBoss.LoadFile(CurFilename,true,false);
|
|
if Buf<>nil then begin
|
|
//DebugLn(['TChgEncodingDialog.GetFiles Filename=',CurFilename,' Encoding=',NormalizeEncoding(Buf.DiskEncoding)]);
|
|
CurEncoding:=NormalizeEncoding(Buf.DiskEncoding);
|
|
Result:=True;
|
|
if CurEncoding=NewEncoding then
|
|
continue;
|
|
if (CurEncoding=EncodingUTF8) and (not UTF8FilesCheckBox.Checked) then
|
|
continue;
|
|
if (CurEncoding<>EncodingUTF8) and (not NonUTF8FilesCheckBox.Checked) then
|
|
continue;
|
|
FFiles[CurFilename]:=Buf.DiskEncoding;
|
|
end else begin
|
|
DebugLn(['TChgEncodingDialog.UpdatePreview read error: ',CurFilename]);
|
|
end;
|
|
end;
|
|
until FindNextUTF8(FileInfo)<>0;
|
|
finally
|
|
FindCloseUTF8(FileInfo);
|
|
end;
|
|
end;
|
|
finally
|
|
IncludeFilterRegExpr.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure TChgEncodingDialog.UpdatePreview;
|
|
var
|
|
Node: TAVLTreeNode;
|
|
Item: PStringToStringItem;
|
|
Filename: String;
|
|
Encoding: String;
|
|
li: TListItem;
|
|
HasFiles: Boolean;
|
|
IsDone: Boolean;
|
|
begin
|
|
Screen.BeginWaitCursor;
|
|
try
|
|
HasFiles:=GetFiles;
|
|
PreviewListView.Items.Clear;
|
|
|
|
IsDone:=HasFiles and (FFiles.Tree.Count=0);
|
|
PreviewGroupBox.Visible:=not IsDone;
|
|
LabelNoPreview.Visible:=IsDone;
|
|
LabelNoPreview.Caption:=lisFilesHaveRightEncoding;
|
|
ApplyButton.Enabled:=not IsDone;
|
|
if IsDone then exit;
|
|
|
|
PreviewListView.BeginUpdate;
|
|
Node:=FFiles.Tree.FindLowest;
|
|
while Node<>nil do begin
|
|
Item:=PStringToStringItem(Node.Data);
|
|
Filename:=Item^.Name;
|
|
Encoding:=Item^.Value;
|
|
DebugLn(['TChgEncodingDialog.UpdatePreview Filename=',Filename,' Encoding=',Encoding]);
|
|
li:=PreviewListView.Items.Add;
|
|
li.Caption:=Filename;
|
|
li.SubItems.Add(Encoding);
|
|
li.Checked := True;
|
|
Node:=FFiles.Tree.FindSuccessor(Node);
|
|
end;
|
|
PreviewListView.EndUpdate;
|
|
PreviewGroupBox.Caption:=
|
|
Format(lisNumberOfFilesToConvert, [IntToStr(PreviewListView.Items.Count)]);
|
|
ApplyButton.Enabled:=True;
|
|
finally
|
|
Screen.EndWaitCursor;
|
|
end;
|
|
end;
|
|
|
|
end.
|
|
|