unit MainUnit; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, Contnrs, TBX, TBXDkPanels, TB2Dock, ExtCtrls, TB2Item, TB2Toolbar, StdCtrls, ComCtrls, TBXOfficeXPTheme, TBXSwitcher, TB2ToolWindow, TB2ExtItems, TBXExtItems, ShellCtrls, ImgList, XPMan; const CM_INIT = WM_USER + 229; CDatFile = 'conversions.ini'; type TConversion = class OldClassName: string; NewClassName: string; CheckBox: TTBXCheckBox; end; { TConverterForm } TConverterForm = class(TForm) TBDock1: TTBDock; TBDock2: TTBDock; TBDock3: TTBDock; TBDock4: TTBDock; Panel1: TTBXDockablePanel; ConversionsPanel: TTBXAlignmentPanel; TBXLabel1: TTBXLabel; TBXLabel2: TTBXLabel; TBXAlignmentPanel1: TTBXAlignmentPanel; FilterPas: TTBXCheckBox; FilterHpp: TTBXCheckBox; FilterCPP: TTBXCheckBox; FilterDFM: TTBXCheckBox; FilterBinaryDFM: TTBXCheckBox; TBXToolbar1: TTBXToolbar; TBXItem1: TTBXItem; TBXItem2: TTBXItem; TBXSeparatorItem1: TTBXSeparatorItem; TBXToolbar2: TTBXToolbar; TBXVisibilityToggleItem1: TTBXVisibilityToggleItem; Memo: TMemo; TBXSwitcher1: TTBXSwitcher; TBXPageScroller1: TTBXPageScroller; TBXAlignmentPanel3: TTBXAlignmentPanel; ConversionCombo: TTBXComboBoxItem; TBXLabelItem1: TTBXLabelItem; Panel2: TPanel; Memo1: TMemo; FilePanel: TTBXDockablePanel; ShellTreeView: TShellTreeView; TBXVisibilityToggleItem2: TTBXVisibilityToggleItem; ImageList1: TImageList; TBXSeparatorItem3: TTBXSeparatorItem; XPManifest1: TXPManifest; procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure TBXItem1Click(Sender: TObject); procedure TBXItem2Click(Sender: TObject); procedure ConversionComboPopup(Sender: TTBCustomItem; FromLink: Boolean); procedure ConversionComboChange(Sender: TObject; const Text: String); procedure ShellTreeViewAddFolder(Sender: TObject; AFolder: TShellFolder; var CanAdd: Boolean); procedure FilterChange(Sender: TObject); procedure ShellTreeViewDblClick(Sender: TObject); procedure TBXVisibilityToggleItem2Click(Sender: TObject); procedure MemoDragDrop(Sender, Source: TObject; X, Y: Integer); procedure MemoDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); procedure ShellTreeViewEditing(Sender: TObject; Node: TTreeNode; var AllowEdit: Boolean); private FConversions: TObjectList; procedure CMInit(var Message: TMessage); message CM_INIT; procedure WMDropFiles(var Message: TWMDropFiles); message WM_DROPFILES; protected procedure ShowConversions; procedure GetConversionTypes(L: TStrings); procedure GetConversions(const ConvType: string); public procedure ProcessFiles(FileNames: TStringList); end; var ConverterForm: TConverterForm; implementation uses ShellApi; const CNameFirst = ['a'..'z', 'A'..'Z', '_']; CNameChar = ['a'..'z', 'A'..'Z', '_', '0'..'9']; type TTreeViewAccess = class(TCustomTreeView); function WordReplace(const Src, OldWord, NewWord: string): string; var P, PStart: PChar; S: string; begin Result := ''; P := PChar(Src); PStart := P; while P^ <> #0 do begin if P^ in CNameFirst then begin SetString(S, PStart, P - PStart); Result := Result + S; PStart := P; Inc(P); while P^ in CNameChar do Inc(P); SetString(S, PStart, P - PStart); if SameText(S, OldWord) then S := NewWord; Result := Result + S; PStart := P; end else Inc(P); end; if P <> PStart then begin SetString(S, PStart, P - PStart); Result := Result + S; end; end; {$R *.dfm} { TConverterForm } procedure TConverterForm.CMInit(var Message: TMessage); begin DragAcceptFiles(Handle, True); ShowConversions; TTreeViewAccess(ShellTreeView).MultiSelect := True; TTreeViewAccess(ShellTreeView).MultiSelectStyle := [msControlSelect, msShiftSelect, msSiblingOnly]; end; procedure TConverterForm.FormCreate(Sender: TObject); begin FConversions := TObjectList.Create; GetConversionTypes(ConversionCombo.Strings); if ConversionCombo.Strings.Count = 0 then raise Exception.Create('Invalid conversion settings'); ConversionCombo.ItemIndex := 0; GetConversions(ConversionCombo.Strings[0]); PostMessage(Handle, CM_INIT, 0, 0); end; procedure TConverterForm.FormDestroy(Sender: TObject); begin FConversions.Free; end; procedure TConverterForm.ProcessFiles(FileNames: TStringList); var I, J: Integer; S, E: string; BinaryStream: TFileStream; StringStream: TStringStream; S1, S2: string; DFM, BinaryDFM: Boolean; function IsTextDFM(const FN: string): Boolean; var Stream: TFileStream; Buffer: array [0..7] of Char; begin Stream := TFileStream.Create(FN, fmOpenRead); FillChar(Buffer[0], 8, 0); Stream.Read(Buffer[0], 7); Result := SameText(Buffer, 'object '); Stream.Free; end; function IsBinaryDFM(const FN: string): Boolean; var Stream: TFileStream; TS: TStream; begin Result := True; Exit; Stream := TFileStream.Create(FN, fmOpenRead); try TS := TMemoryStream.Create; try try Result := True; ObjectResourceToText(Stream, TS); except Result := False; end; finally TS.Free; end; finally Stream.Free; end; end; function GetBackupFileName(F: string): string; var I: Integer; begin I := 0; repeat Inc(I); Result := Format('%s.###%.3d', [F, I]); until not FileExists(Result); end; begin { Filter files } for I := FileNames.Count - 1 downto 0 do begin S := FileNames[I]; E := ExtractFileExt(S); if (SameText(E, '.pas') and FilterPAS.Checked) or ((SameText(E, '.h') or SameText(E, '.hpp')) and FilterHPP.Checked) or (SameText(E, '.cpp') and FilterCPP.Checked) or (SameText(E, '.dfm') and FilterDFM.Checked and IsTextDFM(S)) or (SameText(E, '.dfm') and FilterBinaryDFM.Checked and IsBinaryDFM(S)) then Continue else FileNames.Delete(I); end; if FileNames.Count = 0 then Memo.Lines.Add('No files to process'); { Get each file as string } for I := FileNames.Count - 1 downto 0 do begin DFM := SameText(ExtractFileExt(FileNames[I]), '.dfm'); if DFM then BinaryDFM := not IsTextDFM(FileNames[I]) else BinaryDFM := False; if DFM and BinaryDFM then begin BinaryStream := TFileStream.Create(FileNames[I], fmOpenRead); try StringStream := TStringStream.Create(''); try ObjectResourceToText(BinaryStream, StringStream); S := StringStream.DataString; finally StringStream.Free; end; finally BinaryStream.Free; end; end else begin with TFileStream.Create(FileNames[I], fmOpenRead) do try SetLength(S, Size); ReadBuffer(S[1], Size); finally Free; end; end; Memo.Lines.Add('Converting ' + FileNames[I]); { Now S contains text representation of the file } for J := 0 to FConversions.Count - 1 do with TConversion(FConversions[J]) do if CheckBox.Checked then if DFM then begin S1 := ': ' + OldClassName + #13; S2 := ': ' + NewClassName + #13; S := StringReplace(S, S1, S2, [rfReplaceAll, rfIgnoreCase]); end else begin S := WordReplace(S, OldClassName, NewClassName); end; E := GetBackupFileName(FileNames[I]); Memo.Lines.Add('Saving original file to ' + E); RenameFile(FileNames[I], E); if DFM and BinaryDFM then begin BinaryStream := TFileStream.Create(FileNames[I], fmCreate); try StringStream := TStringStream.Create(S); try ObjectTextToResource(StringStream, BinaryStream); finally StringStream.Free; end; finally BinaryStream.Free; end; end else begin with TFileStream.Create(FileNames[I], fmCreate) do try WriteBuffer(S[1], Length(S)); finally Free; end; end; end; end; procedure TConverterForm.ShowConversions; var I: Integer; C: TConversion; CB: TTBXCheckBox; begin ConversionsPanel.DisableAlign; for I := ConversionsPanel.ControlCount - 1 downto 0 do ConversionsPanel.Controls[I].Free; for I := 0 to FConversions.Count - 1 do begin C := TConversion(FConversions[I]); CB := TTBXCheckBox.Create(Self); CB.Caption := C.OldClassName + ' > ' + C.NewClassName; CB.Wrapping := twEndEllipsis; CB.Top := 10000; CB.Parent := ConversionsPanel; CB.Align := alTop; CB.Checked := True; C.CheckBox := CB; end; ConversionsPanel.EnableAlign; ConversionsPanel.Realign; end; procedure TConverterForm.WMDropFiles(var Message: TWMDropFiles); var SL: TStringList; Buffer: array [0..1024] of Char; NumFiles, I: Integer; S: string; begin SL := TStringList.Create; try NumFiles := DragQueryFile(Message.Drop, $FFFFFFFF, nil, 0); for I := 0 to NumFiles - 1 do begin DragQueryFile(Message.Drop,I, Buffer, SizeOf(Buffer)); S := StrPas(Buffer); if FileExists(S) then SL.Add(S); end; if SL.Count > 0 then ProcessFiles(SL); finally SL.Free; end; end; procedure TConverterForm.TBXItem1Click(Sender: TObject); var I: Integer; begin for I := 0 to FConversions.Count - 1 do TConversion(FConversions[I]).CheckBox.Checked := True; end; procedure TConverterForm.TBXItem2Click(Sender: TObject); var I: Integer; begin for I := 0 to FConversions.Count - 1 do TConversion(FConversions[I]).CheckBox.Checked := False; end; procedure TConverterForm.ConversionComboPopup(Sender: TTBCustomItem; FromLink: Boolean); begin GetConversionTypes(ConversionCombo.Strings); end; procedure TConverterForm.GetConversionTypes(L: TStrings); var SL: TStringList; S: string; I, J: Integer; begin SL := TStringList.Create; try SL.LoadFromFile(CDatFile); L.Clear; for I := 0 to SL.Count - 1 do begin S := Trim(SL[I]); if (Length(S) > 3) and (S[1] = '[') and (S[Length(S)] = ']') then begin S := Copy(S, 2, Length(S) - 2); L.Add(S); end; end; finally SL.Free; end; end; procedure TConverterForm.ConversionComboChange(Sender: TObject; const Text: String); begin GetConversions(Text); ShowConversions; end; procedure TConverterForm.GetConversions(const ConvType: string); var SL: TStringList; S, S1, S2: string; P: Integer; C: TConversion; I: Integer; begin SL := TStringList.Create; try SL.LoadFromFile(CDatFile); I := 0; while I < SL.Count - 1 do begin S := Trim(SL[I]); if (Length(S) > 3) and (S[1] = '[') and (S[Length(S)] = ']') and SameText(Copy(S, 2, Length(S) - 2), ConvType) then Break else Inc(I); end; FConversions.Clear; Inc(I); while I < SL.Count do begin S := Trim(SL[I]); Inc(I); if (Length(S) = 0) or (S[1] in [';', '''', '/', '%']) then Continue; if S[1] = '[' then Break; P := Pos('->', S); if P < 2 then raise Exception.Create('Invalid conversion settings'); S1 := Trim(Copy(S, 1, P - 1)); S2 := Trim(Copy(S, P + 2, Length(S))); if (Length(S1) = 0) or (Length(S2) = 0) then raise Exception.Create('Invalid conversion settings'); C := TConversion.Create; FConversions.Add(C); C.OldClassName := S1; C.NewClassName := S2; end; finally SL.Free; end; end; procedure TConverterForm.ShellTreeViewAddFolder(Sender: TObject; AFolder: TShellFolder; var CanAdd: Boolean); var Ext: string; begin if not AFolder.IsFolder then begin Ext := LowerCase(ExtractFileExt(AFolder.DisplayName)); CanAdd := (FilterPas.Checked and (Ext = '.pas')) or (FilterHpp.Checked and ((Ext = '.h') or (Ext = '.hpp'))) or (FilterCpp.Checked and (Ext = '.cpp')) or ((FilterDfm.Checked or FilterBinaryDfm.Checked) and (Ext = '.dfm')); end; end; procedure TConverterForm.FilterChange(Sender: TObject); begin if FilePanel.Visible then ShellTreeView.Refresh(ShellTreeView.Items[0]); end; procedure TConverterForm.ShellTreeViewDblClick(Sender: TObject); var SL: TStringList; begin SL := TStringList.Create; try SL.Add(ShellTreeView.SelectedFolder.PathName); if FileExists(SL[0]) then ProcessFiles(SL); finally SL.Free; end; end; procedure TConverterForm.TBXVisibilityToggleItem2Click(Sender: TObject); begin if FilePanel.Visible then ShellTreeView.Refresh(ShellTreeView.Items[0]); end; procedure TConverterForm.MemoDragDrop(Sender, Source: TObject; X, Y: Integer); var I: Integer; SL: TStringList; begin if Source <> ShellTreeView then Exit; SL := TStringList.Create; try for I := 0 to ShellTreeView.SelectionCount - 1 do SL.Add(TShellFolder(ShellTreeView.Selections[I].Data).PathName); ProcessFiles(SL); finally SL.Free; end; end; procedure TConverterForm.MemoDragOver(Sender, Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); var I: Integer; begin if Source <> ShellTreeView then Exit; for I := 0 to ShellTreeView.SelectionCount - 1 do if not FileExists(TShellFolder(ShellTreeView.Selections[I].Data).PathName) then begin Accept := False; Break; end; end; procedure TConverterForm.ShellTreeViewEditing(Sender: TObject; Node: TTreeNode; var AllowEdit: Boolean); begin AllowEdit := False; end; end.