ソースを参照

browse directory on macos

circular17 6 年 前
コミット
771eb7d3bc
3 ファイル変更177 行追加37 行削除
  1. 33 19
      lazpaint/dialog/ubrowseimages.pas
  2. 143 17
      lazpaint/ufilesystem.pas
  3. 1 1
      lazpaint/umenu.pas

+ 33 - 19
lazpaint/dialog/ubrowseimages.pas

@@ -67,6 +67,9 @@ type
     procedure ToolButton_ViewBigIconClick(Sender: TObject);
     procedure Tool_SelectDriveClick(Sender: TObject);
     function OnDeleteConfirmation({%H-}AForm:TForm; const AFiles: array of string; AContained: boolean): boolean;
+  private
+    function GetCurrentDirectory: string;
+    procedure SetCurrentDirectory(AValue: string);
   private
     FLazPaintInstance: TLazPaintCustomInstance;
     FDefaultExtension: string;
@@ -124,6 +127,7 @@ type
     procedure DeleteSelectedFiles;
     procedure SelectFile(AName: string);
     procedure PreviewValidate({%H-}ASender: TObject);
+    property CurrentDirectory: string read GetCurrentDirectory write SetCurrentDirectory;
   public
     { public declarations }
     ShowRememberStartupDirectory: boolean;
@@ -295,8 +299,7 @@ begin
   BGRAPaintNet.RegisterPaintNetFormat;
   BGRAOpenRaster.RegisterOpenRasterFormat;
 
-  FFileSystems := FileManager.GetFileSystems;
-  if length(FFileSystems)>0 then
+  if FileManager.CanGetFileSystems then
   begin
     Tool_SelectDrive.Visible := true;
   end else
@@ -335,8 +338,8 @@ begin
   if not IsSaveDialog then FFilename:= FPreviewFilename;
   Timer1.Enabled := false;
   vsList.Anchors := [akLeft,akTop,akRight,akBottom];
-  FLastDirectory := DirectoryEdit1.Text;
-  DirectoryEdit1.Text := '';
+  FLastDirectory := CurrentDirectory;
+  CurrentDirectory := '';
   UpdatePreview('');
 end;
 
@@ -403,9 +406,9 @@ begin
   end;
   if FLastBigIcon then ViewBigIcons;
   if (FLastDirectory = '') or not FileManager.IsDirectory(FLastDirectory) then
-    DirectoryEdit1.Text := DefaultPicturesDirectory
+    CurrentDirectory := DefaultPicturesDirectory
   else
-    DirectoryEdit1.Text := FLastDirectory;
+    CurrentDirectory := FLastDirectory;
   Timer1.Enabled := true;
   vsList.Anchors := [akLeft,akTop];
   ShellListView1.SetFocus;
@@ -449,8 +452,8 @@ procedure TFBrowseImages.ListBox_RecentDirsClick(Sender: TObject);
 begin
   if ListBox_RecentDirs.ItemIndex <> -1 then
   begin
-    if ChompPathDelim(DirectoryEdit1.Text) <> ChompPathDelim(ListBox_RecentDirs.Items[ListBox_RecentDirs.ItemIndex]) then
-      DirectoryEdit1.Text := AppendPathDelim(ListBox_RecentDirs.Items[ListBox_RecentDirs.ItemIndex]);
+    if ChompPathDelim(CurrentDirectory) <> ChompPathDelim(ListBox_RecentDirs.Items[ListBox_RecentDirs.ItemIndex]) then
+      CurrentDirectory := AppendPathDelim(ListBox_RecentDirs.Items[ListBox_RecentDirs.ItemIndex]);
   end;
 end;
 
@@ -604,13 +607,13 @@ var
   newName: String;
   newFullname: string;
 begin
