浏览代码

ADD: FsExtractCustomIcon function support

Alexander Koblov 3 月之前
父节点
当前提交
a824defefe

+ 18 - 4
sdk/wfxplugin.pas

@@ -207,6 +207,20 @@ type
   HICON = THandle;
   HWND = THandle;
 
+const
+  FS_ICON_FORMAT_HICON  = 0; // Load icon from HICON (Windows only)
+  FS_ICON_FORMAT_FILE   = 1; // Load icon from file name returned by plugin in the RemoteName
+  FS_ICON_FORMAT_BINARY = 2; // Load icon from Data byte array (PNG or ICO), destroy data using Free if FS_ICON_EXTRACTED_DESTROY returned
+
+type
+  PWfxIcon = ^TWfxIcon;
+  TWfxIcon = packed record
+    Data: Pointer;   // Icon data
+    Size: UIntPtr;   // Input: suggested icon size (width/height), output: size of Data byte array
+    Format: UIntPtr; // See FS_ICON_FORMAT_*
+    Free: procedure(Data: Pointer); {$IFDEF MSWINDOWS}stdcall{$ELSE}cdecl{$ENDIF}; // Procedure used to destroy Data byte array
+  end;
+
 type
 {$IFDEF MSWINDOWS}
   FILETIME = Windows.FILETIME;
@@ -407,21 +421,21 @@ procedure FsGetDefRootName(DefRootName:pchar;maxlen:integer); {$IFDEF MSWINDOWS}
 
 function FsExtractCustomIcon(RemoteName:pchar;ExtractFlags:integer;
 
-  var TheIcon:hicon):integer; {$IFDEF MSWINDOWS}stdcall{$ELSE}cdecl{$ENDIF};
+  TheIcon: PWfxIcon):integer; {$IFDEF MSWINDOWS}stdcall{$ELSE}cdecl{$ENDIF};
 
 function FsExtractCustomIconW(RemoteName:pwidechar;ExtractFlags:integer;
 
-  var TheIcon:hicon):integer; {$IFDEF MSWINDOWS}stdcall{$ELSE}cdecl{$ENDIF};
+  TheIcon: PWfxIcon):integer; {$IFDEF MSWINDOWS}stdcall{$ELSE}cdecl{$ENDIF};
 
 procedure FsSetDefaultParams(dps:pFsDefaultParamStruct); {$IFDEF MSWINDOWS}stdcall{$ELSE}cdecl{$ENDIF};
 
 function FsGetPreviewBitmap(RemoteName:pchar;width,height:integer,
 
-  var ReturnedBitmap:hbitmap):integer; {$IFDEF MSWINDOWS}stdcall{$ELSE}cdecl{$ENDIF};
+  ReturnedBitmap: PWfxIcon):integer; {$IFDEF MSWINDOWS}stdcall{$ELSE}cdecl{$ENDIF};
 
 function FsGetPreviewBitmapW(RemoteName:pwidechar;width,height:integer,
 
-  var ReturnedBitmap:hbitmap):integer; {$IFDEF MSWINDOWS}stdcall{$ELSE}cdecl{$ENDIF};
+  ReturnedBitmap: PWfxIcon):integer; {$IFDEF MSWINDOWS}stdcall{$ELSE}cdecl{$ENDIF};
 
 function FsLinksToLocalFiles:bool; {$IFDEF MSWINDOWS}stdcall{$ELSE}cdecl{$ENDIF};
 

+ 10 - 1
src/filesources/ufilesource.pas

@@ -5,7 +5,7 @@ unit uFileSource;
 interface
 
 uses
-  Classes, SysUtils, DCStrUtils, syncobjs, LCLProc, URIParser, Menus,
+  Classes, SysUtils, DCStrUtils, syncobjs, LCLProc, URIParser, Menus, Graphics,
   uFile, uDisplayFile, uFileProperty,
   uFileSourceWatcher,
   uFileSourceOperation, uFileSourceOperationTypes, uFileSourceProperty;
@@ -180,6 +180,7 @@ type
     function GetRootDir(sPath : String): String; overload;
     function GetRootDir: String; overload;
     function GetPathType(sPath : String): TPathType;
+    function GetCustomIcon(aFile: TFile; AIconSize: Integer; out AIcon: TBitmap): IntPtr;
     function GetFreeSpace(Path: String; out FreeSize, TotalSize : Int64) : Boolean;
     function GetRealPath(const path: String): String;
     function GetLocalName(var aFile: TFile): Boolean;
@@ -381,6 +382,7 @@ type
 
     function CreateDirectory(const Path: String): Boolean; virtual;
     function FileSystemEntryExists(const Path: String): Boolean; virtual;
+    function GetCustomIcon(aFile: TFile; AIconSize: Integer; out AIcon: TBitmap): PtrInt; virtual;
     function GetFreeSpace(Path: String; out FreeSize, TotalSize : Int64) : Boolean; virtual;
     function QueryContextMenu(AFiles: TFiles; var AMenu: TPopupMenu): Boolean; virtual;
     function GetDefaultView(out DefaultView: TFileSourceFields): Boolean; virtual;
