{ Seksi Commander ---------------------------- Licence : GNU GPL v 2.0 Author : radek.cervinka@centrum.cz storing commands (by file extensions) contributors: Copyright (C) 2008-2011 Koblov Alexander (Alexx2000@mail.ru) } unit uExts; {$mode objfpc}{$H+} interface uses Classes, Contnrs, uFile; type {en Class for storage actions by file extensions } TExtAction = class SectionName: String; //en< Section name, for example "[htm|html|mht]" Name: String; //en< File type name, for example "Hyper text documents" Icon: String; //en< Path to icon IconIndex: Integer; //en< Icon index (used in configuration dialog for paint icons) Extensions: TStringList; //en< List of extensions Actions: TStringList; //en< List of actions, for example "Open=opera '%f'" IsChanged: Boolean; //en< True if item was changed public {en Constructs an object and initializes its data before the object is first used. } constructor Create; {en Destroys an object and frees its memory. } destructor Destroy; override; end; {en Main class for storage actions list by file extensions } TExts = class private {en Return the number of items @returns(The number of items) } function GetCount: Integer; {en Get item by index @param(Index Item index) @returns(TExtAction item) } function GetItems(Index: Integer): TExtAction; protected {en Internal ObjectList for storage items. } FExtList:TObjectList; {en Return new section name for item by index @param(Index Item index) @returns(New section name) } function GetNewSectionName(Index: Integer): String; {en Erase section from file by section line index @param(extFile StringList with loaded extension file) @param(SectionIndex Section line index) @param(SkipComments If @true then don't delete comments) } procedure EraseSection(extFile : TStringList; var SectionIndex: Integer; SkipComments : Boolean = False); public {en Constructs an object and initializes its data before the object is first used. } constructor Create; {en Destroys an object and frees its memory. } destructor Destroy; override; {en Deletes all items. } procedure Clear; {en Inserts a new item at the end of the list @param(AExtAction TExtAction item) @returns(The index of the new item) } function AddItem(AExtAction: TExtAction): Integer; {en Removes the item at the position given by the Index parameter @param(Index Item index) } procedure DeleteItem(Index: Integer); {en Fills the actions list from file @param(sName File name) } procedure LoadFromFile(const sName:String); {en Save the actions list to file @param(sName File name) } procedure SaveToFile(const sName:String); {en Return action command by file and action name @param(aFile File for which action is sought) @param(sActionName Action name) @returns(Action command) } function GetExtActionCmd(aFile: TFile; const sActionName:String):String; {en Return list of actions by extension @param(File File which actions to retrieve) @param(slActions Actions list) @returns(The function returns @true if successful, @false otherwise) } function GetExtActions(aFile: TFile; var slActions:TStringList):Boolean; {en Indicates the number of items } property Count: Integer read GetCount; {en Give access to items by index } property Items[Index: Integer]: TExtAction read GetItems; end; const cMaskDefault = 'default'; cMaskFolder = 'folder'; cMaskFile = 'file'; implementation uses SysUtils, uLog, DCClassesUtf8, DCOSUtils, uDebug; constructor TExtAction.Create; begin Extensions := TStringList.Create; Actions := TStringList.Create; Actions.CaseSensitive:= False; end; destructor TExtAction.Destroy; begin if Assigned(Extensions) then FreeAndNil(Extensions); if Assigned(Actions) then FreeAndNil(Actions); inherited end; procedure TExts.LoadFromFile(const sName:String); var extFile: TStringListEx; sLine, s, sExt: String; extcmd: TExtAction; I, iIndex: Integer; begin extFile:= TStringListEx.Create; try extFile.LoadFromFile(sName); extcmd:=nil; for I:= 0 to extFile.Count - 1 do begin sLine:= extFile.Strings[I]; sLine:= Trim(sLine); if (sLine='') or (sLine[1]='#') then Continue; // writeln(sLine); if sLine[1]='[' then begin extCmd:= TExtAction.Create; FExtList.Add(extcmd); iIndex:=pos(']', sLine); if iIndex>0 then sLine:=Copy(sLine,1,iIndex) else logWrite('] not found in line '+sLine); { add | for easy searching in two and more extensions now I can search for example |pas| or |z| } extCmd.SectionName:=LowerCase(sLine); // fill extensions list s := LowerCase(sLine); Delete(s, 1, 1); // Delete '[' Delete(s, Length(s), 1); // Delete ']' s := s + '|'; while Pos('|', s) <> 0 do begin iIndex := Pos('|',s); sExt := Copy(s,1,iIndex-1); Delete(s, 1, iIndex); extCmd.Extensions.Add(sExt); end; end // end if.. '[' else begin // this must be a command if not assigned(extCmd) then begin logWrite('Command '+sLine+' have not defined extension - ignored.'); Continue; end; // now set command to lowercase s := sLine; for iIndex:=1 to Length(s) do begin if s[iIndex]='=' then Break; s[iIndex]:= LowerCase(s[iIndex]); end; // DCDebug(sLine); if Pos('name', s) = 1 then // File type name extCmd.Name := Copy(sLine, iIndex + 1, Length(sLine)) else if Pos('icon', s) = 1 then // File type icon extCmd.Icon := Copy(sLine, iIndex + 1, Length(sLine)) else // action extCmd.Actions.Add(sLine); end; end; finally extFile.Free; end; end; function TExts.GetNewSectionName(Index: Integer): String; var I, iCount: Integer; begin with GetItems(Index) do begin iCount := Extensions.Count - 1; Result := Extensions[0]; for I:= 1 to iCount do Result := Result + '|' + Extensions[I]; end; Result := '[' + Result + ']'; end; procedure TExts.EraseSection(extFile : TStringList; var SectionIndex: Integer; SkipComments : Boolean = False); var sLine : String; begin repeat if SkipComments and (Pos('#', Trim(extFile.Strings[SectionIndex]))=1) then Inc(SectionIndex) else extFile.Delete(SectionIndex); if SectionIndex >= extFile.Count then Exit; sLine := extFile.Strings[SectionIndex]; //DCDebug('sLine = ', sLine); until ((Pos('[', sLine)<>0) and (Pos(']', sLine)<>0)) or ((Pos('#', sLine)<>0) and (Pos('[', extFile.Strings[SectionIndex+1])<>0) and (Pos(']', extFile.Strings[SectionIndex+1])<>0)); end; procedure TExts.SaveToFile(const sName: String); var I, J, iIndex, iCount, iBegin, iEnd : Integer; extFile : TStringListEx; sLine, sNewName, sSectionName: String; bExists: Boolean; begin extFile := TStringListEx.Create; if mbFileExists(sName) then begin extFile.LoadFromFile(sName); // first rename sections if needed iCount := Count - 1; for I := 0 to iCount do with GetItems(I) do begin sNewName := GetNewSectionName(I); if (SectionName <> sNewName) and // SectionName might be empty for new items (SectionName <> '') then begin iIndex := extFile.IndexOf(SectionName); if iIndex >= 0 then begin extFile.Strings[iIndex] := sNewName; // Update section name so it doesn't get deleted below. SectionName := sNewName; end; end; end; // second delete old sections I := 0; iCount := extFile.Count - 1; while I <= iCount do begin sLine := Trim(extFile.Strings[I]); iBegin := Pos('[', sLine); iEnd := Pos(']', sLine); if (iBegin = 1) and (iEnd <> 0) then begin sSectionName := LowerCase(Copy(extFile.Strings[I], iBegin, iEnd)); bExists := False; for J := 0 to Count - 1 do begin //DCDebug('sSectionName = ', sSectionName); //DCDebug('GetItems(J).SectionName = ', GetItems(J).SectionName); if sSectionName = GetItems(J).SectionName then begin bExists := True; Break; end; end; // for if not bExists then // delete section begin EraseSection(extFile, I); iCount := extFile.Count - 1; end; end; Inc(I); end; // while // third rewrite changed sections iCount := Count - 1; for I := 0 to iCount do with GetItems(I) do begin if IsChanged then begin sNewName := GetNewSectionName(I); iIndex := extFile.IndexOf(sNewName); if iIndex >= 0 then // if section exists then insert actions begin Inc(iIndex); // skip section name EraseSection(extFile, iIndex, True); if Name <> '' then begin extFile.Insert(iIndex, 'Name=' + Name); Inc(iIndex); end; if Icon <> '' then begin extFile.Insert(iIndex, 'Icon=' + Icon); Inc(iIndex); end; for J := 0 to Actions.Count - 1 do begin extFile.Insert(iIndex, Actions.Strings[J]); Inc(iIndex); end; extFile.Insert(iIndex, ''); // add empty line end else // else add new section begin extFile.Add(sNewName); // section if Name <> '' then extFile.Add('Name=' + Name); // file type name if Icon <> '' then extFile.Add('Icon=' + Icon); // icon path for J := 0 to Actions.Count - 1 do extFile.Add(Actions.Strings[J]); extFile.Add(''); // add empty line end; end; end; end // mbFileExists else begin iCount := Count - 1; for I := 0 to iCount do with GetItems(I) do begin extFile.Add(GetNewSectionName(I)); if Name <> '' then extFile.Add('Name=' + Name); // file type name if Icon <> '' then extFile.Add('Icon=' + Icon); // icon path for J := 0 to Actions.Count - 1 do extFile.Add(Actions.Strings[J]); extFile.Add(''); // add empty line end; end; try extFile.SaveToFile(sName); except on e: EStreamError do DCDebug(Format('Error saving file associations to "%s": %s', [sName, e.Message])); end; extFile.Free; end; function TExts.GetExtActions(aFile: TFile; var slActions:TStringList):Boolean; var I: Integer; sMask: String; begin Result:= False; if aFile.IsDirectory or aFile.IsLinkToDirectory then sMask:= cMaskFolder else sMask:= LowerCase(aFile.Extension); if Length(sMask) <> 0 then for I:= 0 to FExtList.Count - 1 do with GetItems(i) do begin if Extensions.IndexOf(sMask) >= 0 then begin slActions.Assign(Actions); Result:= True; Break; end; end; if sMask = cMaskFolder then Exit; for I:= 0 to FExtList.Count - 1 do with GetItems(i) do begin if Extensions.IndexOf(cMaskFile) >= 0 then begin slActions.AddStrings(Actions); Result:= True; Break; end; end; end; function TExts.GetCount: Integer; begin Result := FExtList.Count; end; function TExts.GetItems(Index: Integer): TExtAction; begin Result := TExtAction(FExtList.Items[Index]); end; constructor TExts.Create; begin FExtList:=TObjectList.Create; end; destructor TExts.Destroy; begin if assigned(FExtList) then FreeAndNil(FExtList); inherited end; procedure TExts.Clear; begin FExtList.Clear; end; function TExts.AddItem(AExtAction: TExtAction): Integer; begin Result := FExtList.Add(AExtAction); end; procedure TExts.DeleteItem(Index: Integer); begin FExtList.Delete(Index); end; function TExts.GetExtActionCmd(aFile: TFile; const sActionName:String):String; var I: Integer; sMask: String; begin Result:= EmptyStr; if aFile.IsDirectory or aFile.IsLinkToDirectory then sMask:= cMaskFolder else sMask:= LowerCase(aFile.Extension); if Length(sMask) <> 0 then for I:= 0 to FExtList.Count - 1 do with GetItems(I) do begin if Extensions.IndexOf(sMask) >= 0 then begin Result:= Actions.Values[sActionName]; Exit; end; end; // if command not found then try to find default command for I:= 0 to FExtList.Count - 1 do with GetItems(I) do begin if Extensions.IndexOf(cMaskDefault) >= 0 then begin Result:=Actions.Values[sActionName]; Break; end; end; end; end.