-  if pos(PathDelim, DirectoryEdit1.Text) = 0 then exit;
+  if pos(PathDelim, CurrentDirectory) = 0 then exit;
   newName := InputBox(FCreateFolderOrContainerCaption, rsEnterFolderOrContainerName, '');
   if newName = '' then exit;
   if (pos(':',newName) <> 0) or (pos('\',newName) <> 0) then
     MessageDlg(rsInvalidName, mtError, [mbOK], 0) else
   begin
-    newFullname := ChompPathDelim(DirectoryEdit1.Text)+PathDelim+newName;
+    newFullname := ChompPathDelim(CurrentDirectory)+PathDelim+newName;
     if FileManager.IsDirectory(newFullname) then
       MessageDlg(rsFolderOrContainerAlreadyExists, mtInformation, [mbOK], 0)
     else
@@ -667,7 +670,7 @@ end;
 
 procedure TFBrowseImages.Tool_SelectDriveClick(Sender: TObject);
 begin
-  DirectoryEdit1.Text := ':';
+  CurrentDirectory := ':';
 end;
 
 function TFBrowseImages.OnDeleteConfirmation(AForm: TForm;
@@ -688,6 +691,17 @@ begin
     result := true;
 end;
 
+function TFBrowseImages.GetCurrentDirectory: string;
+begin
+  result := DirectoryEdit1.Text;
+end;
+
+procedure TFBrowseImages.SetCurrentDirectory(AValue: string);
+begin
+  DirectoryEdit1.Text := AValue;
+  ResetDirectory(False);
+end;
+
 procedure TFBrowseImages.UpdateToolButtonOpen;
 var chosenFilename: string;
 begin
@@ -838,7 +852,7 @@ var I: integer;
 begin
   ListBox_RecentDirs.ItemIndex := -1;
   for I := 0 to ListBox_RecentDirs.Count-1 do
-    if ChompPathDelim(ListBox_RecentDirs.Items[i]) = ChompPathDelim(DirectoryEdit1.Text) then
+    if ChompPathDelim(ListBox_RecentDirs.Items[i]) = ChompPathDelim(CurrentDirectory) then
     begin
       ListBox_RecentDirs.ItemIndex:= I;
       break;
@@ -936,7 +950,7 @@ begin
     fullName := ShellListView1.ItemFullName[ShellListView1.SelectedIndex];
     if ShellListView1.ItemIsFolder[ShellListView1.SelectedIndex] then
     begin
-      DirectoryEdit1.Text := fullName;
+      CurrentDirectory := fullName;
       InFilenameChange := true;
       Edit_Filename.text := '';
       InFilenameChange := false;
@@ -977,10 +991,10 @@ begin
       ModalResult:= mrOk;
     end;
   end else
-    if IsSaveDialog and (Trim(Edit_Filename.Text)<>'') and (DirectoryEdit1.Text <> ':') and
-      FileManager.IsDirectory(trim(DirectoryEdit1.Text)) then
+    if IsSaveDialog and (Trim(Edit_Filename.Text)<>'') and (CurrentDirectory <> ':') and
+      FileManager.IsDirectory(trim(CurrentDirectory)) then
     begin
-      FFilename:= IncludeTrailingPathDelimiter(trim(DirectoryEdit1.Text))+Edit_Filename.Text;
+      FFilename:= IncludeTrailingPathDelimiter(trim(CurrentDirectory))+Edit_Filename.Text;
       if (ExtractFileExt(FFilename)='') then
       begin
         if (ComboBox_FileExtension.ItemIndex > 0) then
@@ -1004,15 +1018,15 @@ procedure TFBrowseImages.GoDirUp;
 var dir: string;
   itemToSelect: string;
 begin
-  dir := DirectoryEdit1.Text;
+  dir := CurrentDirectory;
   FileManager.RemoveLastPathElement(dir, itemToSelect);
   if dir = '' then
   begin
     FFileSystems:= FileManager.GetFileSystems;
-    if length(FFileSystems)>0 then DirectoryEdit1.Text := ':';
+    if length(FFileSystems)>0 then CurrentDirectory := ':';
     itemToSelect := '';
   end else
-    DirectoryEdit1.Text := dir;
+    CurrentDirectory := dir;
   ShellListView1.SetFocus;
   UpdatePreview('');
   InFilenameChange := true;

+ 143 - 17
lazpaint/ufilesystem.pas

@@ -28,11 +28,15 @@ const
 
   //more Linux file systems
   fsSysV = 'sysv';     // Unix System V
-  rsXFS = 'XFS';       //IRIX
-  rsJFS = 'JFS';       //AIX journaled file system
-  rsXiaFS = 'xiafs';   //extension of Minix file system
+  fsXFS = 'XFS';       //IRIX
+  fsJFS = 'JFS';       //AIX journaled file system
+  fsXiaFS = 'xiafs';   //extension of Minix file system
   fsReiserFS = 'Reiserfs';  //ReiserFS
 
+  //Apple file systems
+  fsAPFS = 'APFS';
+  fsHFS = 'HFS';
+
   //Misc
   fsHPFS = 'HPFS';     // OS/2 'High Performance File System'
   fsNWFS = 'NWFS';     // Novel NetWare File System
@@ -73,6 +77,7 @@ type
     function RemovePathTrail(ADir: string): string;
     procedure RemoveLastPathElement(var ADir: string; out ALastElement: string);
     function GetFileSystems: TFileSystemArray;
+    function CanGetFileSystems: boolean;
     function MoveToTrash(AForm: TForm; const AFilenamesUTF8: array of string; AConfirmationCallback: TDeleteConfirmationFunction): boolean;
     function CreateFileStream(AFilenameUTF8: string; AMode: Word): TStream; overload;
     procedure CancelStreamAndFree(AStream: TStream);
@@ -96,7 +101,7 @@ implementation
 uses BGRAUTF8, BGRAWinResource, BGRALazResource, LazFileUtils, Dialogs
 {$IFDEF WINDOWS}, Windows{$ENDIF}
 {$IFDEF LINUX}, Process{$ENDIF}
-;
+{$IFDEF DARWIN}, Process{$ENDIF};
 
 type
   TExtendedFilename = record
@@ -104,11 +109,29 @@ type
     SubFilename: string;
   end;
 
+procedure LinuxBundleToFileSystem(ABundle: string; out AFilesystem: string;
+      out ALongFilenames: boolean; out ACaseSensitive: boolean);
+begin
+  if (ABundle = 'ntfs') or (ABundle = 'fuseblk') then AFilesystem := fsNTFS else
+  if (ABundle = 'msdos') or (ABundle = 'umsdos') or (ABundle='vfat') then AFilesystem := fsFAT else
+  if ABundle = 'iso9660' then AFilesystem:= fsCDFS else
+  if ABundle = 'hpfs' then AFilesystem := fsHPFS else
+  if ABundle = 'udf' then AFilesystem := fsUDF else
+  if ABundle = 'ncp' then AFilesystem := fsNWFS else
+  if ABundle = 'apfs' then AFilesystem := fsAPFS else
+  if ABundle = 'hfs' then AFilesystem := fsHFS else
+  if ABundle = 'exfat' then AFilesystem := fsExFAT else
+    AFilesystem := ABundle;
+  ALongFilenames := (ABundle <> 'minix') and (ABundle <> 'msdos');
+  ACaseSensitive := (ABundle <> 'msdos') and (ABundle <> 'umsdos') and (ABundle <> 'vfat')
+                and (ABundle <> 'exfat');
+end;
+
 {$IFDEF LINUX}
-const LinuxFileSystems: array[0..20] of string =
+const LinuxFileSystems: array[0..21] of string =
  ('minix', 'ext2', 'ext3', 'ext4',
   'sysv', 'XFS', 'JFS', 'xiafs', 'Reiserfs',
-  {FAT} 'msdos', 'umsdos', 'vfat', {NTFS} 'ntfs', 'fuseblk',
+  {FAT} 'msdos', 'umsdos', 'vfat', 'exfat', {NTFS} 'ntfs', 'fuseblk',
   {CDFS} 'iso9660', {UDF} 'udf',
   {HPFS} 'hpfs', {NWFS} 'ncp',
   'nfs', 'smb', 'ncpfs');
@@ -179,10 +202,6 @@ begin
               fileSystem := lFileSystem;
               path := UnespacePath(parsedDesc[1]);
               device := parsedDesc[0];
-              longFilenames := (fileSystem <> 'minix') and
-                (fileSystem <> 'msdos');
-              caseSensitive := (fileSystem <> 'msdos') and
-                (fileSystem <> 'umsdos') and (fileSystem <> 'vfat');
               readonly:= (copy(parsedDesc[3],1,3) <> 'rw,') and (parsedDesc[3]<>'rw');
 
               //detecting device type
@@ -221,12 +240,7 @@ begin
                 name := ExtractFileName(path);
 
               //formatting file system
-              if (fileSystem = 'ntfs') or (fileSystem = 'fuseblk') then fileSystem := fsNTFS else
-              if (fileSystem = 'msdos') or (fileSystem = 'umsdos') or (fileSystem='vfat') then fileSystem := fsFAT else
-              if fileSystem = 'iso9660' then fileSystem:= fsCDFS else
-              if fileSystem = 'hpfs' then fileSystem := fsHPFS else
-              if fileSystem = 'udf' then fileSystem := fsUDF else
-              if fileSystem = 'ncp' then fileSystem := fsNWFS;
+              LinuxBundleToFileSystem(fileSystem, fileSystem, longFilenames, caseSensitive);
             end;
           end;
         end;
@@ -294,6 +308,105 @@ begin
 end;
 {$ENDIF}
 
+{$IFDEF DARWIN}
+var
+  darwinFilesystemsDate: TDateTime;
+  darwinFilesystemsCached: TFileSystemArray;
+
+function GetDarwinFileSystems: TFileSystemArray;
+  procedure FindDevices;
+  var
+    runResult, headers, curLine, fs, mountPath: string;
+    lines: TStringList;
+    blocksPos, mountedPos, i, endFS: integer;
+    count: integer;
+  begin
+    if not RunCommand('df',['-P'],runResult) then exit;
+    lines := TStringList.Create;
+    lines.Text:= runResult;
+    headers := lines[0];
+    blocksPos := pos('-blocks', headers);
+    mountedPos := pos('Mounted on', headers);
+    if (blocksPos <> 0) and (mountedPos <> 0) then
+    begin
+      inc(blocksPos, 5);
+      count := 0;
+      setlength(result, lines.Count-1);
+      for i := 1 to lines.Count-1 do
+      begin
+        curLine := lines[i];
+        endFS := blocksPos;
+        if endFS > length(curLine) then continue;
+        while (endFS > 1) and (curLine[endFS] in['0'..'9']) do dec(endFS);
+        while (endFS > 1) and (curLine[endFS] in[#0..#32]) do dec(endFS);
+        fs := copy(curLine,1,endFS);
+        if fs.StartsWith('/dev/') then
+        begin
+          mountPath := copy(curLine, mountedPos, length(curLine)-mountedPos+1);
+          if (mountPath <> '/var/vm') and (mountPath <> '/private/var/vm') then
+          begin
+            result[count].path := mountPath;
+            inc(count);
+          end;
+        end;
+      end;
+      setlength(result, count);
+    end;
+    lines.Free;
+  end;
+
+  procedure FetchDiskInfo(var fsi: TFileSystemInfo);
+  var
+    runResult, curLine, key, value: string;
+    lines: TStringList;
+    posColon: SizeInt;
+    i: Integer;
+  begin
+    if not RunCommand('diskutil',['info',fsi.path],runResult) then exit;
+    lines := TStringList.Create;
+    lines.Text:= runResult;
+    fsi.name:= '';
+    fsi.device := '?';
+    fsi.fileSystem := '?';
+    fsi.longFilenames:= true;
+    for i := 0 to lines.Count-1 do
+    begin
+      curLine := lines[i];
+      posColon := pos(':',curLine);
+      if posColon <> 0 then
+      begin
+        key := copy(curLine,1,posColon-1).TrimLeft;
+        value := copy(curLine,posColon+1,length(curLine)-posColon).Trim;
+        if key = 'Optical Drive Type' then fsi.device := rsCdRom else
+        if (key = 'Removable Media') and (fsi.device = '?') then
+        begin
+          if value = 'Fixed' then fsi.device := rsFixedDrive
+          else fsi.device := rsRemovableDrive;
+        end else
+        if key = 'Type (Bundle)' then
+        begin
+          LinuxBundleToFileSystem(value, fsi.fileSystem, fsi.longFilenames, fsi.caseSensitive);
+        end else
+        if key = 'Volume Name' then fsi.name:= value else
+        if key = 'Read-Only Volume' then fsi.readonly:= value='Yes';
+      end;
+    end;
+    lines.Free;
+  end;
+
+var i: integer;
+begin
+  if (darwinFilesystemsDate <> 0) and (Now < darwinFilesystemsDate + (10/(60*60*24))) then
+    exit(darwinFilesystemsCached);
+  result := nil;
+  FindDevices;
+  for i := 0 to high(result) do
+    FetchDiskInfo(result[i]);
+  darwinFilesystemsCached := result;
+  darwinFilesystemsDate:= Now;
+end;
+{$ENDIF}
+
 function TFileManager.RemovePathTrail(ADir: string): string;
 begin
   if (length(ADir)>=1) and (ADir[length(ADir)]=PathDelim) then
@@ -328,11 +441,24 @@ begin
     {$IFDEF WINDOWS}
     result := GetWindowsFileSystems;
     {$ELSE}
-    result := nil;
+      {$IFDEF DARWIN}
+      result := GetDarwinFileSystems;
+      {$ELSE}
+      result := nil;
+      {$ENDIF}
     {$ENDIF}
   {$ENDIF}
 end;
 
+function TFileManager.CanGetFileSystems: boolean;
+begin
+  {$IFDEF DARWIN}
+  result := true;
+  {$ELSE}
+  result := length(GetFileSystems)>0;
+  {$ENDIF}
+end;
+
 {$IFDEF WINDOWS}
 type
   {$PUSH}{$PACKRECORDS C}

+ 1 - 1
lazpaint/umenu.pas

@@ -372,7 +372,7 @@ begin
 end;
 
 procedure TMainFormMenu.Apply;
-const ImageBrowser = {$IFNDEF DARWIN}'FileUseImageBrowser,'{$ELSE}''{$ENDIF};
+const ImageBrowser = 'FileUseImageBrowser,';
 var i,j,tbHeight,tbHeightOrig: NativeInt;
 begin
   for i := 0 to FActionList.ActionCount-1 do