@@ -702,6 +704,13 @@ begin
   Result := True;
 end;
 
+function TFileSource.GetCustomIcon(aFile: TFile; AIconSize: Integer; out
+  AIcon: TBitmap): PtrInt;
+begin
+  AIcon:= nil;
+  Result:= -1;
+end;
+
 function TFileSource.GetFreeSpace(Path: String; out FreeSize, TotalSize : Int64) : Boolean;
 begin
   Result := False; // not supported by default

+ 5 - 1
src/filesources/ufilesourceproperty.pas

@@ -73,7 +73,11 @@ type
     {en
        Set, if the file source supports custom context menu.
     }
-    fspContextMenu
+    fspContextMenu,
+    {en
+       Set, if the file source supports custom file icons.
+    }
+    fspCustomIcon
   );
 
   TFileSourceProperties = set of TFileSourceProperty;

+ 28 - 2
src/filesources/wfxplugin/uwfxpluginfilesource.pas

@@ -6,7 +6,7 @@ unit uWfxPluginFileSource;
 interface
 
 uses
-  Classes, SysUtils, URIParser, uWFXModule, WfxPlugin,
+  Classes, SysUtils, URIParser, Graphics, uWFXModule, WfxPlugin,
   uFile, uFileSourceProperty, uFileSourceOperationTypes,
   uFileProperty, uFileSource, uFileSourceOperation;
 
@@ -129,6 +129,7 @@ type
     function GetLocalName(var aFile: TFile): Boolean; override;
     function CreateDirectory(const Path: String): Boolean; override;
     function GetDefaultView(out DefaultView: TFileSourceFields): Boolean; override;
+    function GetCustomIcon(aFile: TFile; AIconSize: Integer; out AIcon: TBitmap): PtrInt; override;
 
     class function IsSupportedPath(const Path: String): Boolean; override;
     class function CreateByRootName(aRootName: String): IWfxPluginFileSource;
@@ -170,7 +171,7 @@ uses
   uWfxPluginCopyInOperation, uWfxPluginCopyOutOperation,  uWfxPluginMoveOperation, uVfsModule,
   uWfxPluginExecuteOperation, uWfxPluginListOperation, uWfxPluginCreateDirectoryOperation,
   uWfxPluginDeleteOperation, uWfxPluginSetFilePropertyOperation, uWfxPluginCopyOperation,
-  DCConvertEncoding, uWfxPluginCalcStatisticsOperation, uFileFunctions;
+  DCConvertEncoding, uWfxPluginCalcStatisticsOperation, uFileFunctions, uPixMapManager;
 
 const
   connCopyIn      = 0;
@@ -642,6 +643,8 @@ begin
       if (BackgroundFlags and BG_DOWNLOAD = 0) then
         Result:= Result + [fspCopyOutOnMainThread];
     end;
+    if Assigned(FsExtractCustomIcon) or Assigned(FsExtractCustomIconW) then
+      Result := Result + [fspCustomIcon];
     if Assigned(FsContentGetDefaultView) or Assigned(FsContentGetDefaultViewW) then
       Result := Result + [fspDefaultView];
   end;
@@ -949,6 +952,29 @@ begin
   Result:= FWFXModule.WfxContentGetDefaultView(DefaultView);
 end;
 
+function TWfxPluginFileSource.GetCustomIcon(aFile: TFile; AIconSize: Integer;
+  out AIcon: TBitmap): PtrInt;
+var
+  Status: Integer;
+  TheIcon: TWfxIcon;
+  AIconName: String;
+  UniqueName: String;
+begin
+  AIconName:= aFile.FullPath;
+  Status:= FWfxModule.WfxExtractCustomIcon(AIconName, AIconSize, TheIcon);
+
+  if Status in [FS_ICON_EXTRACTED, FS_ICON_EXTRACTED_DESTROY] then
+  begin
+    if AIconName <> aFile.FullPath then
+      UniqueName:= AIconName
+    else begin
+      UniqueName:= EmptyStr;
+    end;
+    Result:= PixmapManager.CheckAddPixmap(UniqueName, AIconSize, (Status = FS_ICON_EXTRACTED_DESTROY), @TheIcon, AIcon);
+  end;
+
+end;
+
 class function TWfxPluginFileSource.IsSupportedPath(const Path: String): Boolean;
 begin
   Result:= Pos('wfx://', Path) = 1;

+ 2 - 8
src/fileviews/ubrieffileview.pas

@@ -485,16 +485,10 @@ var
   procedure DrawIconCell;
     //------------------------------------------------------
     var
-      IconID: PtrInt;
       targetWidth: Integer;
     begin
       if (gShowIcons <> sim_none) then
       begin
-        IconID := AFile.IconID;
-        // Draw default icon if there is no icon for the file.
-        if IconID = -1 then
-          IconID := PixMapManager.GetDefaultIcon(AFile.FSFile);
-
         // center icon vertically
         params.iconRect.Left:= aRect.Left + CELL_PADDING;
         params.iconRect.Top:= aRect.Top + (aRect.Height - gIconsSize) div 2;
