diff options
Diffstat (limited to 'src/corelib/gfxbase.pas')
-rw-r--r-- | src/corelib/gfxbase.pas | 359 |
1 files changed, 359 insertions, 0 deletions
diff --git a/src/corelib/gfxbase.pas b/src/corelib/gfxbase.pas index 87cc4e54..17a6fd21 100644 --- a/src/corelib/gfxbase.pas +++ b/src/corelib/gfxbase.pas @@ -420,6 +420,69 @@ type constructor Create; property Text: string read DoGetText write DoSetText; end; + + TFileEntryType = (etFile, etDir); + TFileListSortOrder = (soNone, soFileName, soCSFileName, soFileExt, soSize, soTime); + + // A simple data object + TFileEntry = class(TObject) + private + FAttributes: longword; + FEntryType: TFileEntryType; + FExtention: string; + FGroupID: integer; // unix + FIsLink: boolean; + FLinkTarget: string; + FMode: longword; // unix + FModTime: TDateTime; + FName: string; + FOwnerID: integer; // unix + FSize: int64; + public + constructor Create; + property Name: string read FName write FName; + property Extention: string read FExtention write FExtention; + property Size: int64 read FSize write FSize; + property EntryType: TFileEntryType read FEntryType write FEntryType; + property IsLink: boolean read FIsLink write FIsLink; + property Attributes: longword read FAttributes write FAttributes; + property Mode: longword read FMode write FMode; // only used by unix OS's + property ModTime: TDateTime read FModTime write FModTime; + property OwnerID: integer read FOwnerID write FOwnerID; + property GroupID: integer read FGroupID write FGroupID; + property LinkTarget: string read FLinkTarget write FLinkTarget; + end; + + { TfpgFileListBase } + + TfpgFileListBase = class(TObject) + private + FEntries: TList; + FDirectoryName: TfpgString; + FFileMask: TfpgString; + FShowHidden: boolean; + FCurrentSpecialDir: integer; + procedure AddEntry(sr: TSearchRec); + function GetEntry(i: integer): TFileEntry; + function HasAttrib(fileAttrib, testAttrib: Integer): Boolean; + protected + FSpecialDirs: TStringList; + function InitializeEntry(sr: TSearchRec): TFileEntry; virtual; + function UpdateDirectory(const aDirectory: TfpgString): TfpgString virtual; + public + constructor Create; + destructor Destroy; override; + function Count: integer; + function CurrentSpecialDir: integer; + function ReadDirectory(const aDirectory: TfpgString = ''): boolean; + procedure Clear; + procedure Sort(AOrder: TFileListSortOrder); + property Entry[i: integer]: TFileEntry read GetEntry; + property SpecialDirs: TStringList read FSpecialDirs; + property DirectoryName: TfpgString read FDirectoryName; + property FileMask: TfpgString read FFileMask write FFileMask; + property ShowHidden: boolean read FShowHidden write FShowHidden; + end; { ******** Helper functions ******** } @@ -446,6 +509,7 @@ implementation uses fpgfx, // needed for fpgApplication + gfx_utils, // needed for fpgFileList typinfo; @@ -1698,5 +1762,300 @@ begin InitClipboard; end; +// Helper functions for TFileEntry and TfpgFileListBase + +function StringMatches(const astr, apat: string): boolean; +var + pati, si: longint; +begin + result := True; + pati := 1; + si := 1; + while result and (si <= length(astr)) and (pati <= length(apat)) do + begin + if (apat[pati] = '?') or (apat[pati] = astr[si]) then + begin + inc(si); + inc(pati); + end + else if (apat[pati] = '*') then + begin + while (pati <= length(apat)) and (apat[pati] in ['?','*']) do + inc(pati); + if pati > length(apat) then + begin + si := length(astr)+1; + Break; // * at the end + end; + + while (si <= length(astr)) and (astr[si] <> apat[pati]) do + inc(si); + if si > length(astr) then + result := False; + end + else + begin + result := False; + end; + end; + + result := result and (si > length(astr)); +end; + +// multiple patterns separated with ; +function FileNameMatches(const astr, apats: string): boolean; +var + cpat: string; + p: integer; + s: string; + astrupper: string; +begin + astrupper := UpperCase(astr); + result := False; + s := apats; + repeat + cpat := ''; + p := pos(';',s); + if p > 0 then + begin + cpat := copy(s, 1, p-1); + delete(s, 1, p); + end + else + begin + cpat := s; + s := ''; + end; { if/else } + cpat := UpperCase(trim(cpat)); + if cpat <> '' then + result := StringMatches(astrupper, cpat); + until result or (cpat = ''); +end; + +{ TFileEntry } + +constructor TFileEntry.Create; +begin + FAttributes := 0; + FMode := 0; + FSize := 0; + FIsLink := False; + FEntryType := etFile; +end; + +{ TfpgFileListBase } + +procedure TfpgFileListBase.AddEntry(sr: TSearchRec); +var + e: TFileEntry; +begin + e := InitializeEntry(sr); + if Assigned(e) then + FEntries.Add(e); +end; + +function TfpgFileListBase.HasAttrib(fileAttrib, testAttrib: Integer): Boolean; +begin + { HasAttrib() tests whether or not a file (with attributes fileAttrib) has the + testAttrib attribute bit set. } + Result := (fileAttrib and testAttrib) <> 0; +end; + +function TfpgFileListBase.GetEntry(i: integer): TFileEntry; +begin + if (i < 1) or (i > FEntries.Count) then + Result := nil + else + Result := TFileEntry(FEntries[i-1]); +end; + +function TfpgFileListBase.InitializeEntry(sr: TSearchRec): TFileEntry; +var + e: TFileEntry; + fullname: TfpgString; +begin + e := TFileEntry.Create; + e.Name := sr.Name; + e.Extention := ExtractFileExt(e.Name); + e.Size := sr.Size; + e.Attributes := sr.Attr; // this is incorrect and needs to improve! + e.EntryType := etFile; + fullname := FDirectoryName + e.Name; + e.IsLink := FileIsSymlink(fullname); + e.LinkTarget := ExtractTargetSymLinkPath(fullname); + e.ModTime := FileDateToDateTime(sr.Time); + + if HasAttrib(sr.Attr, faDirectory) then + e.EntryType := etDir + else + e.EntryType := etFile; + + if (e.Name = '.') or + ((e.Name = '..') and (FDirectoryName = '/')) or + (not FShowHidden and (Copy(e.Name, 1, 1) = '.') and (Copy(e.Name, 2, 1) <> '.')) or +// (not FShowHidden and HasAttrib(sr.Attr, faHidden)) or + ((e.EntryType = etFile) and not FileNameMatches(e.Name, FFileMask)) then + begin + // do not add this entry + Result := nil; + end else + Result := e; +end; + +function TfpgFileListBase.UpdateDirectory(const aDirectory: TfpgString): TfpgString; +{Sets up FSpecialDirs list} +var + i, n: integer; + rootadd: integer; +begin + // find insert position + i := 0; + while (i < FSpecialDirs.Count) + and (FSpecialDirs.Strings[i][1] <= aDirectory[1]) do + inc(i); + + n := 1; + rootadd := 1; + while n < Length(aDirectory) do + begin + if aDirectory[n] = DirectorySeparator then + begin + FSpecialDirs.Insert(i, Copy(aDirectory, 1, n-1+rootadd)); + rootadd := 0; + inc(i); + end; + inc(n); + end; + + FSpecialDirs.Insert(i, aDirectory); + FCurrentSpecialDir := i; + + Result := aDirectory; +end; + +constructor TfpgFileListBase.Create; +begin + FEntries := TList.Create; + FDirectoryName := ''; + FSpecialDirs := TStringList.Create; +end; + +destructor TfpgFileListBase.Destroy; +begin + Clear; + FSpecialDirs.Free; + FEntries.Free; + inherited Destroy; +end; + +function TfpgFileListBase.Count: integer; +begin + Result := FEntries.Count; +end; + +function TfpgFileListBase.CurrentSpecialDir: integer; +begin + Result := FCurrentSpecialDir; +end; + +function TfpgFileListBase.ReadDirectory(const aDirectory: TfpgString = ''): boolean; +var + SearchRec: TSearchRec; + dir: TfpgString; // to prevent FDirectoryName from having incorrect value +begin + // default parameter value is current directory + if aDirectory <> '' then + dir := ExpandFileName(aDirectory) + else + GetDir(0, dir); + + // vvzh: now we have to use SetCurrentDir in order to make ExpandFileName work + if not SetCurrentDir(dir) then + begin + Result := False; + Exit; //==> + end; + + FDirectoryName := UpdateDirectory(dir); + // Add PathDelim to end if it doesn't yet exist + FDirectoryName := IncludeTrailingPathDelimiter(FDirectoryName); + + Clear; + try + // The extra 'or' includes Normal attribute files under Windows. faAnyFile doesn't return those. + // Reported to FPC as bug 9440 in Mantis. + if SysUtils.FindFirst(FDirectoryName + '*', faAnyFile or $00000080, SearchRec) = 0 then + begin + AddEntry(SearchRec); + while SysUtils.FindNext(SearchRec) = 0 do + begin + AddEntry(SearchRec); + end; + end; + finally + SysUtils.FindClose(SearchRec); + end; +end; + +procedure TfpgFileListBase.Clear; +var + n: integer; +begin + for n := 0 to FEntries.Count-1 do + TFileEntry(FEntries[n]).Free; + FEntries.Clear; +end; + +procedure TfpgFileListBase.Sort(AOrder: TFileListSortOrder); +var + newl: TList; + n: integer; + i: integer; + e: TFileEntry; + + function IsBefore(newitem, item: TFileEntry): boolean; + begin + //if newitem.etype = etDir then writeln('dir: ',newitem.name,' (',item.name,')'); + if (newitem.EntryType = etDir) and (item.EntryType <> etDir) then + begin + result := true; + end + else if (newitem.EntryType <> etDir) and (item.EntryType = etDir) then + begin + result := false; + end + else if (newitem.EntryType = etDir) and (newitem.Name = '..') then + begin + result := true; + end + else if (item.EntryType = etDir) and (item.Name = '..') then + begin + result := false; + end + else + case AOrder of + soFileName : result := UpperCase(newitem.Name) < UpperCase(item.Name); + soCSFileName : result := newitem.Name < item.Name; + soFileExt : result := UpperCase(newitem.Extention+' '+newitem.Name) < UpperCase(item.Extention+' '+item.Name); + soSize : result := newitem.size < item.size; + soTime : result := newitem.modtime < item.modtime; + else + result := False; + end; + end; + +begin + newl := TList.Create; + for n := 0 to FEntries.Count-1 do + begin + e := TFileEntry(FEntries[n]); + i := 0; + while (i < newl.Count) and not IsBefore(e,TFileEntry(newl[i])) do inc(i); + newl.Insert(i,e); + end; + FEntries.Free; + FEntries := newl; +end; + end. |