@@ -502,14 +496,14 @@ var
         params.iconRect.Height:= gIconsSize;
 
         if gShowHiddenDimmed and FBriefView.FileSource.IsHiddenFile(AFile.FSFile) then
-          PixMapManager.DrawBitmapAlpha(IconID,
+          PixMapManager.DrawBitmapAlpha(AFile,
                                         Canvas,
                                         params.iconRect.Left,
                                         params.iconRect.Top
                                        )
         else
           // Draw icon for a file
-          PixMapManager.DrawBitmap(IconID,
+          PixMapManager.DrawBitmap(AFile,
                                    Canvas,
                                    params.iconRect.Left,
                                    params.iconRect.Top

+ 2 - 8
src/fileviews/ucolumnsfileview.pas

@@ -1567,16 +1567,10 @@ var
   procedure DrawIconCell;
   //------------------------------------------------------
   var
-    IconID: PtrInt;
     targetWidth: Integer;
   begin
     if (gShowIcons <> sim_none) then
     begin
-      IconID := AFile.IconID;
-      // Draw default icon if there is no icon for the file.
-      if IconID = -1 then
-        IconID := PixMapManager.GetDefaultIcon(AFile.FSFile);
-
       // center icon vertically
       params.iconRect.Left:= aRect.Left + CELL_PADDING;
       params.iconRect.Top:= aRect.Top + (aRect.Height - gIconsSize) div 2;
@@ -1584,14 +1578,14 @@ var
       params.iconRect.Height:= gIconsSize;
 
       if gShowHiddenDimmed and ColumnsView.FileSource.isHiddenFile(AFile.FSFile) then
-        PixMapManager.DrawBitmapAlpha(IconID,
+        PixMapManager.DrawBitmapAlpha(AFile,
                                       Canvas,
                                       params.iconRect.Left,
                                       params.iconRect.Top
                                      )
       else
         // Draw icon for a file
-        PixMapManager.DrawBitmap(IconID,
+        PixMapManager.DrawBitmap(AFile,
                                  Canvas,
                                  params.iconRect.Left,
                                  params.iconRect.Top

+ 7 - 0
src/fileviews/ufileview.pas

@@ -1156,6 +1156,7 @@ begin
       FHashedNames.Remove(OldFileKey);
       FHashedNames.Add(NewFileKey, ADisplayFile);
       ADisplayFile.Busy:= [];
+      ADisplayFile.Icon:= nil;
       ADisplayFile.IconID := -1;
       ADisplayFile.Selected := False;
       ADisplayFile.IconOverlayID := -1;
@@ -2032,6 +2033,12 @@ begin
       aFile.Properties[propType] := UpdatedFile.FSFile.ReleaseProperty(propType);
     end;
 
+  if UpdatedFile.Icon <> nil then
+  begin
+    OrigDisplayFile.Icon := TBitmap.Create;
+    OrigDisplayFile.Icon.Assign(UpdatedFile.Icon);
+  end;
+
   if UpdatedFile.IconID <> -1 then
     OrigDisplayFile.IconID := UpdatedFile.IconID;
 

+ 7 - 4
src/fileviews/ufileviewworker.pas

@@ -777,7 +777,8 @@ begin
 
       if HaveIcons then
       begin
-        AFile.IconID := PixMapManager.GetIconByFile(AFile.FSFile,
+        AFile.IconID := PixMapManager.GetIconByFile(fs,
+                                                    AFile,
                                                     DirectAccess,
                                                     not gLoadIconsSeparately,
                                                     gShowIcons,
@@ -831,7 +832,8 @@ begin
 
           if HaveIcons then
           begin
-            AFile.IconID := PixMapManager.GetIconByFile(AFile.FSFile,
+            AFile.IconID := PixMapManager.GetIconByFile(fs,
+                                                        AFile,
                                                         DirectAccess,
                                                         not gLoadIconsSeparately,
                                                         gShowIcons,
@@ -947,9 +949,10 @@ begin
 
       if HaveIcons then
       begin
-        if FWorkingFile.IconID < 0 then
+        if (FWorkingFile.IconID < 0) and (FWorkingFile.Icon = nil) then
           FWorkingFile.IconID := PixMapManager.GetIconByFile(
-              FWorkingFile.FSFile,
+              FFileSource,
+              FWorkingFile,
               DirectAccess,
               True,
               gShowIcons,

+ 15 - 13
src/fileviews/uorderedfileview.pas

@@ -399,6 +399,7 @@ var
   AFile: TDisplayFile;
   HaveIcons: Boolean;
   DirectAccess: Boolean;
+  AFileSource: IFileSource;
   AFilePropertiesNeeded: TFilePropertiesTypes;
 begin
   if (csDestroying in ComponentState) or
@@ -406,10 +407,11 @@ begin
      IsEmpty then
     Exit;
 
+  AFileSource := FileSource;
   HaveIcons := gShowIcons <> sim_none;
   VisibleFiles := GetVisibleFilesIndexes;
   AFilePropertiesNeeded := FilePropertiesNeeded;
-  DirectAccess := fspDirectAccess in FileSource.Properties;
+  DirectAccess := fspDirectAccess in AFileSource.Properties;
 
   // Property fpComment should be retrieved in main thread
   if gListFilesInThread and (fpComment in AFilePropertiesNeeded) then
@@ -417,9 +419,9 @@ begin
     for i := VisibleFiles.First to VisibleFiles.Last do
     begin
       AFile := FFiles[i];
-      if FileSource.CanRetrieveProperties(AFile.FSFile, [fpComment]) then
+      if AFileSource.CanRetrieveProperties(AFile.FSFile, [fpComment]) then
       try
-        FileSource.RetrieveProperties(AFile.FSFile, [fpComment], []);
+        AFileSource.RetrieveProperties(AFile.FSFile, [fpComment], []);
       except
         on EFileNotFound do;
       end;
@@ -442,20 +444,20 @@ begin
       begin
         if HaveIcons then
         begin
-          if AFile.IconID < 0 then
-            AFile.IconID := PixMapManager.GetIconByFile(AFile.FSFile,
-              DirectAccess, True, gShowIcons, not gIconOverlays);
+          if (AFile.IconID < 0) and (AFile.Icon = nil) then
+          begin
+            AFile.IconID := PixMapManager.GetIconByFile(AFileSource, AFile, DirectAccess, True, gShowIcons, not gIconOverlays);
+          end;
           {$IF DEFINED(MSWINDOWS) OR DEFINED(RabbitVCS)}
           if gIconOverlays and (AFile.IconOverlayID < 0) then
           begin
-            AFile.IconOverlayID := PixMapManager.GetIconOverlayByFile(AFile.FSFile,
-                                                                      DirectAccess);
+            AFile.IconOverlayID := PixMapManager.GetIconOverlayByFile(AFile.FSFile, DirectAccess);
           end;
           {$ENDIF}
         end;
-        if FileSource.CanRetrieveProperties(AFile.FSFile, FilePropertiesNeeded) then
+        if AFileSource.CanRetrieveProperties(AFile.FSFile, FilePropertiesNeeded) then
         try
-          FileSource.RetrieveProperties(AFile.FSFile, FilePropertiesNeeded, GetVariantFileProperties);
+          AFileSource.RetrieveProperties(AFile.FSFile, FilePropertiesNeeded, GetVariantFileProperties);
         except
           on EFileNotFound do;
         end;
@@ -469,9 +471,9 @@ begin
       begin
         AFile := FFiles[i];
         if (AFile.FSFile.Name <> '..') and (AFile.Busy * [bsProp] = []) and
-           (FileSource.CanRetrieveProperties(AFile.FSFile, AFilePropertiesNeeded) or
+           (AFileSource.CanRetrieveProperties(AFile.FSFile, AFilePropertiesNeeded) or
            (AFile.TextColor = clNone) or
-           (HaveIcons and ((AFile.IconID < 0)
+           (HaveIcons and ( ((AFile.IconID < 0) and (AFile.Icon = nil))
              {$IF DEFINED(MSWINDOWS) OR DEFINED(RabbitVCS)}
              or (gIconOverlays and (AFile.IconOverlayID < 0))
              {$ENDIF}
@@ -487,7 +489,7 @@ begin
       if Assigned(AFileList) and (AFileList.Count > 0) then
       begin
         Worker := TFilePropertiesRetriever.Create(
-          FileSource,
+          AFileSource,
           WorkersThread,
           AFilePropertiesNeeded,
           GetVariantFileProperties,

+ 1 - 6
src/fileviews/uthumbfileview.pas

@@ -489,17 +489,12 @@ var
       end
     else
       begin
-        IconID := AFile.IconID;
-        // Draw default icon if there is no icon for the file.
-        if IconID = -1 then
-          IconID := PixMapManager.GetDefaultIcon(AFile.FSFile);
-
         // Center icon
         X:= aRect.Left + (aRect.Right - aRect.Left - gIconsSize) div 2;
         Y:= aRect.Top + (iTextTop - aRect.Top - gIconsSize) div 2;
 
         // Draw icon for a file
-        PixMapManager.DrawBitmap(IconID, Canvas, X, Y);
+        PixMapManager.DrawBitmap(AFile, Canvas, X, Y);
       end;
 
     // Draw overlay icon for a file if needed

+ 158 - 7
src/platform/upixmapmanager.pas

@@ -47,7 +47,7 @@ interface
 uses
   Classes, SysUtils, Graphics, syncobjs, uFileSorting, DCStringHashListUtf8,
   uFile, uIconTheme, uDrive, uDisplayFile, uGlobs, uDCReadPSD, uOSUtils, FPImage,
-  LCLVersion, uVectorImage, uMultiArc
+  LCLVersion, uVectorImage, uMultiArc, uFileSource, WfxPlugin
   {$IF DEFINED(MSWINDOWS)}
   , ShlObj
   {$ELSEIF DEFINED(MSWINDOWS) and DEFINED(LCLQT5)}
@@ -289,7 +289,8 @@ type
     }
     function GetBitmap(iIndex : PtrInt) : TBitmap;
     function DrawBitmap(iIndex: PtrInt; Canvas : TCanvas; X, Y: Integer) : Boolean;
-    function DrawBitmapAlpha(iIndex: PtrInt; Canvas : TCanvas; X, Y: Integer) : Boolean;
+    function DrawBitmap(AFile: TDisplayFile; Canvas : TCanvas; X, Y: Integer) : Boolean;
+    function DrawBitmapAlpha(AFile: TDisplayFile; Canvas : TCanvas; X, Y: Integer) : Boolean;
     {en
        Draws bitmap stretching it if needed to Width x Height.
        If Width is 0 then full bitmap width is used.
@@ -298,6 +299,7 @@ type
               Index of pixmap manager's bitmap.)
     }
     function DrawBitmap(iIndex: PtrInt; Canvas : TCanvas; X, Y, Width, Height: Integer) : Boolean;
+    function DrawBitmap(AFile: TDisplayFile; Canvas : TCanvas; X, Y, Width, Height: Integer) : Boolean;
     {en
        Draws overlay bitmap for a file.
        @param(AFile
@@ -306,6 +308,7 @@ type
               Whether the file is on a directly accessible file source.)
     }
     function DrawBitmapOverlay(AFile: TDisplayFile; DirectAccess: Boolean; Canvas : TCanvas; X, Y: Integer) : Boolean;
+    function CheckAddPixmap(AUniqueName: String; AIconSize: Integer; ADestroy: Boolean; TheIcon: PWfxIcon; out AIcon: TBitmap): PtrInt;
     function GetIconBySortingDirection(SortingDirection: TSortDirection): PtrInt;
     {en
        Retrieves icon index in FPixmapList table for a file.
@@ -330,6 +333,8 @@ type
     }
     function GetIconByFile(AFile: TFile; DirectAccess: Boolean; LoadIcon: Boolean;
                            IconsMode: TShowIconsMode; GetIconWithLink: Boolean): PtrInt;
+    function GetIconByFile(constref AFileSource: IFileSource; AFile: TDisplayFile; DirectAccess: Boolean; LoadIcon: Boolean;
+                           IconsMode: TShowIconsMode; GetIconWithLink: Boolean): PtrInt; overload;
     {$IF DEFINED(MSWINDOWS) OR DEFINED(RabbitVCS)}
     {en
        Retrieves overlay icon index for a file.
@@ -380,8 +385,8 @@ function getBestNSImageWithSize( const srcImage:NSImage; const size:Integer ): N
 implementation
 
 uses
-  GraphType, LCLIntf, LCLType, LCLProc, Forms, uGlobsPaths, WcxPlugin,
-  DCStrUtils, uDCUtils, uFileSystemFileSource, uReSample, uDebug,
+  GraphType, LCLIntf, LCLType, LCLProc, Forms, uGlobsPaths, WcxPlugin, uClassesEx,
+  DCStrUtils, uDCUtils, uFileSystemFileSource, uReSample, uDebug, uFileSourceProperty,
   IntfGraphics, DCOSUtils, DCClassesUtf8, LazUTF8, uGraphics, uHash, uSysFolders
   {$IFDEF GTK2_FIX}
     , uPixMapGtk, gdk2pixbuf, gdk2, glib2
@@ -1984,12 +1989,30 @@ begin
   Result := DrawBitmap(iIndex, Canvas, X, Y, gIconsSize, gIconsSize); // No bitmap stretching.
 end;
 
-function TPixMapManager.DrawBitmapAlpha(iIndex: PtrInt; Canvas: TCanvas; X, Y: Integer): Boolean;
+function TPixMapManager.DrawBitmap(AFile: TDisplayFile; Canvas: TCanvas; X, Y: Integer): Boolean;
+begin
+  Result := DrawBitmap(AFile, Canvas, X, Y, gIconsSize, gIconsSize); // No bitmap stretching.
+end;
+
+function TPixMapManager.DrawBitmapAlpha(AFile: TDisplayFile; Canvas: TCanvas; X, Y: Integer): Boolean;
 var
   ARect: TRect;
+  IconID: PtrInt;
   ABitmap: Graphics.TBitmap;
 begin
-  ABitmap:= GetBitmap(iIndex);
+  if Assigned(AFile.Icon) then
+  begin
+    ABitmap:= TBitmap.Create;
+    ABitmap.Assign(AFile.Icon);
+  end
+  else begin
+    if AFile.IconID < 0 then
+      IconID:= GetDefaultIcon(AFile.FSFile)
+    else begin
+      IconID:= AFile.IconID;
+    end;
+    ABitmap:= GetBitmap(IconID);
+  end;
   Result := Assigned(ABitmap);
   if Result then
   begin
@@ -2098,6 +2121,29 @@ begin
   {$ENDIF}
 end;
 
+function TPixMapManager.DrawBitmap(AFile: TDisplayFile; Canvas: TCanvas; X, Y, Width, Height: Integer): Boolean;
+var
+  aRect: TRect;
+  IconID: PtrInt;
+begin
+  if (AFile.Icon = nil) then
+  begin
+    // Draw default icon if there is no icon for the file
+    if AFile.IconID < 0 then
+      IconID:= GetDefaultIcon(AFile.FSFile)
+    else begin
+      IconID:= AFile.IconID;
+    end;
+    Result:= DrawBitmap(IconID, Canvas, X, Y, Width, Height);
+  end
+  else begin
+    if Width = 0 then Width:= AFile.Icon.Width;
+    if Height = 0 then Height:= AFile.Icon.Height;
+    aRect:= Classes.Bounds(X, Y, Width, Height);
+    Canvas.StretchDraw(aRect, AFile.Icon);
+  end;
+end;
+
 function TPixMapManager.DrawBitmapOverlay(AFile: TDisplayFile; DirectAccess: Boolean; Canvas: TCanvas; X, Y: Integer): Boolean;
 var
   I: Integer;
@@ -2133,6 +2179,84 @@ begin
     ;
 end;
 
+function TPixMapManager.CheckAddPixmap(AUniqueName: String; AIconSize: Integer;
+  ADestroy: Boolean; TheIcon: PWfxIcon; out AIcon: TBitmap): PtrInt;
+var
+  Index: PtrInt;
+  Picture: TPicture;
+  Stream: TBlobStream;
+begin
+  AIcon:= nil;
+  // Icon has a unique name
+  if Length(AUniqueName) > 0 then
+  begin
+    FPixmapsLock.Acquire;
+    try
+      // Try to find in the cache
+      Index:= FPixmapsFileNames.Find(AUniqueName);
+      if (Index >= 0) then
+      begin
+        if ADestroy then
+        begin
+          case TheIcon^.Format of
+{$IF DEFINED(MSWINDOWS)}
+            FS_ICON_FORMAT_HICON: DestroyIcon(HICON(TheIcon^.Data));
+{$ENDIF}
+            FS_ICON_FORMAT_BINARY: TheIcon^.Free(TheIcon^.Data);
+          end;
+        end;
+        Exit(PtrInt(FPixmapsFileNames.List[Index]^.Data));
+      end;
+    finally
+      FPixmapsLock.Release;
+    end;
+  end;
+  Result:= -1;
+
+  case TheIcon^.Format of
+{$IF DEFINED(MSWINDOWS)}
+    FS_ICON_FORMAT_HICON:
+    begin
+      AIcon:= BitmapCreateFromHICON(HICON(TheIcon^.Data));
+      if ADestroy then DestroyIcon(HICON(TheIcon^.Data));
+    end;
+{$ENDIF}
+    FS_ICON_FORMAT_FILE:
+    begin
+      Result:= CheckAddPixmap(AUniqueName, AIconSize);
+    end;
+    FS_ICON_FORMAT_BINARY:
+    begin
+      Picture:= TPicture.Create;
+      try
+        Stream:= TBlobStream.Create(TheIcon^.Data, TheIcon^.Size);
+        try
+          Picture.LoadFromStream(Stream);
+          AIcon:= Graphics.TBitmap.Create;
+          BitmapAssign(AIcon, TRasterImage(Picture.Graphic));
+        finally
+          Stream.Free;
+        end;
+      except
+        // Ignore;
+      end;
+      Picture.Free;
+      if ADestroy then TheIcon^.Free(TheIcon^.Data);
+    end;
+  end;
+  // Icon has a unique name, save to the cache
+  if Assigned(AIcon) and (Length(AUniqueName) > 0) then
+  begin
+    FPixmapsLock.Acquire;
+    try
+      Result := FPixmapList.Add(AIcon);
+      FPixmapsFileNames.Add(AUniqueName, Pointer(Result));
+    finally
+      FPixmapsLock.Release;
+    end;
+  end;
+end;
+
 function TPixMapManager.GetIconBySortingDirection(SortingDirection: TSortDirection): PtrInt;
 begin
   case SortingDirection of
@@ -2150,7 +2274,7 @@ begin
 end;
 
 function TPixMapManager.GetIconByFile(AFile: TFile; DirectAccess: Boolean; LoadIcon: Boolean;
-  IconsMode: TShowIconsMode; GetIconWithLink: Boolean): PtrInt;
+                                      IconsMode: TShowIconsMode; GetIconWithLink: Boolean): PtrInt;
 var
   Ext: String;
 {$IFDEF MSWINDOWS}
@@ -2436,6 +2560,33 @@ begin
   end;
 end;
 
+function TPixMapManager.GetIconByFile(constref AFileSource: IFileSource; AFile: TDisplayFile;
+  DirectAccess: Boolean; LoadIcon: Boolean; IconsMode: TShowIconsMode; GetIconWithLink: Boolean): PtrInt;
+var
+  ABitmap: TBitmap;
+begin
+  if Assigned(AFile.Icon) then Exit(-1);
+
+  if (fspCustomIcon in AFileSource.Properties) and AFileSource.IsPathAtRoot(AFile.FSFile.Path) then
+  begin
+    if AFile.FSFile.Name = '..' then
+    begin
+      Result := FiUpDirIconID;
+      Exit;
+    end;
+
+    Result:= AFileSource.GetCustomIcon(AFile.FSFile, gIconsSize, ABitmap);
+    if (Result >= 0) then Exit;
+    if Assigned(ABitmap) then
+    begin
+      AFile.Icon:= ABitmap;
+      Exit(-1);
+    end;
+  end;
+
+  Result:= GetIconByFile(AFile.FSFile, DirectAccess, LoadIcon, IconsMode, GetIconWithLink);
+end;
+
 {$IF DEFINED(MSWINDOWS)}
 procedure TPixMapManager.ClearSystemCache;
 var

+ 17 - 0
src/udisplayfile.pas

@@ -31,6 +31,7 @@ type
     FTag: PtrInt;            //<en File view related info
     FSelected: Boolean;      //<en If is selected
     FBusy: TDisplayFileBusy; //<en File properties is busy
+    FIcon: TBitmap;          //<en File icon (local cache)
     FIconID: PtrInt;         //<en Icon ID for PixmapManager
     FIconOverlayID: PtrInt;  //<en Overlay icon ID for PixmapManager
     FTextColor: TColor;      //<en Text color in file list
@@ -42,6 +43,7 @@ type
 
     // Cache of displayed strings.
     FDisplayStrings: TStringList;
+    procedure SetIcon(AValue: TBitmap);
 
   public
     {en
@@ -72,6 +74,7 @@ type
     property FSFile: TFile read FFSFile write FFSFile;
     property DisplayItem: TDisplayItemPtr read FDisplayItem write FDisplayItem;
     property Selected: Boolean read FSelected write FSelected;
+    property Icon: TBitmap read FIcon write SetIcon;
     property IconID: PtrInt read FIconID write FIconID;
     property IconOverlayID: PtrInt read FIconOverlayID write FIconOverlayID;
     property TextColor: TColor read FTextColor write FTextColor;
@@ -126,6 +129,12 @@ type
 
 implementation
 
+procedure TDisplayFile.SetIcon(AValue: TBitmap);
+begin
+  FIcon.Free;
+  FIcon:= AValue;
+end;
+
 constructor TDisplayFile.Create(ReferenceFile: TFile);
 begin
   FTag := -1;
@@ -141,6 +150,7 @@ begin
   inherited Destroy;
   FDisplayStrings.Free;
   FSFile.Free;
+  FIcon.Free;
 end;
 
 function TDisplayFile.Clone(NewReferenceFile: TFile): TDisplayFile;
@@ -170,6 +180,13 @@ begin
     AFile.FIconOverlayID := FIconOverlayID;
     AFile.FTextColor := FTextColor;
 
+    if (FIcon = nil) then
+      AFile.Icon:= nil
+    else begin
+      AFile.Icon:= TBitmap.Create;
+      AFile.FIcon.Assign(FIcon);
+    end;
+
     if Assigned(AFile.FFSFile) then
     begin
       AFile.FDisplayStrings.AddStrings(FDisplayStrings);

+ 43 - 2
src/uwfxmodule.pas

@@ -30,7 +30,7 @@ unit uWFXmodule;
 interface
 
 uses
-  SysUtils, Classes, WfxPlugin, uWFXprototypes,
+  SysUtils, Classes, Graphics, WfxPlugin, uWFXprototypes,
   dynlibs, DCClassesUtf8, Extension, DCBasicTypes, DCXmlConfig,
   uWdxPrototypes, uWdxModule, uFileSource;
 
@@ -63,6 +63,7 @@ type
   TWFXModule = class(TPluginWDX)
   private
     FBackgroundFlags: Integer;
+    FIconMutex: TRTLCriticalSection;
   public
   { Mandatory }
     FsInit : TFsInit;
@@ -137,6 +138,7 @@ type
     function WfxGetLocalName(var sFileName: String): Boolean;
     function WfxDisconnect(const DisconnectRoot: String): Boolean;
     function WfxContentGetDefaultView(out DefaultView: TFileSourceFields): Boolean;
+    function WfxExtractCustomIcon(var RemoteName: String; AIconSize: Integer; out TheIcon: TWfxIcon): Integer;
   private
     function LoadModule(const sName: String):Boolean; overload; {Load plugin}
     procedure UnloadModule; override;
@@ -191,7 +193,7 @@ uses
   LazUTF8, FileUtil,
 
   //DC
-  uDCUtils, uLng, uGlobsPaths, uOSUtils, uWfxPluginUtil, fDialogBox, DCOSUtils,
+  uDCUtils, uLng, uGlobsPaths, uOSUtils, uWfxPluginUtil, DCOSUtils,
   DCStrUtils, DCConvertEncoding, uComponentsSignature, uOSForms, uExtension;
 
 const
@@ -614,10 +616,48 @@ begin
   end;
 end;
 
+function TWFXModule.WfxExtractCustomIcon(var RemoteName: String; AIconSize: Integer; out TheIcon: TWfxIcon): Integer;
+var
+  Flags: Integer = FS_ICONFLAG_BACKGROUND;
+  asFileName: array[0..MAX_PATH] of AnsiChar;
+  wsFileName: array[0..MAX_PATH] of WideChar;
+begin
+  TheIcon:= Default(TWfxIcon);
+
+  if (AIconSize = 16) then begin
+    Flags:= Flags or FS_ICONFLAG_SMALL;
+  end;
+  TheIcon.Size:= AIconSize;
+
+  EnterCriticalSection(FIconMutex);
+  try
+    if Assigned(FsExtractCustomIconW) then
+    begin
+      StrPLCopy(wsFileName, CeUtf8ToUtf16(RemoteName), MAX_PATH);
+      Result:= FsExtractCustomIconW(wsFileName, Flags, @TheIcon);
+      if Result in [FS_ICON_EXTRACTED, FS_ICON_EXTRACTED_DESTROY] then
+        RemoteName:= CeUtf16ToUtf8(UnicodeString(wsFileName));
+    end
+    else if Assigned(FsExtractCustomIcon) then
+    begin
+      StrPLCopy(asFileName, CeUtf8ToSys(RemoteName), MAX_PATH);
+      Result:= FsExtractCustomIcon(asFileName, Flags, @TheIcon);
+      if Result in [FS_ICON_EXTRACTED, FS_ICON_EXTRACTED_DESTROY] then
+        RemoteName:= CeSysToUtf8(StrPas(asFileName));
+    end
+    else begin
+      Result:= FS_ICON_USEDEFAULT;
+    end;
+  finally
+    LeaveCriticalSection(FIconMutex);
+  end;
+end;
+
 constructor TWFXModule.Create;
 begin
   inherited;
   FName:= 'FS';
+  InitCriticalSection(FIconMutex);
 end;
 
 destructor TWFXModule.Destroy;
@@ -630,6 +670,7 @@ begin
       ExtensionFinalize(nil);
   end;
   inherited Destroy;
+  DoneCriticalSection(FIconMutex);
 end;
 
 function TWFXModule.LoadModule(const sName: String): Boolean;

+ 4 - 4
src/uwfxprototypes.pas

@@ -32,10 +32,10 @@ type
 //------------------------------------------------------
 {R}  TFsSetAttr=function (RemoteName:pansichar;NewAttr:integer):bool;
 {R}  TFsSetTime=Function(RemoteName:pansichar;CreationTime,LastAccessTime,LastWriteTime:PWfxFileTime):bool;
-{U}  TFsExtractCustomIcon=function(RemoteName:pansichar;ExtractFlags:integer;var TheIcon:hicon):integer;
+{U}  TFsExtractCustomIcon=function(RemoteName:pansichar;ExtractFlags:integer; TheIcon: PWfxIcon):integer;
 {R}  TFsRenMovFile= function(OldName,NewName:pansichar;  Move, OverWrite:bool; ri:pRemoteInfo):Integer;
 {U}  TFsDisconnect = function (DisconnectRoot:pansichar):bool;
-{U}  TFsGetPreviewBitmap = function ( RemoteName:pansichar; width,height:integer; ReturnedBitmap:HBITMAP):integer;
+{U}  TFsGetPreviewBitmap = function ( RemoteName:pansichar; width,height:integer; ReturnedBitmap: PWfxIcon):integer;
 {R}  TFsLinksToLocalFiles = function:bool;
 {R}  TFsGetLocalName = function (RemoteName:pansichar;maxlen:integer):bool;
 //------------------------------------------------------
@@ -68,8 +68,8 @@ type
      TFsSetAttrW = function(RemoteName:pwidechar;NewAttr:integer):bool;
      TFsSetTimeW = function(RemoteName:pwidechar;CreationTime,LastAccessTime, LastWriteTime:PWfxFileTime):bool;
      TFsStatusInfoW = procedure(RemoteDir:pwidechar;InfoStartEnd,InfoOperation:integer);
-     TFsExtractCustomIconW = function(RemoteName:pwidechar;ExtractFlags:integer; var TheIcon:hicon):integer;
-     TFsGetPreviewBitmapW = function(RemoteName:pwidechar;width,height:integer; var ReturnedBitmap:hbitmap):integer;
+     TFsExtractCustomIconW = function(RemoteName:pwidechar;ExtractFlags:integer; TheIcon: PWfxIcon):integer;
+     TFsGetPreviewBitmapW = function(RemoteName:pwidechar;width,height:integer; ReturnedBitmap: PWfxIcon):integer;
      TFsGetLocalNameW = function(RemoteName:pwidechar;maxlen:integer):bool;
 //------------------------------------------------------
      TFsContentGetValueW = function(FileName:pwidechar;FieldIndex,UnitIndex:integer;FieldValue:pbyte; maxlen,flags:integer):integer;