Thumbnails Gadget V6

Partagez votre expérience de PureBasic avec les autres utilisateurs.
Avatar de l’utilisateur
Thyphoon
Messages : 2697
Inscription : mer. 25/août/2004 6:31
Localisation : Eragny
Contact :

Thumbnails Gadget V6

Message par Thyphoon »

Salut à tous,

ça fait longtemps. Je profite des vacances et ce temps humide pour me faire plaisir avec Purebasic. La version 6Beta3 est très prometeuse.
Du coup j'ai repris un vieux projet. Dont je vous partage une petite partie. J'avais déjà partagé avec vous en 2019 une version (Voir ici)
C'est vrai que je n'ai pas partagé avec vous chaque version. Il faudrait que je le fasse plus souvent.
Voici donc venir la 6ème version. Reste encore du boulot, mais ça permet déjà de tester.
Au programme des nouveautés
  • Compatibilité avec les ecrans 4K (support du DPI)
  • Gestion du cache amélioré.
  • Support de plusieurs Gadgets
  • Quelques optimisations

J'ai encore du nettoyage de code et quelques fonctionnalités a finir. Mais si vous voulez tester n'hesitez pas.
Si vous avez des suggestions d'amélioration ou des bugs n'hesitez pas.

Code : Tout sélectionner

; ******************************************************************** 
; Program:           Thumbnails
; Description:       add a Thumbnails to select image 
; Version:           6Alpha1
; Author:            Thyphoon
; Date:              August, 2021
; License:           Free, unrestricted, credit 
;                    appreciated but not required.
; Note:              Please share improvement !
; ******************************************************************** 

CompilerIf Not Defined(Core,#PB_Module)
  DeclareModule Core
    Structure FileData
      FilePath.s
      Selected.b
      State.b                 ; 0 No Loaded ; 1 loaded; 2 Displayed
      Image.i
      Map MetaData.s()
    EndStructure
    
  EndDeclareModule
  
  Module Core
  EndModule
CompilerEndIf 

DeclareModule ImgTools
  Structure DefDisplayImage
    X.l
    Y.l
    Width.l
    Height.l
  EndStructure
  
  Enumeration
    #Image_Style_Fit
    #Image_Style_Fill
    #Image_Style_Stretch
  EndEnumeration 
  
  Declare ImageToContainer(*result.DefDisplayImage,Image,ContainerWidth.l,ContainerHeight.l,Style.l=#Image_Style_Fit)
EndDeclareModule

Module ImgTools
  Procedure ImageToContainer(*result.DefDisplayImage,Image,ContainerWidth.l,ContainerHeight.l,Style.l=#Image_Style_Fit)
    
    If IsImage(Image)
      Protected ImgRatio.l
      Protected ContRatio.l
      Protected ContWidth.l,ContHeight.l
      ImgRatio.l = ImageWidth(Image) / ImageHeight(Image)
      ContRatio.l = ContainerWidth /ContainerHeight
      
      Select Style
        Case #Image_Style_Fit
          If ImgRatio<ContRatio
            *result\Width=ImageWidth(Image)*ContainerHeight/ImageHeight(Image)
            *result\Height=ContainerHeight
            *result\X=(ContainerWidth-*result\Width)/2
            *result\Y=0
          Else
            *result\Width=ContainerWidth
            *result\Height=ImageHeight(Image)*ContainerWidth/ImageWidth(Image)
            *result\X=0
            *result\Y=(ContainerHeight-*result\Height)/2
          EndIf
        Case #Image_Style_Fill
          If ImgRatio<ContRatio
            *result\Width=ImageWidth(Image)*ContainerHeight/ImageHeight(Image)
            *result\Height=ContainerHeight
            *result\X=(ContainerWidth-*result\Width)/2
            *result\Y=0
          Else
            *result\Width=ContainerWidth
            *result\Height=ImageHeight(Image)*ContainerWidth/ImageWidth(Image)
            *result\X=0
            *result\Y=(ContainerHeight-*result\Height)/2
          EndIf
          
        Case #Image_Style_Stretch
          *result\X=0
          *result\Y=0
          *result\Width=ContainerWidth
          *result\Height=ContainerHeight
      EndSelect
      
    EndIf            
  EndProcedure
EndModule

;-Cache Module

DeclareModule Cache
  EnableExplicit
  
  Prototype.i CallBackLoadMedia(*Ptr.Core::FileData)
  
  Structure Param
    CallBackLoadMedia.CallBackLoadMedia
    ;LoadList  
    LoadListSemaphore.i
    LoadListMutex.i
    List LoadList.i()
    ;CacheList
    CacheListMutex.i
    Map CacheList.Core::FileData()
    BackGroundThread.i
  EndStructure
  
  Global Param.Param
  Param\LoadListSemaphore=CreateSemaphore(1)
  Param\LoadListMutex=CreateMutex()
  Param\CacheListMutex=CreateMutex()
  
  Declare SetCallBackLoadMedia(CallBackLoadMedia.i)
  Declare AddFileToLoadList(FilePath.s)  
  Declare CacheClean()  
  Declare  AutoLoadStart() 
  Declare.i GetFileDataFromCache(FilePath.s)
  Declare Quit()
EndDeclareModule

Module Cache
  
  Procedure SetCallBackLoadMedia(CallBackLoadMedia.i)
    Param\CallBackLoadMedia=CallBackLoadMedia
  EndProcedure
  
  Procedure AddFileToLoadList(FilePath.s)
    Protected *Ptr
    LockMutex(param\CacheListMutex)
    *Ptr=AddMapElement(param\CacheList(),FilePath)
    param\CacheList()\FilePath=FilePath
    param\CacheList()\State=0
    UnlockMutex(param\CacheListMutex)
    
    LockMutex(Param\LoadListMutex)
    AddElement(param\LoadList())
    param\LoadList()=*Ptr
    UnlockMutex(Param\LoadListMutex)
  EndProcedure
  
  Procedure LoadCacheDataThread(*Ptr.core::FileData)
    If *Ptr\Image=0
      Debug "Cache Load:"+*Ptr\FilePath
      ;Param\CallBackLoadMedia=0 ;To Force to use Internal Loader
      If Param\CallBackLoadMedia<>0 ; <- Use extern procedure to Load Image
        Debug "CallBackLoadMedia"
        
        Param\CallBackLoadMedia(*Ptr)
        
      Else
        LockMutex(Param\CacheListMutex)           ; <- Or intern with PB Plugin
        *Ptr\Image=LoadImage(#PB_Any,*Ptr\FilePath)
        UnlockMutex(Param\CacheListMutex)
      EndIf  
      
      ;Resize Image to Thumnails MaxSize
      If *Ptr\Image<>0
        Protected result.ImgTools::DefDisplayImage
        ImgTools::ImageToContainer(@result,*Ptr\Image,256,256,ImgTools::#Image_Style_Fit)
        ResizeImage(*Ptr\Image,result\Width,result\Height,#PB_Image_Smooth) 
        *Ptr\State=1; Ready to Display Image
                    ;If can't load image  
      Else
        ;MessageRequester("Thumbnails Error","ERROR THREAD LOAD IMAGE"+Chr(13)+FilePath+Chr(13)+" in "+#PB_Compiler_Procedure+"()",#PB_MessageRequester_Ok | #PB_MessageRequester_Error)
        Debug "ERROR THREAD LOAD IMAGE"+Chr(13)+*Ptr\FilePath
        *Ptr\Image=CreateImage(#PB_Any,320,200)
        StartDrawing(ImageOutput(*Ptr\Image))
        Box(0,0,320,200,RGB(0,255,0))
        StopDrawing()
        *Ptr\State=1
      EndIf
    EndIf   
    SignalSemaphore(Param\LoadListSemaphore)
  EndProcedure
  
  Procedure BackgroundThread(n.l)
    ;Repeat 
    Debug "Run CacheBackgroundThread()****************"
    Repeat 
      ;If TrySemaphore(Param\LoadListSemaphore)
      LockMutex(Param\LoadListMutex)
      ;Select Data from Id
      If FirstElement(Param\LoadList())<>0
        
        If CreateThread(@LoadCacheDataThread(),Param\LoadList())<>0
          
          DeleteElement(Param\LoadList())
        Else
          Debug "Can't Start Thread"
          End
        EndIf   
        
      EndIf 
      UnlockMutex(Param\LoadListMutex)
      ;Else
      ;   Debug "Wait Semaphore"
      ; EndIf 
      Delay(200)
      Debug "LOADLIST SIZE:"+Str(ListSize(Param\LoadList()))
    Until ListSize(Param\LoadList())=0
    Delay(1000)
    Debug "Finish CacheBackgroundThread()**************"
    ;ForEver 
  EndProcedure
  
  Procedure Quit()
    If IsThread(Param\BackGroundThread)
      KillThread(Param\BackGroundThread)
    EndIf 
  EndProcedure
  
  Procedure AutoLoadStart()
    If IsThread(Param\BackGroundThread)=#False
      Param\BackGroundThread=CreateThread(@BackgroundThread(),0)
    EndIf 
  EndProcedure
  
  Procedure Free(*Ptr.core::FileData)
    LockMutex(Param\CacheListMutex)
    If IsImage(*Ptr\Image):FreeImage(*Ptr\Image):EndIf
    FreeMap(*Ptr\MetaData())
    UnlockMutex(Param\CacheListMutex)
  EndProcedure
  
  
  ;TODO remake it
  Procedure CacheClean()
    Protected *Ptr.core::FileData
    LockMutex(Param\CacheListMutex)
    ForEach Param\CacheList() 
      If MapSize(Param\CacheList())<500
        Break;
      Else
        *Ptr=Param\CacheList()
        If *Ptr\State=1 And *Ptr\Selected=#False
          Debug "Free Cache :"+GetFilePart(*Ptr\FilePath)+" State:"+Str(*Ptr\State)
          Free(*Ptr)
          DeleteMapElement(Param\CacheList())
        EndIf 
      EndIf 
    Next
    UnlockMutex(Param\CacheListMutex)
  EndProcedure
  
  Procedure.i GetFileDataFromCache(FilePath.s)
    LockMutex(Param\CacheListMutex)
    Protected *Ptr.core::FileData
    *Ptr=FindMapElement(Param\CacheList(),FilePath)
    UnlockMutex(Param\CacheListMutex)
    If *Ptr=0
      ;AddToLoadList
      LockMutex(Param\CacheListMutex)
      *Ptr=AddMapElement(Param\CacheList(),FilePath)
      *Ptr\FilePath=FilePath
      *Ptr\State=0
      *Ptr\Image=0
      UnlockMutex(Param\CacheListMutex)
      LockMutex(Param\LoadListMutex)
      AddElement(param\LoadList())
      Param\LoadList()=*Ptr
      UnlockMutex(Param\LoadListMutex)
      AutoLoadStart()
      ;Debug "Pas Trouve:"+GetFilePart(*Ptr\FilePath)
    EndIf 
    ;Debug "trouve:"+GetFilePart(*Ptr\FilePath)
    ProcedureReturn *Ptr
    
  EndProcedure
EndModule

;-Thumbs Module

DeclareModule Thumbs
  EnableExplicit  
  ;Declare SetBusyIcon(Image.i)
  Declare SetCallBackLoadFromIndex(GadgetId.i,CallBackLoadFromIndex.i)
  Declare SetCallBackSimpleClick(GadgetId.i,CallBackLoadFromIndex.i)
  Declare SetCallBackDoubleClick(GadgetId.i,CallBackLoadFromIndex.i)
  Declare ThumbnailsGadget(GadgetId.i,X.l,Y.l,Width.l,Height.l,Size.l)
  Declare Start(GadgetId.i)
  Declare Stop(GadgetId.i)
  Declare AddImageToThumb(GadgetId.i,*Ptr)
  Declare CheckEvent(Event.i)
  ;Declare Navigate
  Declare NavigateGadget(GadgetId.i,X.l,Y.l,Width.l,Height.l,ThumbnailsId.i)
  Declare DrawNavigate(Gadget.i)
  Declare CheckEventNav(Event.i)
  ;-Declare List
  Declare CallBackLoadFromInternalListIndex(GadgetId.i,Index.i,Lenght.l)
  Declare AddFileToList(GadgetId.i,FilePath.s,nImage.i=0)
  Declare ClearTheList(GadgetId.i)
  Declare.i GetTheListSize(GadgetId.i)
EndDeclareModule

Module Thumbs
  
  Prototype CallBackLoadFromIndex(GadgetId.i,Index.i,Lenght.l)
  Prototype.i CallBackDoubleClick(GadgetId.i,CallBackLoadFromIndex.i)
  Prototype.i CallBackSimpleClick(GadgetId.i,FileName.s,selected.b)
  Structure gdt
    
    BufferMutex.i
    BufferImage.i
    
    Gadget.i                ;Canvas Gadget number
    Size.l                  ;Thumb Size Width and Height 
    Index.i                 ;ThumbIndex
    NbH.l                   ;Number of horizontal thumbnails
    NbV.l                   ;Number of Vertical thumbnails
    
    CursorStartY.l          ; Cursor Y coord when clic
    CursorPosY.l
    CursorDeltaY.l          ; Cursor Delta Y from StartY
    ThumbsDeltaY.l          ; Scroll Thumbs
    
    CallBackLoadFromIndex.CallBackLoadFromIndex
    CallBackDoubleClick.CallBackDoubleClick
    CallBackSimpleClick.CallBackSimpleClick
    
    FileListMutex.i
    List FileList.core::FileData()
    
    MutexThumb.i
    List ThumbPointer.i()
    ;MutexRedraw.i
    
    
    DrawBufferImage.b
    GenerateBufferImage.b
    LoadFromIndex.b
    
  EndStructure 
  
  Structure GdtNav     
    ThumbnailsGadget.i  
    Gadget.i                ;Canvas Gadget number
  EndStructure
  
  Structure param
    BusyIcon.i
    SetTimer.b
    RefreshThread.i
    
    GdtMutex.i
    Map Gdt.Gdt()
    
    Map GdtNav.GdtNav()
  EndStructure
  
  Global param.param
  param\GdtMutex=CreateMutex()
  
  param\SetTimer=#False
  LoadFont(0, "Impact", 20, #PB_Font_Bold)
  
  Procedure SetCallBackLoadFromIndex(GadgetId.i,CallBackLoadFromIndex.i)
    LockMutex(param\GdtMutex)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      Param\Gdt()\CallBackLoadFromIndex=CallBackLoadFromIndex
    EndIf
    UnlockMutex(param\GdtMutex)
  EndProcedure
  
  Procedure SetCallBackDoubleClick(GadgetId.i,CallBackDoubleClick.i)
    LockMutex(param\GdtMutex)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      Param\Gdt()\CallBackDoubleClick=CallBackDoubleClick
    EndIf
    UnlockMutex(param\GdtMutex)
  EndProcedure
  
  Procedure SetCallBackSimpleClick(GadgetId.i,CallBackSimpleClick.i)
    LockMutex(param\GdtMutex)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      Param\Gdt()\CallBackSimpleClick=CallBackSimpleClick
    EndIf
    UnlockMutex(param\GdtMutex)
  EndProcedure
  
  Procedure ThumbnailsGadget(GadgetId.i,X.l,Y.l,Width.l,Height.l,Size.l)
    Protected Gdt.i
    Gdt=CanvasGadget(GadgetId.i,X.l,Y.l,Width.l,Height.l)
    If GadgetId=#PB_Any
      GadgetId=Gdt
    EndIf
    LockMutex(param\GdtMutex)
    AddMapElement(Param\Gdt(),Str(GadgetId))
    Param\Gdt()\BufferMutex=CreateMutex()
    Param\Gdt()\FileListMutex=CreateMutex()
    
    Param\Gdt()\Gadget=GadgetId
    Param\Gdt()\Size=128
    
    If param\SetTimer=#False 
      AddWindowTimer(GetActiveWindow(), 123, 75)
      param\SetTimer=#True
    EndIf
    
    param\Gdt()\BufferMutex=CreateMutex()
    param\Gdt()\MutexThumb=CreateMutex()
    UnlockMutex(param\GdtMutex)
  EndProcedure
  
  Procedure AddImageToThumb(GadgetId.i,*Ptr)
    LockMutex(param\GdtMutex)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      LockMutex(param\Gdt()\MutexThumb)
      AddElement(param\Gdt()\ThumbPointer())
      param\Gdt()\ThumbPointer()=*Ptr
      UnlockMutex(param\Gdt()\MutexThumb)
    EndIf
    UnlockMutex(param\GdtMutex)
  EndProcedure
  
  Procedure GenerateBufferImage(GadgetId.i)
    LockMutex(param\GdtMutex)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      
      Protected.l _GadgetWidth=DesktopScaledX(GadgetWidth(Param\Gdt()\Gadget))
      Protected.l _GadgetHeight=DesktopScaledY(GadgetHeight(Param\Gdt()\Gadget))
      Protected.l _Size=DesktopScaledX(Param\Gdt()\Size)
      Protected.l _ScrollWidth=DesktopScaledX(25)
      Protected GdtWidth.l=_GadgetWidth-_ScrollWidth
      Protected NbH.l=Int(GdtWidth/_Size):Param\Gdt()\NbH=NbH
      Protected NbV.l=Int(_GadgetHeight/_Size):Param\Gdt()\NbV=NbV
      Protected MarginH.l=(GdtWidth-NbH*_Size)/(NbH+1)
      Protected MarginV.l=(_GadgetHeight-NbV*_Size)/(NbV+1)
      
      ;First Draw create the Image
      LockMutex(param\Gdt()\BufferMutex)
      If IsImage(param\Gdt()\BufferImage)=#False
        param\Gdt()\BufferImage=CreateImage(#PB_Any,_GadgetWidth-_ScrollWidth,_GadgetHeight+2*_Size)
        param\Gdt()\LoadFromIndex=#True
      EndIf 
      UnlockMutex(param\Gdt()\BufferMutex)
      
      LockMutex(param\Gdt()\MutexThumb)
      Protected *Ptr.core::FileData
      If param\Gdt()\CallBackLoadFromIndex>0
        If param\Gdt()\LoadFromIndex=#True
          param\Gdt()\LoadFromIndex=#False
          ForEach Param\Gdt()\ThumbPointer()
            *Ptr=Param\Gdt()\ThumbPointer()
            If *Ptr>0
              If *Ptr\State=2:*Ptr\State=1:EndIf
            EndIf
            DeleteElement(Param\Gdt()\ThumbPointer())
          Next
          
          ;ClearList(Param\Gdt()\ThumbPointer()) ; ça plante donc au dessus j'efface au fur et a mesure avec DeleteElement(Param\Gdt()\ThumbPointer())
          param\Gdt()\CallBackLoadFromIndex(Param\Gdt()\Gadget,Param\Gdt()\Index-Nbh,(NbV+2)*Nbh)
          Debug "param\CallBackLoadFromIndex("+Str(Param\Gdt()\Index-Nbh)+","+Str((NbV+2)*Nbh)+")"
        EndIf 
      Else 
        Debug "No Set CallBackLoadFromIndex"
      EndIf 
      UnlockMutex(param\Gdt()\MutexThumb)
      
      Protected ContinueGenerateBufferImage=#False
      param\Gdt()\GenerateBufferImage=#True
      LockMutex(param\Gdt()\BufferMutex)
      If IsImage(Param\Gdt()\BufferImage) And StartVectorDrawing(ImageVectorOutput(Param\Gdt()\BufferImage))
        VectorSourceColor(RGBA(128, 128, 128, 255))
        FillVectorOutput()
        
        Protected.l nx,ny,x,y,i
        For ny=-1 To NbV+1
          For nx=0 To NbH-1
            ;Position
            x=nx*_Size+MarginH*nx+(MarginH)
            y=ny*_Size+MarginV*ny+(MarginV)
            i=nx+ny*NbH
            
            ;If in Limit
            If i>-1 And i<ListSize(Param\Gdt()\ThumbPointer())
              Protected State.b
              Protected Image.i
              Protected FileName.s
              Protected Selected.b
              LockMutex(param\Gdt()\MutexThumb)
              *Ptr=0
              If SelectElement(Param\Gdt()\ThumbPointer(),i)
                *Ptr=Param\Gdt()\ThumbPointer()
                Selected=0
                State=0
                Image=-1
                FileName="No File"
                If *Ptr>0
                  State=*Ptr\State
                  Image.i=*Ptr\Image
                  Selected=*Ptr\Selected
                  FileName.s=GetFilePart(*Ptr\FilePath)
                EndIf
              EndIf
              UnlockMutex(param\Gdt()\MutexThumb)
              
              If *Ptr>0
                If State>0 And IsImage(Image) ;If Image loaded 
                  Protected result.ImgTools::DefDisplayImage
                  ImgTools::ImageToContainer(@result,Image,_Size,_Size,ImgTools::#Image_Style_Fit)
                  ;If element selected display green
                   Protected _Border.l,_BorderX2.l
                  ;Draw Green Border when selected
                  If Selected=1
                    AddPathBox(result\X+x, result\Y+y,result\Width,result\Height)
                    VectorSourceColor(RGBA(0, 255, 0, 255))
                    FillPath()
                    _Border=DesktopScaledX(2)
                    _BorderX2=_Border*2
                  Else
                    _Border=DesktopScaledX(0)
                    _BorderX2=0
                  EndIf
                  ;Draw Image
                  MovePathCursor(result\X+x+_Border,result\Y+y+_Border)
                  DrawVectorImage(ImageID(Image),255,result\Width-_BorderX2,result\Height-_BorderX2)
                    
                  LockMutex(param\Gdt()\MutexThumb)
                  *Ptr\State=2
                  UnlockMutex(param\Gdt()\MutexThumb)
                Else
                  AddPathBox(x, y, _Size,_Size)
                  VectorSourceColor(RGBA(0, 0, 0, 255))
                  FillPath()
                  
                  VectorFont(FontID(0), 30)
                  VectorSourceColor(RGBA(0, 125, 0, 255))
                  MovePathCursor(result\X+x+_Border,result\Y+y+_Border+10)
                  DrawVectorText("Wait....")
                  ContinueGenerateBufferImage=#True
                EndIf
                  VectorFont(FontID(0), 40)
                  MovePathCursor(x+(_Size-VectorTextWidth(FileName))/2,y+result\Height-VectorTextHeight(FileName))
                  VectorSourceColor(RGBA(255, 255, 255, 255))
                  DrawVectorText(Str(*Ptr\Selected)+FileName)
              EndIf 
              
            Else ;overLimit
              AddPathBox(x, y, _Size,_Size)
              VectorSourceColor(RGBA(255, 0, 0, 255))
              FillPath()
            EndIf
            
          Next
        Next
        
        MovePathCursor(10,500)
            VectorFont(FontID(0), 40)
            VectorSourceColor(RGBA(255, 255, 255, 255))
            DrawVectorText("Test")
        
        StopVectorDrawing()
      EndIf
      
      If ContinueGenerateBufferImage=#False
        param\Gdt()\GenerateBufferImage=#False
        Debug"END REDRAW ALL IS DISPLAY"
      Else
        Cache::AutoLoadStart()
      EndIf
      
      param\Gdt()\DrawBufferImage=#True
      UnlockMutex(param\Gdt()\BufferMutex)
      ;Cache::CacheClean()
    EndIf 
    UnlockMutex(param\GdtMutex)
  EndProcedure
  
  Procedure Refresh(time.l)
    
    
    Protected DeltaIndex.l
    Repeat  
      
      
      LockMutex(param\GdtMutex)
      ForEach param\Gdt()
        Protected UpdateList.b=#False
        Protected *Gdt.Gdt
        *Gdt=param\Gdt()
        Protected _Size=DesktopScaledX(*Gdt\Size)
        ;Scroll    
        If  *Gdt\CursorStartY>0
          If GetGadgetAttribute(*Gdt\Gadget, #PB_Canvas_MouseY)<>0
            *Gdt\CursorPosY=GetGadgetAttribute(*Gdt\Gadget, #PB_Canvas_MouseY)
          EndIf   
          *Gdt\CursorDeltaY=*Gdt\CursorStartY-*Gdt\CursorPosY
          
          *Gdt\ThumbsDeltaY=*Gdt\ThumbsDeltaY+(*Gdt\CursorDeltaY/10)
        EndIf 
        
        ;Change Index when Scroll > Thumbnails
        If *Gdt\ThumbsDeltaY>_Size
          DeltaIndex=Int(*Gdt\ThumbsDeltaY/_Size)* *Gdt\NbH
          *Gdt\Index=*Gdt\Index-DeltaIndex
          *Gdt\ThumbsDeltaY=*Gdt\ThumbsDeltaY%_Size
          UpdateList=#True
        EndIf
        
        If *Gdt\ThumbsDeltaY<-_Size 
          DeltaIndex=Abs(Int(*Gdt\ThumbsDeltaY/_Size)* *Gdt\NbH)
          *Gdt\Index=*Gdt\Index+DeltaIndex
          *Gdt\ThumbsDeltaY=*Gdt\ThumbsDeltaY%_Size
          UpdateList=#True
        EndIf 
        
        ;Limit Up Scroll
        If *Gdt\Index<=0 And *Gdt\ThumbsDeltaY>0
          *Gdt\Index=0
          *Gdt\ThumbsDeltaY=0
          Debug "Limit Up Scroll"
        EndIf 
        
        ;Limit Down Scroll
        ;If *Gdt\Index>
        ;EndIf
        
        
        If UpdateList=#True 
          *Gdt\LoadFromIndex=#True
          *Gdt\GenerateBufferImage=#True
          *Gdt\DrawBufferImage=#True
        EndIf 
        
        If *Gdt\GenerateBufferImage=#True
          GenerateBufferImage(*Gdt\Gadget)
        EndIf
        
        Protected.l _GadgetHeight=DesktopScaledX(GadgetHeight(*Gdt\Gadget))
        Protected.l _ScrollWidth=DesktopScaledX(25)
        Protected.l _ScrollHeight=_ScrollWidth*2
        If *Gdt\DrawBufferImage=#True
          If  StartVectorDrawing(CanvasVectorOutput(*Gdt\Gadget))
            LockMutex(*Gdt\BufferMutex)
            If IsImage(*Gdt\BufferImage)
              MovePathCursor(0,*Gdt\ThumbsDeltaY-_Size)
              DrawVectorImage(ImageID(*Gdt\BufferImage))
              AddPathBox(ImageWidth(*Gdt\BufferImage),0,_ScrollWidth,_GadgetHeight):VectorSourceColor(RGBA(100, 100, 100, 255)):FillPath()
              AddPathBox(ImageWidth(*Gdt\BufferImage),_GadgetHeight/2-_ScrollHeight-*Gdt\CursorDeltaY,_ScrollWidth,_ScrollHeight):VectorSourceColor(RGBA(200, 200, 200, 255)):FillPath()
              
            Else
              Debug "can't draw BufferImage"
            EndIf  
            UnlockMutex(*Gdt\BufferMutex)
            StopVectorDrawing()
            *Gdt\DrawBufferImage=#False
          EndIf
        EndIf
        UnlockMutex(param\GdtMutex)
        ; If *Gdt\DrawBufferImage=#True
        ;   Delay(75)
        ; Else
        ;   Delay(200)
        ; EndIf
      Next
      Delay(75)
    ForEver
    
  EndProcedure
  
  
  
  
  Procedure MyEvent()
    Protected *Gdt.Gdt
    LockMutex(Param\GdtMutex)
    *Gdt=FindMapElement(Param\Gdt(), Str(EventGadget()))
    If *Gdt<>0
      UnlockMutex(Param\GdtMutex)
      Protected.l _GadgetWidth=DesktopScaledX(GadgetWidth(*Gdt\Gadget))
      Protected.l _GadgetHeight=DesktopScaledY(GadgetHeight(*Gdt\Gadget))
      Protected.l _Size=DesktopScaledX(*Gdt\Size)
      Protected.l _ScrollWidth=DesktopScaledX(25)
      ;Scroll Event
      If GetGadgetAttribute(*Gdt\Gadget, #PB_Canvas_MouseX)>_GadgetWidth-_ScrollWidth
        ;Start Scroll
        If EventType()=#PB_EventType_LeftButtonDown
          If *Gdt\CursorStartY=0
            *Gdt\CursorStartY=GetGadgetAttribute(*Gdt\Gadget, #PB_Canvas_MouseY) 
            *Gdt\CursorPosY=*Gdt\CursorStartY
            Debug "Start Scroll"
          EndIf 
        EndIf 
      EndIf   
      
      ;Scroll is Enable
      If *Gdt\CursorStartY>0
        *Gdt\DrawBufferImage=#True
        If EventType()=#PB_EventType_MouseMove
          *Gdt\CursorPosY=GetGadgetAttribute(*Gdt\Gadget, #PB_Canvas_MouseY)
        EndIf 
        
        ;Stop Scroll
        If EventType() = #PB_EventType_LeftButtonUp
          *Gdt\CursorStartY=0
          *Gdt\CursorDeltaY=0
          Debug "Stop Scroll"
        EndIf
        
        ;Thumbnails Event  
      ElseIf GetGadgetAttribute(*Gdt\Gadget, #PB_Canvas_MouseX)<_GadgetWidth-_ScrollWidth   
        ;Change Thumbs Size with MouseWheel
        If EventType() = #PB_EventType_MouseWheel
          *Gdt\Size=*Gdt\Size+GetGadgetAttribute(*Gdt\Gadget,#PB_Canvas_WheelDelta)*8
          *Gdt\LoadFromIndex=#True
          *Gdt\GenerateBufferImage=#True
          ;param\DrawBufferImage=#True
        EndIf   
        
        ;Select Media
        If EventType()=#PB_EventType_LeftClick Or EventType()=#PB_EventType_LeftDoubleClick
          
          Protected GdtWidth.l=_GadgetWidth-_ScrollWidth
          Protected NbH.l=Int(GdtWidth/_Size):*Gdt\NbH=NbH
          Protected NbV.l=Int(_GadgetHeight/_Size):*Gdt\NbV=NbV
          Protected MarginH.l=(GdtWidth-NbH* _Size)/(NbH+1)
          Protected MarginV.l=(_GadgetHeight-NbV* _Size)/(NbV+1)
          
          Protected x.l=GetGadgetAttribute(*Gdt\Gadget, #PB_Canvas_MouseX) 
          Protected y.l=GetGadgetAttribute(*Gdt\Gadget, #PB_Canvas_MouseY) 
          
          Protected nx.l=(x-MarginH)/(_Size+MarginH)
          Protected ny.l=(y-MarginV-*Gdt\ThumbsDeltaY)/(_Size+MarginV)
          Debug Str(x)+","+Str(y)
          Protected index.l=nx+ny*NbH+NbH
          Debug "INDEX Selected:"+Str(index)
          Protected *Ptr.core::FileData
          LockMutex(*Gdt\MutexThumb)
          
          
          ;Get 
          If index>-1
            If SelectElement(*Gdt\ThumbPointer(),index)
            *Ptr=*Gdt\ThumbPointer()
            If *Ptr<>0
              Select EventType()
                Case #PB_EventType_LeftClick
                  Debug GetFilePart(*Ptr\FilePath)+" Selected:"+Str(*Ptr\Selected)
                  *Ptr\Selected=1-*Ptr\Selected
                  Debug "Left CLick"
                  ;                   Debug Param\CallBackSimpleClick
                  ;                   If Param\CallBackSimpleClick<>0 ; check if a @Procedure is added
                  ;                     Debug "@procedure"
                  ;                     Param\CallBackSimpleClick(Param\FileList()\FileName,Param\FileList()\Selected)
                  ;                   EndIf 
                Case #PB_EventType_LeftDoubleClick
                  ;                   Debug "Left Double Click"
                  ;                   If Param\CallBackDoubleClick<>0 ; check if a @Procedure is added
                  ;                     Debug "@procedure"
                  ;                     Param\CallBackDoubleClick(Param\FileList()\FileName)
                  ;                   EndIf
                  ;                   Param\IndexNav=index
              EndSelect
            EndIf
            EndIf
           
          EndIf 
          UnlockMutex(*Gdt\MutexThumb)
          *Gdt\DrawBufferImage=#True
          *Gdt\GenerateBufferImage=#True
        EndIf
      EndIf  
      
      
    EndIf
    UnlockMutex(Param\GdtMutex)
  EndProcedure
  
  Procedure Start(GadgetId.i)
    LockMutex(param\GdtMutex)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      If IsThread(Param\RefreshThread)=#False
        Param\RefreshThread=CreateThread(@Refresh(),10)
      EndIf 
      param\Gdt()\LoadFromIndex=#True
      param\Gdt()\GenerateBufferImage=#True
      param\Gdt()\DrawBufferImage=#True
      BindGadgetEvent(GadgetId,@MyEvent(),#PB_All)
    EndIf
    UnlockMutex(param\GdtMutex)
  EndProcedure
  
  Procedure Stop(GadgetId.i)
    LockMutex(param\GdtMutex)
    UnbindGadgetEvent(GadgetId,@MyEvent(),#PB_All)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      If IsThread(Param\RefreshThread)=#True
        KillThread(Param\RefreshThread)
      EndIf 
      param\Gdt()\LoadFromIndex=#False
      param\Gdt()\GenerateBufferImage=#False
      param\Gdt()\DrawBufferImage=#False
      Cache::Quit()
    EndIf
    UnlockMutex(param\GdtMutex)
  EndProcedure
  
  Procedure CheckEvent(Event.i)
    Protected *Gdt.Gdt
    LockMutex(Param\GdtMutex)
    Debug "CheckEvent:"+Str(EventGadget())
    *Gdt=FindMapElement(Param\Gdt(), Str(EventGadget()))
    If *Gdt<>0
      Debug Param\Gdt()
      ;*Gdt=Param\Gdt()
      UnlockMutex(Param\GdtMutex)
      
      
      If Event=#PB_Event_Timer
        ;DrawThumbnails()
      EndIf 
      
      Select Event
        Case #PB_Event_Gadget
          If EventType()=#PB_EventType_Focus
            Debug "Focus"
          EndIf 
          
          If EventType()=#PB_EventType_LostFocus
            Debug "lost Focus"
          EndIf 
          
          ;Scroll Event
          If GetGadgetAttribute(*Gdt\Gadget, #PB_Canvas_MouseX)>GadgetWidth(*Gdt\Gadget)-25
            ;Start Scroll
            If EventType()=#PB_EventType_LeftButtonDown
              If *Gdt\CursorStartY=0
                *Gdt\CursorStartY=GetGadgetAttribute(*Gdt\Gadget, #PB_Canvas_MouseY) 
                *Gdt\CursorPosY=*Gdt\CursorStartY
                Debug "Start Scroll"
              EndIf 
            EndIf 
          EndIf   
          
          ;Scroll is Enable
          If *Gdt\CursorStartY>0
            *Gdt\DrawBufferImage=#True
            If EventType()=#PB_EventType_MouseMove
              *Gdt\CursorPosY=GetGadgetAttribute(*Gdt\Gadget, #PB_Canvas_MouseY)
            EndIf 
            
            ;Stop Scroll
            If EventType() = #PB_EventType_LeftButtonUp
              *Gdt\CursorStartY=0
              *Gdt\CursorDeltaY=0
              Debug "Stop Scroll"
            EndIf
            
            ;Thumbnails Event  
          ElseIf GetGadgetAttribute(*Gdt\Gadget, #PB_Canvas_MouseX)<GadgetWidth(*Gdt\Gadget)-25   
            ;Change Thumbs Size with MouseWheel
            If EventType() = #PB_EventType_MouseWheel
              *Gdt\Size=Param\Gdt()\Size+GetGadgetAttribute(*Gdt\Gadget,#PB_Canvas_WheelDelta)*8
              *Gdt\LoadFromIndex=#True
              *Gdt\GenerateBufferImage=#True
              ;param\DrawBufferImage=#True
            EndIf   
            
            ;Select Media
            If EventType()=#PB_EventType_LeftClick Or EventType()=#PB_EventType_LeftDoubleClick
              
              Protected GdtWidth.l=GadgetWidth(*Gdt\Gadget)-25
              Protected NbH.l=Int(GdtWidth/*Gdt\Size):*Gdt\NbH=NbH
              Protected NbV.l=Int(GadgetHeight(*Gdt\Gadget)/*Gdt\Size):*Gdt\NbV=NbV
              Protected MarginH.l=(GdtWidth-NbH* *Gdt\Size)/(NbH+1)
              Protected MarginV.l=(GadgetHeight(*Gdt\Gadget)-NbV* *Gdt\Size)/(NbV+1)
              
              Protected x.l=GetGadgetAttribute(*Gdt\Gadget, #PB_Canvas_MouseX) 
              Protected y.l=GetGadgetAttribute(*Gdt\Gadget, #PB_Canvas_MouseY) 
              
              Protected nx.l=(x-MarginH)/(*Gdt\Size+MarginH)
              Protected ny.l=(y-MarginV-*Gdt\ThumbsDeltaY)/(*Gdt\Size+MarginV)
              Debug Str(nx)+","+Str(ny)
              Protected index.l=nx+ny*NbH+NbH
              Debug "INDEX Selected:"+Str(index)
              Protected *Ptr.core::FileData
              LockMutex(*Gdt\MutexThumb)
              
              
              ;Get 
              If index>-1 And SelectElement(*Gdt\ThumbPointer(),index)<>0 
                *Ptr=*Gdt\ThumbPointer()
                Debug"ATTENTION"
                Debug *Ptr
                Select EventType()
                  Case #PB_EventType_LeftClick
                    ;*Ptr\Selected=1
                    *Ptr\Selected=1-*Ptr\Selected
                    Debug "Left CLick"
                    ;                   Debug Param\CallBackSimpleClick
                    ;                   If Param\CallBackSimpleClick<>0 ; check if a @Procedure is added
                    ;                     Debug "@procedure"
                    ;                     Param\CallBackSimpleClick(Param\FileList()\FileName,Param\FileList()\Selected)
                    ;                   EndIf 
                  Case #PB_EventType_LeftDoubleClick
                    ;                   Debug "Left Double Click"
                    ;                   If Param\CallBackDoubleClick<>0 ; check if a @Procedure is added
                    ;                     Debug "@procedure"
                    ;                     Param\CallBackDoubleClick(Param\FileList()\FileName)
                    ;                   EndIf
                    ;                   Param\IndexNav=index
                EndSelect
              EndIf 
              UnlockMutex(*Gdt\MutexThumb)
              *Gdt\DrawBufferImage=#True
              *Gdt\GenerateBufferImage=#True
            EndIf
          EndIf  
          
          
      EndSelect
    EndIf
    UnlockMutex(Param\GdtMutex)
  EndProcedure
  
  Procedure CallBackLoadFromInternalListIndex(GadgetId.i,Index.i,Lenght.l)
    Protected n.l
    Protected TmpIndex.i
    LockMutex(Param\GdtMutex)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      Debug "CallBackLoadFromIndex("+Str(Index)+","+Str(Lenght)+")"
      For n=1 To Lenght
        TmpIndex=Index+n-1
        If TmpIndex>=0 And TmpIndex<ListSize(Param\Gdt()\FileList())
          SelectElement(Param\Gdt()\FileList(),TmpIndex)
          Protected *Ptr.Core::FileData
          *Ptr=Cache::GetFileDataFromCache(Param\Gdt()\FileList()\FilePath)
          If *Ptr
            Thumbs::AddImageToThumb(GadgetId,*Ptr)
          Else
            Thumbs::AddImageToThumb(GadgetId,0)
          EndIf 
        Else
          Thumbs::AddImageToThumb(GadgetId,-1)
        EndIf 
      Next
    EndIf
    UnlockMutex(Param\GdtMutex)
  EndProcedure
  
  
  ;-Navigate
  Procedure NavigateGadget(GadgetId.i,X.l,Y.l,Width.l,Height.l,ThumbnailsId.i)
    Protected Gdt.i
    Gdt=CanvasGadget(GadgetId.i,X.l,Y.l,Width.l,Height.l)
    If GadgetId=#PB_Any
      GadgetId=Gdt
    EndIf
    If AddMapElement(Param\GdtNav(),Str(GadgetId),#PB_Map_ElementCheck)
      Param\GdtNav()\Gadget=GadgetId
      Param\GdtNav()\ThumbnailsGadget=ThumbnailsId
    EndIf 
  EndProcedure
  
  Procedure DrawNavigate(Gadget.i)
    ;     Protected FileName.s
    ;     StartVectorDrawing(CanvasVectorOutput(Gadget))
    ;     VectorSourceColor(GetWindowColor(0))
    ;     FillVectorOutput()
    ;     Protected h.l=GadgetWidth(Gadget)
    ;     If FindMapElement(Param\Navigate(),Str(Gadget))
    ;       If FindMapElement(Param\Thumbnails(),Str(Param\Navigate()\ThumbnailsGadget))
    ;         h=GadgetHeight(Param\Navigate()\Gadget)
    ;         index=Param\Thumbnails()\IndexNav
    ;         nb=(Int(GadgetWidth(Param\Navigate()\Gadget)/h)-2)/2
    ;         
    ;         
    ;         For z=1 To nb
    ;           ;Right
    ;           
    ;           LockMutex(Param\FileListMutex)
    ;           
    ;           index_Right.i=index+z
    ;           FileName_Right.s=""
    ;           image_left.i=-1
    ;           If index_Right>=0 And index_Right<ListSize(Param\FileList())
    ;              If SelectElement(Param\FileList(),index_Right)>0
    ;               FileName_Right.s=Param\FileList()\FileName
    ;             EndIf 
    ;           EndIf
    ;           
    ;           ;Left
    ;           index_Left.i=index-z
    ;           FileName_Left.s=""
    ;           image_left.i=-1
    ;           If index_Left>=0 And index_Left<ListSize(Param\FileList())
    ;             
    ;             If SelectElement(Param\FileList(),index_Left)>0
    ;               FileName_Left.s=Param\FileList()\FileName
    ;             EndIf
    ;           EndIf 
    ;           UnlockMutex(Param\FileListMutex)
    ;           
    ;           
    ;           If FileName_Right<>""
    ;             image_right.i=GetImageByName(FileName_Right)
    ;             x=(GadgetWidth(Param\Navigate()\Gadget)/2-h/2)+z*h
    ;             y=0
    ;             AddPathBox(x,y,h,h)
    ;             SaveVectorState()
    ;             ClipPath()
    ;             DrawNavThumb(image_right,x,y,255-(z*200/nb));
    ;             RestoreVectorState()
    ;           EndIf
    ;           
    ;           If FileName_Left<>""
    ;             image_left.i=GetImageByName(FileName_Left.s)
    ;             x=(GadgetWidth(Param\Navigate()\Gadget)/2-GadgetHeight(Param\Navigate()\Gadget)/2)-z*GadgetHeight(Param\Navigate()\Gadget)
    ;             y=0
    ;             AddPathBox(x,y,GadgetHeight(Param\Navigate()\Gadget),GadgetHeight(Param\Navigate()\Gadget))
    ;             SaveVectorState()
    ;             ClipPath()
    ;             DrawNavThumb(image_left,x,y,255-(z*200/nb));-(z*200/nb)
    ;             RestoreVectorState()
    ;           EndIf 
    ;           
    ;           
    ;         Next
    ;         
    ;         If index>=0 And index<ListSize(Param\FileList())
    ;           FileName.s=""
    ;           If SelectElement(Param\FileList(),index)>0
    ;             FileName.s=Param\FileList()\FileName
    ;           EndIf
    ;         EndIf   
    ;         
    ;         
    ;         If FileName<>""
    ;           image=GetImageByName(FileName.s)
    ;           x=GadgetWidth(Param\Navigate()\Gadget)/2-GadgetHeight(Param\Navigate()\Gadget)/2
    ;           y=0
    ;           AddPathBox(x,y,GadgetHeight(Param\Navigate()\Gadget),GadgetHeight(Param\Navigate()\Gadget))
    ;           SaveVectorState()
    ;           ClipPath()
    ;           DrawNavThumb(image,x,y,255)
    ;           RestoreVectorState()
    ;           AddPathBox(x,y,GadgetHeight(Param\Navigate()\Gadget),GadgetHeight(Param\Navigate()\Gadget))
    ;           VectorSourceColor(RGBA(255, 0, 0, 255))
    ;           StrokePath(2)
    ;         EndIf 
    ;         
    ;         
    ;       EndIf 
    ;     EndIf 
    ;     StopVectorDrawing()
  EndProcedure
  
  Procedure CheckEventNav(Event.i)
    ;     If Event=#PB_Event_Timer
    ;       ForEach Param\Navigate()
    ;         If Param\Refresh=#True 
    ;           DrawNavigate(Param\Navigate()\Gadget)
    ;         EndIf 
    ;       Next
    ;       If Param\Refresh=#False
    ;       EndIf 
    ;     EndIf 
    ;     
    ;     ForEach Param\Navigate()
    ;       
    ;       If Event = #PB_Event_Gadget And EventGadget() = Param\Navigate()\Gadget
    ;         
    ;         If EventType()=#PB_EventType_Focus
    ;           Debug "Focus"
    ;         EndIf 
    ;         
    ;         If EventType()=#PB_EventType_LostFocus
    ;           Debug "lost Focus"
    ;         EndIf 
    ;         
    ;         If EventType()=#PB_EventType_LeftButtonDown
    ;           If GetGadgetAttribute(Param\Navigate()\Gadget, #PB_Canvas_MouseX)>GadgetWidth(Param\Navigate()\Gadget)/2
    ;             param\Thumbnails(Str(Param\Navigate()\ThumbnailsGadget))\IndexNav+1
    ;           Else
    ;              param\Thumbnails(Str(Param\Navigate()\ThumbnailsGadget))\IndexNav-1
    ;            EndIf
    ;            
    ;           LockMutex(Param\FileListMutex)
    ;           If param\Thumbnails()\IndexNav<0:param\Thumbnails()\IndexNav=0:EndIf
    ;           If param\Thumbnails()\IndexNav>ListSize(Param\FileList())-1:param\Thumbnails()\IndexNav=ListSize(Param\FileList())-1:EndIf
    ;           
    ;           index=param\Thumbnails()\IndexNav
    ;           
    ;           If index>-1 And SelectElement(Param\FileList(),index)<>0 
    ;             If Param\Thumbnails()\CallBackDoubleClick<>0 ; check if a @Procedure is added
    ;               Param\Thumbnails()\CallBackDoubleClick(Param\FileList()\FileName)
    ;             EndIf
    ;           EndIf 
    ;           UnlockMutex(Param\FileListMutex)
    ;         EndIf
    ;       EndIf 
    ;     Next       
  EndProcedure
  
  
  ;-List
  
  Procedure AddFileToList(GadgetId.i,FilePath.s,nImage.i=0)
    LockMutex(param\GdtMutex)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      ;Select Data from Id
      AddElement(Param\Gdt()\FileList())
      Param\Gdt()\FileList()\FilePath=FilePath
    EndIf 
    UnlockMutex(param\GdtMutex)
  EndProcedure   
  
  Procedure ClearTheList(GadgetId.i)
    LockMutex(param\GdtMutex)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      ClearList(Param\Gdt()\FileList())
    EndIf
    UnlockMutex(param\GdtMutex)
  EndProcedure
  
  Procedure.i GetTheListSize(GadgetId.i)
    LockMutex(param\GdtMutex)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      Protected n.i
      
      LockMutex(Param\Gdt()\FileListMutex)
      n=ListSize(Param\Gdt()\FileList())
      UnlockMutex(Param\Gdt()\FileListMutex)
    EndIf
    UnlockMutex(param\GdtMutex)
    ProcedureReturn n
  EndProcedure
  
  
EndModule  

CompilerIf #PB_Compiler_IsMainFile 
  UseJPEGImageDecoder()
  UsePNGImageDecoder()
  UseMD5Fingerprint()
  Enumeration
    #Win_main
    #Gdt_Nav
    #Gdt_ThumbA
    #Gdt_ThumbB
  EndEnumeration
  
  Procedure CallBackDoubleClick(FileName.s)
    Debug FileName
  EndProcedure
  
  
  Global NewList CurrentList.s()
  
  Procedure CallBackLoadFromIndex(GadgetId.i,Index.i,Lenght.l)
    Protected n.l
    Protected TmpIndex.i
    
    Debug "CallBackLoadFromIndex("+Str(Index)+","+Str(Lenght)+")"
    For n=1 To Lenght
      TmpIndex=Index+n-1
      If TmpIndex>=0 And TmpIndex<ListSize(CurrentList())
        SelectElement(CurrentList(),TmpIndex)
        Protected *Ptr.Core::FileData
        *Ptr=Cache::GetFileDataFromCache(CurrentList())
        If *Ptr
          Thumbs::AddImageToThumb(GadgetId,*Ptr)
        Else
          Thumbs::AddImageToThumb(GadgetId,0)
        EndIf 
      Else
        Thumbs::AddImageToThumb(GadgetId,-1)
      EndIf 
    Next
  EndProcedure
  
  If OpenWindow(#Win_main, 0, 0, 1024, 600, "Thumbnails", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    LoadFont(0, "Arial", 20, #PB_Font_Bold)
    Thumbs::ThumbnailsGadget(#Gdt_ThumbA,0,50,512,550,128)
    Thumbs::ThumbnailsGadget(#Gdt_ThumbB,512,50,512,550,128)
    Define Repertoire$
    Define Event.i
    Repertoire$="D:\Photos\2019-07-14 Week-End Chez Angélique et Nadège\";"D:\Documents a Trier\Camera\";"C:\Users\413\Pictures\";"D:\Cloud\Amazon Drive\Photos\Photos a trier\"
    Repertoire$=PathRequester("Chose Directory", Repertoire$)
    If ExamineDirectory(0, Repertoire$, "*.jpg")  
      While NextDirectoryEntry(0)
        If DirectoryEntryType(0) = #PB_DirectoryEntry_File
          AddElement(CurrentList())
          CurrentList()=Repertoire$+DirectoryEntryName(0)
          Debug DirectoryEntryName(0)
        EndIf
      Wend
      FinishDirectory(0)
    EndIf 
    Thumbs::SetCallBackLoadFromIndex(#Gdt_ThumbA,@CallBackLoadFromIndex())
    Thumbs::SetCallBackLoadFromIndex(#Gdt_ThumbB,@CallBackLoadFromIndex())
    Thumbs::Start(#Gdt_ThumbA)
    Thumbs::Start(#Gdt_ThumbB)
    
    Repeat
      Delay(1)
      Event = WindowEvent()
      ;Thumbs::CheckEvent(Event.i)
      ;MagicGdt::EventThumb(Event)
      ;MagicGdt::EventNav(Event)
    Until Event = #PB_Event_CloseWindow
    Thumbs::Stop(#Gdt_ThumbA)
    Thumbs::Stop(#Gdt_ThumbB)
  EndIf
CompilerEndIf
Avatar de l’utilisateur
Philippe_GEORGES
Messages : 119
Inscription : mer. 28/janv./2009 13:28

Re: Thumbnails Gadget V6

Message par Philippe_GEORGES »

Merci pour ce travail remarquable.

Juste une question : avez vous un ouvrage de référence à recommander afin de comprendre et de coder avec ce niveau de technicité ?

Encore un grand merci et félicitations !

Phil
Philippe GEORGES
"La simplicité est la sophistication suprême" (De Vinci)
assistance informatique, création de logiciels
georges.informatique@gmail.com
Avatar de l’utilisateur
Thyphoon
Messages : 2697
Inscription : mer. 25/août/2004 6:31
Localisation : Eragny
Contact :

Re: Thumbnails Gadget V6

Message par Thyphoon »

Bonjour,

Merci :oops: mais je suis pas sur d'être un très bon codeur :| Je me suis fais sur le tat :mrgreen:
Le mieux pour ça c'est de se donner des petits challenges et prendre le code des autres et les modifier et essayer de les comprendre.
A chaque fois que je finis de coder quelques choses je sais que si je le refais, ça sera mieux. On apprend a force de patience.
Ici dans le code que j'ai donné il y a de gros bugs encore et quelques plantages, mais j'ai trouvé d'où ça venait. je mettrais a jour le code des que j'aurais amélioré certain éléments
Avatar de l’utilisateur
Kwai chang caine
Messages : 6962
Inscription : sam. 23/sept./2006 18:32
Localisation : Isere

Re: Thumbnails Gadget V6

Message par Kwai chang caine »

Ca marche ici, au premier regard :wink:
Les textes sont très gros et se chevauchent
Merci pour le partage 8)
ImageLe bonheur est une route...
Pas une destination

PureBasic Forum Officiel - Site PureBasic
Avatar de l’utilisateur
Thyphoon
Messages : 2697
Inscription : mer. 25/août/2004 6:31
Localisation : Eragny
Contact :

Re: Thumbnails Gadget V6

Message par Thyphoon »

Kwai chang caine a écrit : jeu. 05/août/2021 19:46 Ca marche ici, au premier regard :wink:
Les textes sont très gros et se chevauchent
Merci pour le partage 8)
Salut ! Oui le texte il faut que je le retravaille ! Mais je corrige d'abord les bugs... j'en ai déjà corrigé beaucoup, mais je continue. Je posterais surement demain une version du code bien meilleur. :P
Avatar de l’utilisateur
Thyphoon
Messages : 2697
Inscription : mer. 25/août/2004 6:31
Localisation : Eragny
Contact :

Re: Thumbnails Gadget V6

Message par Thyphoon »

Nouvelle version du code
Le code est toujours pas très propre. Mais tout les plantages ont été normalement corrigés et pas mal d'Optimisation.

Code : Tout sélectionner

; ******************************************************************** 
; Program:           Thumbnails
; Description:       add a Thumbnails to select image 
; Version:           6Alpha2
; Author:            Thyphoon
; Date:              August, 2021
; License:           Free, unrestricted, credit 
;                    appreciated but not required.
; Note:              Please share improvement !
; ******************************************************************** 

CompilerIf Not Defined(Core,#PB_Module)
  DeclareModule Core
    Structure FileData
      FilePath.s
      Selected.b
      State.b                 ; 0 No Loaded ; 1 loaded; 2 Displayed
      Image.i
      ;You can Add All You want After
      Map MetaData.s()
    EndStructure
    
  EndDeclareModule
  
  Module Core
  EndModule
CompilerEndIf 

DeclareModule ImgTools
  Structure DefDisplayImage
    X.l
    Y.l
    Width.l
    Height.l
  EndStructure
  
  Enumeration
    #Image_Style_Fit
    #Image_Style_Fill
    #Image_Style_Stretch
  EndEnumeration 
  
  Declare ImageToContainer(*result.DefDisplayImage,Image,ContainerWidth.l,ContainerHeight.l,Style.l=#Image_Style_Fit)
EndDeclareModule

Module ImgTools
  Procedure ImageToContainer(*result.DefDisplayImage,Image,ContainerWidth.l,ContainerHeight.l,Style.l=#Image_Style_Fit)
    
    If IsImage(Image)
      Protected ImgRatio.l
      Protected ContRatio.l
      Protected ContWidth.l,ContHeight.l
      ImgRatio.l = ImageWidth(Image) / ImageHeight(Image)
      ContRatio.l = ContainerWidth /ContainerHeight
      
      Select Style
        Case #Image_Style_Fit
          If ImgRatio<ContRatio
            *result\Width=ImageWidth(Image)*ContainerHeight/ImageHeight(Image)
            *result\Height=ContainerHeight
            *result\X=(ContainerWidth-*result\Width)/2
            *result\Y=0
          Else
            *result\Width=ContainerWidth
            *result\Height=ImageHeight(Image)*ContainerWidth/ImageWidth(Image)
            *result\X=0
            *result\Y=(ContainerHeight-*result\Height)/2
          EndIf
        Case #Image_Style_Fill
          If ImgRatio<ContRatio
            *result\Width=ImageWidth(Image)*ContainerHeight/ImageHeight(Image)
            *result\Height=ContainerHeight
            *result\X=(ContainerWidth-*result\Width)/2
            *result\Y=0
          Else
            *result\Width=ContainerWidth
            *result\Height=ImageHeight(Image)*ContainerWidth/ImageWidth(Image)
            *result\X=0
            *result\Y=(ContainerHeight-*result\Height)/2
          EndIf
          
        Case #Image_Style_Stretch
          *result\X=0
          *result\Y=0
          *result\Width=ContainerWidth
          *result\Height=ContainerHeight
      EndSelect
      
    EndIf            
  EndProcedure
EndModule

;-Cache Module

DeclareModule Cache
  EnableExplicit
  
  Prototype.i CallBackLoadMedia(*Ptr.Core::FileData)
  
  Structure Param
    CallBackLoadMedia.CallBackLoadMedia
    ;LoadList  
    LoadListSemaphore.i
    LoadListMutex.i
    List LoadList.i()
    ;CacheList
    CacheListMutex.i
    Map CacheList.Core::FileData()
    BackGroundThread.i
    ;Message
    QuitMutex.i
    Quit.b
  EndStructure
  
  Global Param.Param
  Param\LoadListSemaphore=CreateSemaphore(4)
  Param\LoadListMutex=CreateMutex()
  Param\CacheListMutex=CreateMutex()
  Param\QuitMutex=CreateMutex()
  
  Declare SetCallBackLoadMedia(CallBackLoadMedia.i)
  Declare AddFileToLoadList(FilePath.s)  
  Declare CacheClean()  
  Declare AutoLoadStart() 
  Declare.i GetFileDataFromCache(FilePath.s,Image.i=0)
  Declare Quit()
EndDeclareModule

Module Cache
  
  Procedure SetCallBackLoadMedia(CallBackLoadMedia.i)
    Param\CallBackLoadMedia=CallBackLoadMedia
  EndProcedure
  
  Procedure AddFileToLoadList(FilePath.s)
    Protected *Ptr
    LockMutex(param\CacheListMutex)
    *Ptr=AddMapElement(param\CacheList(),FilePath)
    param\CacheList()\FilePath=FilePath
    param\CacheList()\State=0
    UnlockMutex(param\CacheListMutex)
    
    LockMutex(Param\LoadListMutex)
    AddElement(param\LoadList())
    param\LoadList()=*Ptr
    UnlockMutex(Param\LoadListMutex)
  EndProcedure
  
  Procedure LoadCacheDataThread(*Ptr.core::FileData)
    If *Ptr\Image=0
      Debug "Cache Load:"+*Ptr\FilePath
      ;Param\CallBackLoadMedia=0 ;To Force to use Internal Loader
      If Param\CallBackLoadMedia<>0 ; <- Use extern procedure to Load Image
        Param\CallBackLoadMedia(*Ptr)
      Else
        LockMutex(Param\CacheListMutex)           ; <- Or intern with PB Plugin
        *Ptr\Image=LoadImage(#PB_Any,*Ptr\FilePath)
        UnlockMutex(Param\CacheListMutex)
      EndIf  
      
      ;Resize Image to Thumnails MaxSize
      If *Ptr\Image<>0
        Protected result.ImgTools::DefDisplayImage
        ImgTools::ImageToContainer(@result,*Ptr\Image,256,256,ImgTools::#Image_Style_Fit)
        ResizeImage(*Ptr\Image,result\Width,result\Height,#PB_Image_Smooth) 
        *Ptr\State=1; Ready to Display Image
                    ;If can't load image  
      Else
        ;MessageRequester("Thumbnails Error","ERROR THREAD LOAD IMAGE"+Chr(13)+FilePath+Chr(13)+" in "+#PB_Compiler_Procedure+"()",#PB_MessageRequester_Ok | #PB_MessageRequester_Error)
        Debug "ERROR THREAD LOAD IMAGE"+Chr(13)+*Ptr\FilePath
        *Ptr\Image=CreateImage(#PB_Any,320,200)
        StartDrawing(ImageOutput(*Ptr\Image))
        Box(0,0,320,200,RGB(0,255,0))
        StopDrawing()
        *Ptr\State=1
      EndIf
    EndIf   
    SignalSemaphore(Param\LoadListSemaphore)
  EndProcedure
  
  Procedure BackgroundThread(n.l)
    Protected Quit.b
    Repeat 
      Repeat 
        LockMutex(Param\LoadListMutex)
        ;Select Data from Id
        If FirstElement(Param\LoadList())<>0
          Protected *Ptr.core::FileData
          *Ptr=Param\LoadList()
          WaitSemaphore(Param\LoadListSemaphore)
          If CreateThread(@LoadCacheDataThread(),*Ptr)<>0
            
            DeleteElement(Param\LoadList())
          Else
            Debug "######################Can't Start Thread"
            End
          EndIf   
          
        EndIf 
        UnlockMutex(Param\LoadListMutex)
        ;Else
        ;  Debug "Wait Semaphore"
        ;  Delay(200)
        ; EndIf 
        
        ; Debug "LOADLIST SIZE:"+Str(ListSize(Param\LoadList()))
        LockMutex(Param\QuitMutex)
        Quit=Param\Quit
        UnlockMutex(Param\QuitMutex)
        
      Until ListSize(Param\LoadList())=0 Or Quit=#True
      Delay(5)
    Until Quit=#True
    Debug "Bye Bye ! Cache::BackgroundThread()"
  EndProcedure
  
  Procedure Quit()
    Debug "QUIT CACHE"
    If IsThread(Param\BackGroundThread)
      LockMutex(Param\QuitMutex)
      Param\Quit=#True
      UnlockMutex(Param\QuitMutex)
      Debug "Cache::Quit Wait Param\BackGroundThread"
      WaitThread(Param\BackGroundThread)
      Debug "Cache::Quit Finish"
    EndIf 
  EndProcedure
  
  Procedure AutoLoadStart()
    If IsThread(Param\BackGroundThread)=#False
      Param\BackGroundThread=CreateThread(@BackgroundThread(),0)
    EndIf 
  EndProcedure
  
  Procedure Free(*Ptr.core::FileData)
    LockMutex(Param\CacheListMutex)
    If IsImage(*Ptr\Image):FreeImage(*Ptr\Image):EndIf
    FreeMap(*Ptr\MetaData())
    UnlockMutex(Param\CacheListMutex)
  EndProcedure
  
  
  ;TODO remake it
  Procedure CacheClean()
    Protected *Ptr.core::FileData
    LockMutex(Param\CacheListMutex)
    ForEach Param\CacheList() 
      If MapSize(Param\CacheList())<500
        Break;
      Else
        *Ptr=Param\CacheList()
        If *Ptr\State=1 And *Ptr\Selected=#False
          Debug "Free Cache :"+GetFilePart(*Ptr\FilePath)+" State:"+Str(*Ptr\State)
          Free(*Ptr)
          DeleteMapElement(Param\CacheList())
        EndIf 
      EndIf 
    Next
    UnlockMutex(Param\CacheListMutex)
  EndProcedure
  
  Procedure.i GetFileDataFromCache(FilePath.s,Image.i=0)
    LockMutex(Param\CacheListMutex)
    Protected *Ptr.core::FileData
    *Ptr=FindMapElement(Param\CacheList(),FilePath)
    UnlockMutex(Param\CacheListMutex)
    If *Ptr=0
      ;AddToLoadList
      LockMutex(Param\CacheListMutex)
      *Ptr=AddMapElement(Param\CacheList(),FilePath)
      *Ptr\FilePath=FilePath
      ;TODO CHecki This
      ;If Image=0
      *Ptr\State=0
      *Ptr\Image=0
      ;Else ;If We have Image (Ex when use Blob in DB)
      ;  *Ptr\State=1
      ;  *Ptr\Image=Image
      ;EndIf
      UnlockMutex(Param\CacheListMutex)
      LockMutex(Param\LoadListMutex)
      AddElement(param\LoadList())
      Param\LoadList()=*Ptr
      UnlockMutex(Param\LoadListMutex)
      AutoLoadStart()
    EndIf 
    ProcedureReturn *Ptr
    
  EndProcedure
EndModule

;-Thumbs Module

DeclareModule Thumbs
  EnableExplicit  
  ;Declare SetBusyIcon(Image.i)
  Declare SetCallBackLoadFromIndex(GadgetId.i,CallBackLoadFromIndex.i)
  Declare SetCallBackSimpleClick(GadgetId.i,CallBackLoadFromIndex.i)
  Declare SetCallBackDoubleClick(GadgetId.i,CallBackLoadFromIndex.i)
  Declare Refresh(time.l)
  Declare MyEvent()
  Declare ThumbnailsGadget(GadgetId.i,X.l,Y.l,Width.l,Height.l,Size.l)
  Declare Stop()
  Declare AddImageToThumb(GadgetId.i,*Ptr)
  Declare AddToDisplay(GadgetId.i,Index.i,FullPath.s,Image.i=0)
  Declare SetNumberOfElements(GadgetId.i,Number.l)
  
  ;Declare Navigate
  Declare NavigateGadget(GadgetId.i,X.l,Y.l,Width.l,Height.l,ThumbnailsId.i)
  Declare DrawNavigate(Gadget.i)
  Declare CheckEventNav(Event.i)
  ;-Declare List
  Declare CallBackLoadFromInternalListIndex(GadgetId.i,Index.i,Lenght.l)
  Declare AddFileToList(GadgetId.i,FilePath.s,nImage.i=0)
  Declare ClearTheList(GadgetId.i)
  Declare.i GetTheListSize(GadgetId.i)
EndDeclareModule

Module Thumbs
  
  Prototype CallBackLoadFromIndex(GadgetId.i,Index.i,Lenght.l)
  Prototype.i CallBackDoubleClick(GadgetId.i,CallBackLoadFromIndex.i)
  Prototype.i CallBackSimpleClick(GadgetId.i,FileName.s,selected.b)
  Structure gdt
    
    BufferMutex.i
    BufferImage.i
    
    Gadget.i                ;Canvas Gadget number
    Size.l                  ;Thumb Size Width and Height 
    Index.i                 ;ThumbIndex
    NumberOfElementsOnBufferImage.l ; Number Of Elements send by the CallBackLoadFromIndex
    NbH.l                           ;Number of horizontal thumbnails
    NbV.l                           ;Number of Vertical thumbnails
    
    CursorStartY.l          ; Cursor Y coord when clic
    CursorPosY.l
    CursorDeltaY.l          ; Cursor Delta Y from StartY
    ThumbsDeltaY.l          ; Scroll Thumbs
    
    CallBackLoadFromIndex.CallBackLoadFromIndex
    CallBackDoubleClick.CallBackDoubleClick
    CallBackSimpleClick.CallBackSimpleClick
    
    FileListMutex.i
    List FileList.core::FileData()
    
    MutexThumb.i
    List ThumbPointer.i()
    ;MutexRedraw.i
    
    
    DrawBufferImage.b
    GenerateBufferImage.b
    LoadFromIndex.b
    
    
    
  EndStructure 
  
  Structure GdtNav     
    ThumbnailsGadget.i  
    Gadget.i                ;Canvas Gadget number
  EndStructure
  
  Structure param
    RefreshThread.i
    
    GdtMutex.i
    Map Gdt.Gdt()
    
    Map GdtNav.GdtNav()
    
    Quit.b        ;#True = Quit else #False
    
  EndStructure
  
  Global param.param
  param\GdtMutex=CreateMutex()
  
  LoadFont(0, "Impact", 20, #PB_Font_Bold)
  
  Procedure SetCallBackLoadFromIndex(GadgetId.i,CallBackLoadFromIndex.i)
    If IsGadget(GadgetId) And GadgetType(GadgetId)=#PB_GadgetType_Canvas
      LockMutex(param\GdtMutex)
      If FindMapElement(Param\Gdt(), Str(GadgetId))
        Param\Gdt()\CallBackLoadFromIndex=CallBackLoadFromIndex
        param\Gdt()\GenerateBufferImage=#True
        param\Gdt()\LoadFromIndex=#True
        param\Gdt()\DrawBufferImage=#True
      EndIf
      UnlockMutex(param\GdtMutex)
    Else
      Debug "Gadget "+Str(GadgetId)+" Not Initialized Or Wrong Type Thumbs::SetCallBackLoadFromIndex()"
    EndIf 
  EndProcedure
  
  Procedure SetCallBackDoubleClick(GadgetId.i,CallBackDoubleClick.i)
    LockMutex(param\GdtMutex)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      Param\Gdt()\CallBackDoubleClick=CallBackDoubleClick
    EndIf
    UnlockMutex(param\GdtMutex)
  EndProcedure
  
  Procedure SetCallBackSimpleClick(GadgetId.i,CallBackSimpleClick.i)
    LockMutex(param\GdtMutex)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      Param\Gdt()\CallBackSimpleClick=CallBackSimpleClick
    EndIf
    UnlockMutex(param\GdtMutex)
  EndProcedure
  
  Procedure ThumbnailsGadget(GadgetId.i,X.l,Y.l,Width.l,Height.l,Size.l)
    Protected Gdt.i
    Gdt=CanvasGadget(GadgetId.i,X.l,Y.l,Width.l,Height.l)
    If GadgetId=#PB_Any
      GadgetId=Gdt
    EndIf
    LockMutex(param\GdtMutex)
    AddMapElement(Param\Gdt(),Str(GadgetId))
    Param\Gdt()\BufferMutex=CreateMutex()
    Param\Gdt()\FileListMutex=CreateMutex()
    Param\Gdt()\Gadget=GadgetId
    Param\Gdt()\Size=128
    param\Gdt()\BufferMutex=CreateMutex()
    param\Gdt()\MutexThumb=CreateMutex()
    If IsThread(Param\RefreshThread)=#False
      Param\RefreshThread=CreateThread(@Refresh(),10)
    EndIf 
    param\Gdt()\LoadFromIndex=#True
    param\Gdt()\GenerateBufferImage=#True
    param\Gdt()\DrawBufferImage=#True
    BindGadgetEvent(GadgetId,@MyEvent(),#PB_All)
    UnlockMutex(param\GdtMutex)
  EndProcedure
  
  Procedure AddImageToThumb(GadgetId.i,*Ptr)
    LockMutex(param\GdtMutex)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      LockMutex(param\Gdt()\MutexThumb)
      AddElement(param\Gdt()\ThumbPointer())
      param\Gdt()\ThumbPointer()=*Ptr
      UnlockMutex(param\Gdt()\MutexThumb)
    EndIf
    UnlockMutex(param\GdtMutex)
  EndProcedure
  
  Procedure GenerateBufferImage(GadgetId.i)
    LockMutex(param\GdtMutex)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      
      Protected.l _GadgetWidth=DesktopScaledX(GadgetWidth(Param\Gdt()\Gadget))
      Protected.l _GadgetHeight=DesktopScaledY(GadgetHeight(Param\Gdt()\Gadget))
      Protected.l _Size=DesktopScaledX(Param\Gdt()\Size)
      Protected.l _ScrollWidth=DesktopScaledX(25)
      Protected GdtWidth.l=_GadgetWidth-_ScrollWidth
      Protected NbH.l=Int(GdtWidth/_Size):Param\Gdt()\NbH=NbH
      Protected NbV.l=Int(_GadgetHeight/_Size):Param\Gdt()\NbV=NbV
      Protected MarginH.l=(GdtWidth-NbH*_Size)/(NbH+1)
      Protected MarginV.l=(_GadgetHeight-NbV*_Size)/(NbV+1)
      
      ;First Draw create the Image
      LockMutex(param\Gdt()\BufferMutex)
      If IsImage(param\Gdt()\BufferImage)=#False
        param\Gdt()\BufferImage=CreateImage(#PB_Any,_GadgetWidth-_ScrollWidth,_GadgetHeight+2*_Size)
        param\Gdt()\LoadFromIndex=#True
      EndIf 
      UnlockMutex(param\Gdt()\BufferMutex)
      
      LockMutex(param\Gdt()\MutexThumb)
      Protected *Ptr.core::FileData
      If param\Gdt()\CallBackLoadFromIndex>0
        If param\Gdt()\LoadFromIndex=#True
          param\Gdt()\LoadFromIndex=#False
          ForEach Param\Gdt()\ThumbPointer()
            *Ptr=Param\Gdt()\ThumbPointer()
            If *Ptr>0
              If *Ptr\State=2:*Ptr\State=1:EndIf
            EndIf
            DeleteElement(Param\Gdt()\ThumbPointer())
          Next
          
          ;ClearList(Param\Gdt()\ThumbPointer()) ; ça plante donc au dessus j'efface au fur et a mesure avec DeleteElement(Param\Gdt()\ThumbPointer())
          param\Gdt()\CallBackLoadFromIndex(Param\Gdt()\Gadget,Param\Gdt()\Index-Nbh,(NbV+2)*Nbh)
          Debug "param\CallBackLoadFromIndex("+Str(Param\Gdt()\Index-Nbh)+","+Str((NbV+2)*Nbh)+")"
        EndIf 
      Else 
        Debug "No Set CallBackLoadFromIndex"
      EndIf 
      UnlockMutex(param\Gdt()\MutexThumb)
      
      Protected ContinueGenerateBufferImage=#False
      param\Gdt()\GenerateBufferImage=#True
      LockMutex(param\Gdt()\BufferMutex)
      If IsImage(Param\Gdt()\BufferImage) And StartVectorDrawing(ImageVectorOutput(Param\Gdt()\BufferImage))
        VectorSourceColor(RGBA(128, 128, 128, 255))
        FillVectorOutput()
        
        Protected.l nx,ny,x,y,i
        For ny=-1 To NbV+1
          For nx=0 To NbH-1
            ;Position
            x=nx*_Size+MarginH*nx+(MarginH)
            y=ny*_Size+MarginV*ny+(MarginV)
            i=nx+ny*NbH
            
            ;If in Limit
            If i>-1 And i<ListSize(Param\Gdt()\ThumbPointer())
              Protected State.b
              Protected Image.i
              Protected FileName.s
              Protected Selected.b
              LockMutex(param\Gdt()\MutexThumb)
              *Ptr=0
              If SelectElement(Param\Gdt()\ThumbPointer(),i)
                *Ptr=Param\Gdt()\ThumbPointer()
                Selected=0
                State=0
                Image=-1
                FileName="No File"
                If *Ptr>0
                  State=*Ptr\State
                  Image.i=*Ptr\Image
                  Selected=*Ptr\Selected
                  FileName.s=GetFilePart(*Ptr\FilePath)
                EndIf
              EndIf
              UnlockMutex(param\Gdt()\MutexThumb)
              
              If *Ptr>0
                If State>0 And IsImage(Image) ;If Image loaded 
                  Protected result.ImgTools::DefDisplayImage
                  ImgTools::ImageToContainer(@result,Image,_Size,_Size,ImgTools::#Image_Style_Fit)
                  ;If element selected display green
                  Protected _Border.l,_BorderX2.l
                  ;Draw Green Border when selected
                  If Selected=1
                    AddPathBox(result\X+x, result\Y+y,result\Width,result\Height)
                    VectorSourceColor(RGBA(0, 255, 0, 255))
                    FillPath()
                    _Border=DesktopScaledX(2)
                    _BorderX2=_Border*2
                  Else
                    _Border=DesktopScaledX(0)
                    _BorderX2=0
                  EndIf
                  ;Draw Image
                  MovePathCursor(result\X+x+_Border,result\Y+y+_Border)
                  DrawVectorImage(ImageID(Image),255,result\Width-_BorderX2,result\Height-_BorderX2)
                  
                  LockMutex(param\Gdt()\MutexThumb)
                  *Ptr\State=2
                  UnlockMutex(param\Gdt()\MutexThumb)
                Else
                  AddPathBox(x, y, _Size,_Size)
                  VectorSourceColor(RGBA(0, 0, 0, 255))
                  FillPath()
                  
                  VectorFont(FontID(0), 30)
                  VectorSourceColor(RGBA(0, 125, 0, 255))
                  MovePathCursor(result\X+x+_Border,result\Y+y+_Border+10)
                  DrawVectorText("Wait....")
                  ContinueGenerateBufferImage=#True
                EndIf
                VectorFont(FontID(0), 40)
                MovePathCursor(x+(_Size-VectorTextWidth(FileName))/2,y+result\Height-VectorTextHeight(FileName))
                VectorSourceColor(RGBA(255, 255, 255, 255))
                ;DrawVectorText(Str(*Ptr\Selected)+FileName)
              EndIf 
              
            Else ;overLimit
              AddPathBox(x, y, _Size,_Size)
              VectorSourceColor(RGBA(255, 0, 0, 50))
              FillPath()
            EndIf
            
          Next
        Next
        StopVectorDrawing()
      EndIf
      
      If ContinueGenerateBufferImage=#False
        param\Gdt()\GenerateBufferImage=#False
      Else
        Cache::AutoLoadStart()
      EndIf
      
      param\Gdt()\DrawBufferImage=#True
      UnlockMutex(param\Gdt()\BufferMutex)
      Cache::CacheClean()
    EndIf 
    UnlockMutex(param\GdtMutex)
  EndProcedure
  
  Procedure Refresh(time.l)
    Protected DeltaIndex.l
    Protected Quit.b=#False
    Repeat  
      
      If MapSize(param\Gdt())=0
        Quit=#True
      EndIf
      
      LockMutex(param\GdtMutex)
      ForEach param\Gdt()
        Protected UpdateList.b=#False
        Protected *Gdt.Gdt
        *Gdt=param\Gdt()
        Protected _Size=DesktopScaledX(*Gdt\Size)
        ;Scroll    
        If  *Gdt\CursorStartY>0
          If GetGadgetAttribute(*Gdt\Gadget, #PB_Canvas_MouseY)<>0
            *Gdt\CursorPosY=GetGadgetAttribute(*Gdt\Gadget, #PB_Canvas_MouseY)
          EndIf   
          *Gdt\CursorDeltaY=*Gdt\CursorStartY-*Gdt\CursorPosY
          
          *Gdt\ThumbsDeltaY=*Gdt\ThumbsDeltaY+(*Gdt\CursorDeltaY/10)
        EndIf 
        
        ;Change Index when Scroll > Thumbnails
        If *Gdt\ThumbsDeltaY>_Size
          DeltaIndex=Int(*Gdt\ThumbsDeltaY/_Size)* *Gdt\NbH
          *Gdt\Index=*Gdt\Index-DeltaIndex
          *Gdt\ThumbsDeltaY=*Gdt\ThumbsDeltaY%_Size
          UpdateList=#True
        EndIf
        
        If *Gdt\ThumbsDeltaY<-_Size 
          DeltaIndex=Abs(Int(*Gdt\ThumbsDeltaY/_Size)* *Gdt\NbH)
          *Gdt\Index=*Gdt\Index+DeltaIndex
          *Gdt\ThumbsDeltaY=*Gdt\ThumbsDeltaY%_Size
          UpdateList=#True
        EndIf 
        
        ;-Limit Up Scroll
        If *Gdt\Index<=0 And *Gdt\ThumbsDeltaY>0
          *Gdt\Index=0
          *Gdt\ThumbsDeltaY=0
          Debug "Limit Up Scroll"
        EndIf 
        
        ;-Limit Down Scroll
        Protected ni.l=ListSize(Param\Gdt()\ThumbPointer())-Param\Gdt()\NbH
        If ni>0
          If SelectElement(Param\Gdt()\ThumbPointer(),ni)
            If *Gdt\CursorDeltaY<0 And Param\Gdt()\ThumbPointer()=-1;And *Gdt\NumberOfElementsOnBufferImage<(*Gdt\NbH* (*Gdt\NbV-1))
              Debug "Limit Down Scroll "
              *Gdt\ThumbsDeltaY=0
            EndIf
          EndIf 
        EndIf
        
        
        If UpdateList=#True 
          *Gdt\LoadFromIndex=#True
          *Gdt\GenerateBufferImage=#True
          *Gdt\DrawBufferImage=#True
        EndIf 
        
        If *Gdt\GenerateBufferImage=#True
          GenerateBufferImage(*Gdt\Gadget)
        EndIf
        
        Protected.l _GadgetHeight=DesktopScaledX(GadgetHeight(*Gdt\Gadget))
        Protected.l _ScrollWidth=DesktopScaledX(25)
        Protected.l _ScrollHeight=_ScrollWidth*2
        If *Gdt\DrawBufferImage=#True
          If  StartVectorDrawing(CanvasVectorOutput(*Gdt\Gadget))
            LockMutex(*Gdt\BufferMutex)
            If IsImage(*Gdt\BufferImage)
              MovePathCursor(0,*Gdt\ThumbsDeltaY-_Size)
              DrawVectorImage(ImageID(*Gdt\BufferImage))
              AddPathBox(ImageWidth(*Gdt\BufferImage),0,_ScrollWidth,_GadgetHeight):VectorSourceColor(RGBA(100, 100, 100, 255)):FillPath()
              AddPathBox(ImageWidth(*Gdt\BufferImage),_GadgetHeight/2-_ScrollHeight-*Gdt\CursorDeltaY,_ScrollWidth,_ScrollHeight):VectorSourceColor(RGBA(200, 200, 200, 255)):FillPath()
              
            Else
              Debug "can't draw BufferImage"
            EndIf  
            UnlockMutex(*Gdt\BufferMutex)
            StopVectorDrawing()
            *Gdt\DrawBufferImage=#False
          EndIf
        EndIf
        
        ; If *Gdt\DrawBufferImage=#True
        ;   Delay(75)
        ; Else
        ;   Delay(200)
        ; EndIf
        
        
      Next
      UnlockMutex(param\GdtMutex)
      Delay(50)
    Until Quit=#True
    Debug "Refresh Say Bye Bye"
  EndProcedure
  
  
  
  
  Procedure MyEvent()
    Protected *Gdt.Gdt
    LockMutex(Param\GdtMutex)
    *Gdt=FindMapElement(Param\Gdt(), Str(EventGadget()))
    If *Gdt<>0
      UnlockMutex(Param\GdtMutex)
      Protected.l _GadgetWidth=DesktopScaledX(GadgetWidth(*Gdt\Gadget))
      Protected.l _GadgetHeight=DesktopScaledY(GadgetHeight(*Gdt\Gadget))
      Protected.l _Size=DesktopScaledX(*Gdt\Size)
      Protected.l _ScrollWidth=DesktopScaledX(25)
      ;Scroll Event
      If GetGadgetAttribute(*Gdt\Gadget, #PB_Canvas_MouseX)>_GadgetWidth-_ScrollWidth
        ;Start Scroll
        If EventType()=#PB_EventType_LeftButtonDown
          If *Gdt\CursorStartY=0
            *Gdt\CursorStartY=GetGadgetAttribute(*Gdt\Gadget, #PB_Canvas_MouseY) 
            *Gdt\CursorPosY=*Gdt\CursorStartY
            ;Debug "Start Scroll"
          EndIf 
        EndIf 
      EndIf   
      
      ;Scroll is Enable
      If *Gdt\CursorStartY>0
        *Gdt\DrawBufferImage=#True
        If EventType()=#PB_EventType_MouseMove
          *Gdt\CursorPosY=GetGadgetAttribute(*Gdt\Gadget, #PB_Canvas_MouseY)
        EndIf 
        
        ;Stop Scroll
        If EventType() = #PB_EventType_LeftButtonUp
          *Gdt\CursorStartY=0
          *Gdt\CursorDeltaY=0
          ;Debug "Stop Scroll"
        EndIf
        
        ;Thumbnails Event  
      ElseIf GetGadgetAttribute(*Gdt\Gadget, #PB_Canvas_MouseX)<_GadgetWidth-_ScrollWidth   
        ;Change Thumbs Size with MouseWheel
        If EventType() = #PB_EventType_MouseWheel
          *Gdt\Size=*Gdt\Size+GetGadgetAttribute(*Gdt\Gadget,#PB_Canvas_WheelDelta)*8
          *Gdt\LoadFromIndex=#True
          *Gdt\GenerateBufferImage=#True
          ;param\DrawBufferImage=#True
        EndIf   
        
        ;Select Media
        If EventType()=#PB_EventType_LeftClick Or EventType()=#PB_EventType_LeftDoubleClick
          
          Protected GdtWidth.l=_GadgetWidth-_ScrollWidth
          Protected NbH.l=Int(GdtWidth/_Size):*Gdt\NbH=NbH
          Protected NbV.l=Int(_GadgetHeight/_Size):*Gdt\NbV=NbV
          Protected MarginH.l=(GdtWidth-NbH* _Size)/(NbH+1)
          Protected MarginV.l=(_GadgetHeight-NbV* _Size)/(NbV+1)
          
          Protected x.l=GetGadgetAttribute(*Gdt\Gadget, #PB_Canvas_MouseX) 
          Protected y.l=GetGadgetAttribute(*Gdt\Gadget, #PB_Canvas_MouseY) 
          
          Protected nx.l=(x-MarginH)/(_Size+MarginH)
          Protected ny.l=(y-MarginV-*Gdt\ThumbsDeltaY)/(_Size+MarginV)
          Protected index.l=nx+ny*NbH+NbH
          Debug "INDEX Selected:"+Str(index)
          Protected *Ptr.core::FileData
          LockMutex(*Gdt\MutexThumb)
          
          ;Get 
          If index>-1 ;And index<ListSize(*Gdt\ThumbPointer())
            Debug "selectElement:"+Str(SelectElement(*Gdt\ThumbPointer(),index))
            If SelectElement(*Gdt\ThumbPointer(),index)
              *Ptr=*Gdt\ThumbPointer()
              Debug "*Ptr="+Str(*Ptr)
              If *Ptr>0
                Select EventType()
                  Case #PB_EventType_LeftClick
                    ;Debug GetFilePart(*Ptr\FilePath)+" Selected:"+Str(*Ptr\Selected)
                    Debug "Selected="+Str(*Ptr\Selected)
                    *Ptr\Selected=1-*Ptr\Selected
                    ;                   Debug Param\CallBackSimpleClick
                    ;                   If Param\CallBackSimpleClick<>0 ; check if a @Procedure is added
                    ;                     Debug "@procedure"
                    ;                     Param\CallBackSimpleClick(Param\FileList()\FileName,Param\FileList()\Selected)
                    ;                   EndIf 
                  Case #PB_EventType_LeftDoubleClick
                    ;                   Debug "Left Double Click"
                    ;                   If Param\CallBackDoubleClick<>0 ; check if a @Procedure is added
                    ;                     Debug "@procedure"
                    ;                     Param\CallBackDoubleClick(Param\FileList()\FileName)
                    ;                   EndIf
                    ;                   Param\IndexNav=index
                EndSelect
              EndIf
            EndIf
            
          EndIf 
          UnlockMutex(*Gdt\MutexThumb)
          *Gdt\DrawBufferImage=#True
          *Gdt\GenerateBufferImage=#True
        EndIf
      EndIf  
      
      
    EndIf
    UnlockMutex(Param\GdtMutex)
  EndProcedure
  
  Procedure Start(GadgetId.i)
    LockMutex(param\GdtMutex)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      If IsThread(Param\RefreshThread)=#False
        Param\RefreshThread=CreateThread(@Refresh(),10)
      EndIf 
      param\Gdt()\LoadFromIndex=#True
      param\Gdt()\GenerateBufferImage=#True
      param\Gdt()\DrawBufferImage=#True
      BindGadgetEvent(GadgetId,@MyEvent(),#PB_All)
    EndIf
    UnlockMutex(param\GdtMutex)
  EndProcedure
  
  
  Procedure FreeGdt(GadgetId.i)
    LockMutex(param\GdtMutex)
    UnbindGadgetEvent(GadgetId,@MyEvent(),#PB_All)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      If IsThread(Param\RefreshThread)=#True
        param\Gdt()\LoadFromIndex=#False
        param\Gdt()\GenerateBufferImage=#False
        param\Gdt()\DrawBufferImage=#False
        
        ;KillThread(Param\RefreshThread) ; <- Old Method
        Debug "Thumbs::Stop() Wait Thread Param\RefreshThread"
        WaitThread(Param\RefreshThread)
        Debug "Thumbs::Stop() Stop to Wait"
      EndIf
      
    EndIf
    UnlockMutex(param\GdtMutex)
  EndProcedure
  
  Procedure Stop()
    LockMutex(param\GdtMutex)
    ForEach Param\Gdt()
      UnbindGadgetEvent(Val(MapKey(Param\Gdt())),@MyEvent(),#PB_All)
      
      DeleteMapElement(Param\Gdt())
    Next
    UnlockMutex(param\GdtMutex)
    
    Debug "Thumbs::Stop() Wait Thread Param\RefreshThread"
    If IsThread(Param\RefreshThread)=#True
      param\Quit=#True
    EndIf 
    WaitThread(Param\RefreshThread)
    Debug "Thumbs::Stop() Stop to Wait"  
    Cache::Quit()   
    
  EndProcedure
  
  Procedure AddToDisplay(GadgetId.i,Index.i,FullPath.s,Image.i=0)
    Protected *Ptr.Core::FileData
    *Ptr=Cache::GetFileDataFromCache(FullPath,Image)
    If Index>=0 And FullPath<>""
      If *Ptr
        Thumbs::AddImageToThumb(GadgetId,*Ptr)
      Else
        Thumbs::AddImageToThumb(GadgetId,0)
      EndIf 
    Else
      Thumbs::AddImageToThumb(GadgetId,-1)
    EndIf 
  EndProcedure
  
  Procedure SetNumberOfElements(GadgetId.i,Number.l)
    LockMutex(param\GdtMutex)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      Param\Gdt()\NumberOfElementsOnBufferImage=Number
    EndIf
    UnlockMutex(param\GdtMutex)
  EndProcedure
  
  Procedure CallBackLoadFromInternalListIndex(GadgetId.i,Index.i,Lenght.l)
    Protected n.l
    Protected TmpIndex.i
    LockMutex(Param\GdtMutex)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      
      Debug "CallBackLoadFromIndex("+Str(Index)+","+Str(Lenght)+")"
      For n=0 To Lenght-1
        TmpIndex=Index+n
        If TmpIndex>=0 And TmpIndex<ListSize(Param\Gdt()\FileList())
          SelectElement(Param\Gdt()\FileList(),TmpIndex)
          Protected *Ptr.Core::FileData
          *Ptr=Cache::GetFileDataFromCache(Param\Gdt()\FileList()\FilePath)
          If *Ptr
            Thumbs::AddImageToThumb(GadgetId,*Ptr)
          Else
            Thumbs::AddImageToThumb(GadgetId,0)
          EndIf 
        Else
          Thumbs::AddImageToThumb(GadgetId,-1)
        EndIf 
      Next
    EndIf
    UnlockMutex(Param\GdtMutex)
  EndProcedure
  
  
  ;-Navigate
  Procedure NavigateGadget(GadgetId.i,X.l,Y.l,Width.l,Height.l,ThumbnailsId.i)
    Protected Gdt.i
    Gdt=CanvasGadget(GadgetId.i,X.l,Y.l,Width.l,Height.l)
    If GadgetId=#PB_Any
      GadgetId=Gdt
    EndIf
    If AddMapElement(Param\GdtNav(),Str(GadgetId),#PB_Map_ElementCheck)
      Param\GdtNav()\Gadget=GadgetId
      Param\GdtNav()\ThumbnailsGadget=ThumbnailsId
    EndIf 
  EndProcedure
  
  Procedure DrawNavigate(Gadget.i)
    ;     Protected FileName.s
    ;     StartVectorDrawing(CanvasVectorOutput(Gadget))
    ;     VectorSourceColor(GetWindowColor(0))
    ;     FillVectorOutput()
    ;     Protected h.l=GadgetWidth(Gadget)
    ;     If FindMapElement(Param\Navigate(),Str(Gadget))
    ;       If FindMapElement(Param\Thumbnails(),Str(Param\Navigate()\ThumbnailsGadget))
    ;         h=GadgetHeight(Param\Navigate()\Gadget)
    ;         index=Param\Thumbnails()\IndexNav
    ;         nb=(Int(GadgetWidth(Param\Navigate()\Gadget)/h)-2)/2
    ;         
    ;         
    ;         For z=1 To nb
    ;           ;Right
    ;           
    ;           LockMutex(Param\FileListMutex)
    ;           
    ;           index_Right.i=index+z
    ;           FileName_Right.s=""
    ;           image_left.i=-1
    ;           If index_Right>=0 And index_Right<ListSize(Param\FileList())
    ;              If SelectElement(Param\FileList(),index_Right)>0
    ;               FileName_Right.s=Param\FileList()\FileName
    ;             EndIf 
    ;           EndIf
    ;           
    ;           ;Left
    ;           index_Left.i=index-z
    ;           FileName_Left.s=""
    ;           image_left.i=-1
    ;           If index_Left>=0 And index_Left<ListSize(Param\FileList())
    ;             
    ;             If SelectElement(Param\FileList(),index_Left)>0
    ;               FileName_Left.s=Param\FileList()\FileName
    ;             EndIf
    ;           EndIf 
    ;           UnlockMutex(Param\FileListMutex)
    ;           
    ;           
    ;           If FileName_Right<>""
    ;             image_right.i=GetImageByName(FileName_Right)
    ;             x=(GadgetWidth(Param\Navigate()\Gadget)/2-h/2)+z*h
    ;             y=0
    ;             AddPathBox(x,y,h,h)
    ;             SaveVectorState()
    ;             ClipPath()
    ;             DrawNavThumb(image_right,x,y,255-(z*200/nb));
    ;             RestoreVectorState()
    ;           EndIf
    ;           
    ;           If FileName_Left<>""
    ;             image_left.i=GetImageByName(FileName_Left.s)
    ;             x=(GadgetWidth(Param\Navigate()\Gadget)/2-GadgetHeight(Param\Navigate()\Gadget)/2)-z*GadgetHeight(Param\Navigate()\Gadget)
    ;             y=0
    ;             AddPathBox(x,y,GadgetHeight(Param\Navigate()\Gadget),GadgetHeight(Param\Navigate()\Gadget))
    ;             SaveVectorState()
    ;             ClipPath()
    ;             DrawNavThumb(image_left,x,y,255-(z*200/nb));-(z*200/nb)
    ;             RestoreVectorState()
    ;           EndIf 
    ;           
    ;           
    ;         Next
    ;         
    ;         If index>=0 And index<ListSize(Param\FileList())
    ;           FileName.s=""
    ;           If SelectElement(Param\FileList(),index)>0
    ;             FileName.s=Param\FileList()\FileName
    ;           EndIf
    ;         EndIf   
    ;         
    ;         
    ;         If FileName<>""
    ;           image=GetImageByName(FileName.s)
    ;           x=GadgetWidth(Param\Navigate()\Gadget)/2-GadgetHeight(Param\Navigate()\Gadget)/2
    ;           y=0
    ;           AddPathBox(x,y,GadgetHeight(Param\Navigate()\Gadget),GadgetHeight(Param\Navigate()\Gadget))
    ;           SaveVectorState()
    ;           ClipPath()
    ;           DrawNavThumb(image,x,y,255)
    ;           RestoreVectorState()
    ;           AddPathBox(x,y,GadgetHeight(Param\Navigate()\Gadget),GadgetHeight(Param\Navigate()\Gadget))
    ;           VectorSourceColor(RGBA(255, 0, 0, 255))
    ;           StrokePath(2)
    ;         EndIf 
    ;         
    ;         
    ;       EndIf 
    ;     EndIf 
    ;     StopVectorDrawing()
  EndProcedure
  
  Procedure CheckEventNav(Event.i)
    ;     If Event=#PB_Event_Timer
    ;       ForEach Param\Navigate()
    ;         If Param\Refresh=#True 
    ;           DrawNavigate(Param\Navigate()\Gadget)
    ;         EndIf 
    ;       Next
    ;       If Param\Refresh=#False
    ;       EndIf 
    ;     EndIf 
    ;     
    ;     ForEach Param\Navigate()
    ;       
    ;       If Event = #PB_Event_Gadget And EventGadget() = Param\Navigate()\Gadget
    ;         
    ;         If EventType()=#PB_EventType_Focus
    ;           Debug "Focus"
    ;         EndIf 
    ;         
    ;         If EventType()=#PB_EventType_LostFocus
    ;           Debug "lost Focus"
    ;         EndIf 
    ;         
    ;         If EventType()=#PB_EventType_LeftButtonDown
    ;           If GetGadgetAttribute(Param\Navigate()\Gadget, #PB_Canvas_MouseX)>GadgetWidth(Param\Navigate()\Gadget)/2
    ;             param\Thumbnails(Str(Param\Navigate()\ThumbnailsGadget))\IndexNav+1
    ;           Else
    ;              param\Thumbnails(Str(Param\Navigate()\ThumbnailsGadget))\IndexNav-1
    ;            EndIf
    ;            
    ;           LockMutex(Param\FileListMutex)
    ;           If param\Thumbnails()\IndexNav<0:param\Thumbnails()\IndexNav=0:EndIf
    ;           If param\Thumbnails()\IndexNav>ListSize(Param\FileList())-1:param\Thumbnails()\IndexNav=ListSize(Param\FileList())-1:EndIf
    ;           
    ;           index=param\Thumbnails()\IndexNav
    ;           
    ;           If index>-1 And SelectElement(Param\FileList(),index)<>0 
    ;             If Param\Thumbnails()\CallBackDoubleClick<>0 ; check if a @Procedure is added
    ;               Param\Thumbnails()\CallBackDoubleClick(Param\FileList()\FileName)
    ;             EndIf
    ;           EndIf 
    ;           UnlockMutex(Param\FileListMutex)
    ;         EndIf
    ;       EndIf 
    ;     Next       
  EndProcedure
  
  
  ;-List
  
  Procedure AddFileToList(GadgetId.i,FilePath.s,nImage.i=0)
    LockMutex(param\GdtMutex)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      ;Select Data from Id
      AddElement(Param\Gdt()\FileList())
      Param\Gdt()\FileList()\FilePath=FilePath
    EndIf 
    UnlockMutex(param\GdtMutex)
  EndProcedure   
  
  Procedure ClearTheList(GadgetId.i)
    LockMutex(param\GdtMutex)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      ClearList(Param\Gdt()\FileList())
    EndIf
    UnlockMutex(param\GdtMutex)
  EndProcedure
  
  Procedure.i GetTheListSize(GadgetId.i)
    LockMutex(param\GdtMutex)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      Protected n.i
      
      LockMutex(Param\Gdt()\FileListMutex)
      n=ListSize(Param\Gdt()\FileList())
      UnlockMutex(Param\Gdt()\FileListMutex)
    EndIf
    UnlockMutex(param\GdtMutex)
    ProcedureReturn n
  EndProcedure
  
  
EndModule  

CompilerIf #PB_Compiler_IsMainFile 
  UseJPEGImageDecoder()
  UsePNGImageDecoder()
  UseMD5Fingerprint()
  Enumeration
    #Win_main
    #Gdt_Nav
    #Gdt_ThumbA
    #Gdt_ThumbB
  EndEnumeration
  
  Procedure CallBackDoubleClick(FileName.s)
    Debug FileName
  EndProcedure
  
  Global NewList CurrentList.s()
  Global NewList CurrentListB.s()
  
  Procedure CallBackLoadFromIndex(GadgetId.i,Index.i,Lenght.l)
    Protected n.l
    Protected TmpIndex.i
    Protected relativeIndex.l
    Protected FilePath.s
    Debug "CallBackLoadFromIndex("+Str(Index)+","+Str(Lenght)+")"
    For n=1 To Lenght
      TmpIndex=Index+n-1
      If TmpIndex>=0 And TmpIndex<ListSize(CurrentList())
        SelectElement(CurrentList(),TmpIndex)
        FilePath.s=CurrentList()
      Else
        FilePath.s=""
      EndIf 
      Thumbs::AddToDisplay(GadgetId,TmpIndex,FilePath) ;<- You must Add
      relativeIndex=relativeIndex+1
    Next
    Thumbs::SetNumberOfElements(GadgetId,relativeIndex) ;<- You Must Add This or Scroll Down is Locked. This value is to detect End of Scroll
  EndProcedure
  
  Procedure CallBackLoadFromIndexB(GadgetId.i,Index.i,Lenght.l)
    Protected n.l
    Protected TmpIndex.i
    Protected relativeIndex.l
    Debug "CallBackLoadFromIndexB("+Str(Index)+","+Str(Lenght)+")"
    For n=1 To Lenght
      TmpIndex=Index+n-1
      If TmpIndex>=0 And TmpIndex<ListSize(CurrentListB())
        SelectElement(CurrentListB(),TmpIndex)
        relativeIndex=relativeIndex+1
        Protected *Ptr.Core::FileData
        *Ptr=Cache::GetFileDataFromCache(CurrentListB())
        If *Ptr
          Thumbs::AddImageToThumb(GadgetId,*Ptr)
        Else
          Thumbs::AddImageToThumb(GadgetId,0)
        EndIf 
      Else
        Thumbs::AddImageToThumb(GadgetId,-1)
      EndIf 
    Next
    Thumbs::SetNumberOfElements(GadgetId,relativeIndex)
  EndProcedure
  
  If OpenWindow(#Win_main, 0, 0, 1024, 600, "Thumbnails", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    LoadFont(0, "Arial", 20, #PB_Font_Bold)
    Thumbs::ThumbnailsGadget(#Gdt_ThumbA,0,50,512,550,128)
    Thumbs::ThumbnailsGadget(#Gdt_ThumbB,512,50,512,550,128)
    Define Repertoire$
    Define Event.i
    Repertoire$="C:\Users\413\Pictures\Photos\"
    Repertoire$=PathRequester("Chose Directory", Repertoire$)
    If ExamineDirectory(0, Repertoire$, "*.jpg")  
      While NextDirectoryEntry(0)
        If DirectoryEntryType(0) = #PB_DirectoryEntry_File
          AddElement(CurrentList())
          CurrentList()=Repertoire$+DirectoryEntryName(0)
        EndIf
      Wend
      FinishDirectory(0)
    EndIf
    
    Repertoire$="C:\Users\413\Pictures\"
    Repertoire$=PathRequester("Chose Directory", Repertoire$)
    If ExamineDirectory(0, Repertoire$, "*.jpg")  
      While NextDirectoryEntry(0)
        If DirectoryEntryType(0) = #PB_DirectoryEntry_File
          AddElement(CurrentListB())
          CurrentListB()=Repertoire$+DirectoryEntryName(0)
        EndIf
      Wend
      FinishDirectory(0)
    EndIf 
    
    Thumbs::SetCallBackLoadFromIndex(#Gdt_ThumbA,@CallBackLoadFromIndex())
    Thumbs::SetCallBackLoadFromIndex(#Gdt_ThumbB,@CallBackLoadFromIndexB())
    
    Repeat
      Delay(1)
      Event = WindowEvent()
    Until Event = #PB_Event_CloseWindow
    Thumbs::Stop()
  EndIf
CompilerEndIf
Avatar de l’utilisateur
Kwai chang caine
Messages : 6962
Inscription : sam. 23/sept./2006 18:32
Localisation : Isere

Re: Thumbnails Gadget V6

Message par Kwai chang caine »

Ca fonctionne ici :D
Pas trop de fluidité sur le scrolling vertical :wink:
Merci du partage 8)
ImageLe bonheur est une route...
Pas une destination

PureBasic Forum Officiel - Site PureBasic
Avatar de l’utilisateur
Thyphoon
Messages : 2697
Inscription : mer. 25/août/2004 6:31
Localisation : Eragny
Contact :

Re: Thumbnails Gadget V6

Message par Thyphoon »

j'y travail sur la fluidité Vertical ... pas facile ... :?
Avatar de l’utilisateur
Thyphoon
Messages : 2697
Inscription : mer. 25/août/2004 6:31
Localisation : Eragny
Contact :

Re: Thumbnails Gadget V6

Message par Thyphoon »

Et voilà enfin des améliorations sur la fluidité. Si vous avez l'occasion de tester avec 2 dossiers et si l'un des 2 dossiers a énormément d'images je suis intéressé d'avoir un retour aussi.

Code : Tout sélectionner

; ******************************************************************** 
; Program:           Thumbnails
; Description:       add a Thumbnails to select image 
; Version:           6Alpha4
; Author:            Thyphoon
; Date:              August, 2021
; License:           Free, unrestricted, credit 
;                    appreciated but not required.
; Note:              Please share improvement !
; ******************************************************************** 

CompilerIf #PB_Compiler_Thread=#False
  CompilerError("You must enable Compiler threadsafe")
  End
CompilerEndIf

CompilerIf Not Defined(Core,#PB_Module)
  DeclareModule Core
    Structure FileData
      FilePath.s
      Selected.b
      State.b                 ; 0 No Loaded ; 1 loaded; 2 Displayed
      Image.i
      ;You can Add All You want After
      Map MetaData.s()
    EndStructure
    
  EndDeclareModule
  
  Module Core
  EndModule
CompilerEndIf 

DeclareModule ImgTools
  Structure DefDisplayImage
    X.l
    Y.l
    Width.l
    Height.l
  EndStructure
  
  Enumeration
    #Image_Style_Fit
    #Image_Style_Fill
    #Image_Style_Stretch
  EndEnumeration 
  
  Declare ImageToContainer(*result.DefDisplayImage,Image,ContainerWidth.l,ContainerHeight.l,Style.l=#Image_Style_Fit)
EndDeclareModule

Module ImgTools
  Procedure ImageToContainer(*result.DefDisplayImage,Image,ContainerWidth.l,ContainerHeight.l,Style.l=#Image_Style_Fit)
    
    If IsImage(Image)
      Protected ImgRatio.l
      Protected ContRatio.l
      Protected ContWidth.l,ContHeight.l
      ImgRatio.l = ImageWidth(Image) / ImageHeight(Image)
      ContRatio.l = ContainerWidth /ContainerHeight
      
      Select Style
        Case #Image_Style_Fit
          If ImgRatio<ContRatio
            *result\Width=ImageWidth(Image)*ContainerHeight/ImageHeight(Image)
            *result\Height=ContainerHeight
            *result\X=(ContainerWidth-*result\Width)/2
            *result\Y=0
          Else
            *result\Width=ContainerWidth
            *result\Height=ImageHeight(Image)*ContainerWidth/ImageWidth(Image)
            *result\X=0
            *result\Y=(ContainerHeight-*result\Height)/2
          EndIf
        Case #Image_Style_Fill
          If ImgRatio<ContRatio
            *result\Width=ImageWidth(Image)*ContainerHeight/ImageHeight(Image)
            *result\Height=ContainerHeight
            *result\X=(ContainerWidth-*result\Width)/2
            *result\Y=0
          Else
            *result\Width=ContainerWidth
            *result\Height=ImageHeight(Image)*ContainerWidth/ImageWidth(Image)
            *result\X=0
            *result\Y=(ContainerHeight-*result\Height)/2
          EndIf
          
        Case #Image_Style_Stretch
          *result\X=0
          *result\Y=0
          *result\Width=ContainerWidth
          *result\Height=ContainerHeight
      EndSelect
      
    EndIf            
  EndProcedure
EndModule

;-Cache Module

DeclareModule Cache
  EnableExplicit
  
  Prototype.i CallBackLoadMedia(*Ptr.Core::FileData)
  
  Structure Param
    CallBackLoadMedia.CallBackLoadMedia
    ;LoadList  
    LoadListSemaphore.i
    LoadListMutex.i
    List LoadList.i()
    ;CacheList
    CacheListMutex.i
    Map CacheList.Core::FileData()
    BackGroundThread.i
    ;Signal New Image Loaded
    SignalMutex.i
    Signal.b
    ;Message
    QuitMutex.i
    Quit.b
  EndStructure
  
  Global Param.Param
  Param\LoadListSemaphore=CreateSemaphore(16)
  Param\LoadListMutex=CreateMutex()
  Param\CacheListMutex=CreateMutex()
  param\SignalMutex=CreateMutex()
  Param\QuitMutex=CreateMutex()
  
  Declare SetCallBackLoadMedia(CallBackLoadMedia.i)
  Declare AddFileToLoadList(FilePath.s)  
  Declare CacheClean()  
  Declare AutoLoadStart() 
  Declare.i GetFileDataFromCache(FilePath.s,Image.i=0)
  Declare.b GetSignalAndReset()
  Declare Quit()
EndDeclareModule

Module Cache
  
  Procedure SetCallBackLoadMedia(CallBackLoadMedia.i)
    Param\CallBackLoadMedia=CallBackLoadMedia
  EndProcedure
  
  Procedure AddFileToLoadList(FilePath.s)
    Protected *Ptr
    LockMutex(param\CacheListMutex)
    *Ptr=AddMapElement(param\CacheList(),FilePath)
    param\CacheList()\FilePath=FilePath
    param\CacheList()\State=0
    UnlockMutex(param\CacheListMutex)
    
    LockMutex(Param\LoadListMutex)
    AddElement(param\LoadList())
    param\LoadList()=*Ptr
    UnlockMutex(Param\LoadListMutex)
  EndProcedure
  
  Procedure LoadCacheDataThread(*Ptr.core::FileData)
    If *Ptr\Image=0 And FileSize(*Ptr\FilePath)>0
      Debug "Cache Load:"+*Ptr\FilePath
      ;Param\CallBackLoadMedia=0 ;To Force to use Internal Loader
      If Param\CallBackLoadMedia<>0 ; <- Use extern procedure to Load Image
        Param\CallBackLoadMedia(*Ptr)
      Else
        LockMutex(Param\CacheListMutex)           ; <- Or intern with PB Plugin
        *Ptr\Image=LoadImage(#PB_Any,*Ptr\FilePath)
        UnlockMutex(Param\CacheListMutex)
      EndIf  
      
      ;Resize Image to Thumnails MaxSize
      If IsImage(*Ptr\Image)
        Protected result.ImgTools::DefDisplayImage
        ImgTools::ImageToContainer(@result,*Ptr\Image,256,256,ImgTools::#Image_Style_Fit)
        ResizeImage(*Ptr\Image,result\Width,result\Height,#PB_Image_Smooth) 
        *Ptr\State=1; Ready to Display Image
                    ;We Have a new Image i Signal it           
        LockMutex (Param\SignalMutex)
        Param\Signal=#True
        UnlockMutex (Param\SignalMutex)
        ;If can't load image  
      Else
        ;MessageRequester("Thumbnails Error","ERROR THREAD LOAD IMAGE"+Chr(13)+FilePath+Chr(13)+" in "+#PB_Compiler_Procedure+"()",#PB_MessageRequester_Ok | #PB_MessageRequester_Error)
        Debug "ERROR THREAD LOAD IMAGE"+Chr(13)+*Ptr\FilePath
        *Ptr\Image=CreateImage(#PB_Any,320,200)
        StartDrawing(ImageOutput(*Ptr\Image))
        Box(0,0,320,200,RGB(0,255,0))
        StopDrawing()
        *Ptr\State=1
      EndIf
    Else
      Debug "Cache Load Error:"+*Ptr\FilePath
    EndIf   
    SignalSemaphore(Param\LoadListSemaphore)
  EndProcedure
  
  Procedure BackgroundThread(n.l)
    Protected Quit.b
    Repeat 
      Repeat 
        LockMutex(Param\LoadListMutex)
        ;Select Data from Id
        If FirstElement(Param\LoadList())<>0
          Protected *Ptr.core::FileData
          *Ptr=Param\LoadList()
          WaitSemaphore(Param\LoadListSemaphore)
          If CreateThread(@LoadCacheDataThread(),*Ptr)<>0
            
            DeleteElement(Param\LoadList())
          Else
            Debug "######################Can't Start Thread"
            End
          EndIf   
          
        EndIf 
        UnlockMutex(Param\LoadListMutex)
        ;Else
        ;  Debug "Wait Semaphore"
        ;  Delay(200)
        ; EndIf 
        
        ; Debug "LOADLIST SIZE:"+Str(ListSize(Param\LoadList()))
        LockMutex(Param\QuitMutex)
        Quit=Param\Quit
        UnlockMutex(Param\QuitMutex)
        
      Until ListSize(Param\LoadList())=0 Or Quit=#True
      Delay(5)
    Until Quit=#True
    Debug "Bye Bye ! Cache::BackgroundThread()"
  EndProcedure
  
  Procedure Quit()
    Debug "QUIT CACHE"
    If IsThread(Param\BackGroundThread)
      LockMutex(Param\QuitMutex)
      Param\Quit=#True
      UnlockMutex(Param\QuitMutex)
      Debug "Cache::Quit Wait Param\BackGroundThread"
      WaitThread(Param\BackGroundThread)
      Debug "Cache::Quit Finish"
    EndIf 
  EndProcedure
  
  Procedure AutoLoadStart()
    If IsThread(Param\BackGroundThread)=#False
      Param\BackGroundThread=CreateThread(@BackgroundThread(),0)
    EndIf 
  EndProcedure
  
  Procedure Free(*Ptr.core::FileData)
    LockMutex(Param\CacheListMutex)
    If IsImage(*Ptr\Image):FreeImage(*Ptr\Image):EndIf
    FreeMap(*Ptr\MetaData())
    UnlockMutex(Param\CacheListMutex)
  EndProcedure
  
  
  ;TODO remake it
  Procedure CacheClean()
    Protected *Ptr.core::FileData
    LockMutex(Param\CacheListMutex)
    ForEach Param\CacheList() 
      If MapSize(Param\CacheList())<500
        Break;
      Else
        *Ptr=Param\CacheList()
        If *Ptr\State=1 And *Ptr\Selected=#False
          Debug "Free Cache :"+GetFilePart(*Ptr\FilePath)+" State:"+Str(*Ptr\State)
          Free(*Ptr)
          DeleteMapElement(Param\CacheList())
        EndIf 
      EndIf 
    Next
    UnlockMutex(Param\CacheListMutex)
  EndProcedure
  
  Procedure.i GetFileDataFromCache(FilePath.s,Image.i=0)
    LockMutex(Param\CacheListMutex)
    Protected *Ptr.core::FileData
    *Ptr=FindMapElement(Param\CacheList(),FilePath)
    UnlockMutex(Param\CacheListMutex)
    If *Ptr=0
      ;AddToLoadList
      LockMutex(Param\CacheListMutex)
      *Ptr=AddMapElement(Param\CacheList(),FilePath)
      *Ptr\FilePath=FilePath
      
      If Image=0
        *Ptr\State=0
        *Ptr\Image=0
      Else ;If We have Image (Ex when use Blob in DB)
        *Ptr\State=1
        *Ptr\Image=Image
      EndIf
      UnlockMutex(Param\CacheListMutex)
      LockMutex(Param\LoadListMutex)
      AddElement(param\LoadList())
      Param\LoadList()=*Ptr
      UnlockMutex(Param\LoadListMutex)
      AutoLoadStart()
    EndIf 
    ProcedureReturn *Ptr
    
  EndProcedure
  
  Procedure.b GetSignalAndReset()
    Protected Signal.b
    LockMutex (Param\SignalMutex)
    Signal=Param\Signal
    Param\Signal=#False
    UnlockMutex (Param\SignalMutex)
    ProcedureReturn Signal
  EndProcedure
  
EndModule



;-Thumbs Module

DeclareModule Thumbs
  EnableExplicit  
  
  Prototype CallBackLoadFromIndex(GadgetId.i,Index.i,Lenght.l)
  Prototype.i CallBackDoubleClick(GadgetId.i,CallBackLoadFromIndex.i)
  Prototype.i CallBackSimpleClick(GadgetId.i,FileName.s,selected.b)
  Structure gdt
    
    BufferMutex.i
    BufferImageA.i
    BufferImageB.i
    
    Gadget.i                ;Canvas Gadget number
    Size.l                  ;Thumb Size Width and Height 
    Index.i                 ;ThumbIndex
    NumberOfElementsOnBufferImage.l ; Number Of Elements send by the CallBackLoadFromIndex
    NbH.l                           ;Number of horizontal thumbnails
    NbV.l                           ;Number of Vertical thumbnails
    
    StartScroll.b           ; #True or #False
    CursorStartY.l          ; Cursor Y coord when clic
    CursorPosY.l
    CursorDeltaY.l          ; Cursor Delta Y from StartY
    ThumbsDeltaY.l          ; Scroll Thumbs
    
    CallBackLoadFromIndex.CallBackLoadFromIndex
    CallBackDoubleClick.CallBackDoubleClick
    CallBackSimpleClick.CallBackSimpleClick
    
    FileListMutex.i
    List FileList.core::FileData()
    
    MutexThumb.i
    List ThumbPointer.i()
    ;MutexRedraw.i
    
    
    DrawBufferImage.b
    GenerateBufferImage.b
    LoadFromIndex.b
    
    GenerateBufferImageNotFinish.b
    
  EndStructure 
  
  ;Declare SetBusyIcon(Image.i)
  Declare SetCallBackLoadFromIndex(GadgetId.i,CallBackLoadFromIndex.i)
  Declare SetCallBackSimpleClick(GadgetId.i,CallBackLoadFromIndex.i)
  Declare SetCallBackDoubleClick(GadgetId.i,CallBackLoadFromIndex.i)
  Declare GenerateBufferImage(*Gdt.Gdt)
  Declare  Refresh(time.l)
  Declare MyEvent()
  Declare ThumbnailsGadget(GadgetId.i,X.l,Y.l,Width.l,Height.l,Size.l)
  Declare Stop()
  Declare AddImageToThumb(GadgetId.i,*Ptr)
  Declare AddToDisplay(GadgetId.i,Index.i,FullPath.s,Image.i=0)
  Declare SetNumberOfElements(GadgetId.i,Number.l)
  
  ;Declare Navigate
  Declare NavigateGadget(GadgetId.i,X.l,Y.l,Width.l,Height.l,ThumbnailsId.i)
  Declare DrawNavigate(Gadget.i)
  Declare CheckEventNav(Event.i)
  ;-Declare List
  Declare CallBackLoadFromInternalListIndex(GadgetId.i,Index.i,Lenght.l)
  Declare AddFileToList(GadgetId.i,FilePath.s,nImage.i=0)
  Declare ClearTheList(GadgetId.i)
  Declare.i GetTheListSize(GadgetId.i)
  
EndDeclareModule

Module Thumbs
  
  Structure GdtNav     
    ThumbnailsGadget.i  
    Gadget.i                ;Canvas Gadget number
  EndStructure
  
  Structure param
    RefreshThread.i
    
    GdtMutex.i
    Map Gdt.Gdt()
    
    Map GdtNav.GdtNav()
    
    Quit.b        ;#True = Quit else #False
    
  EndStructure
  
  Global param.param
  param\GdtMutex=CreateMutex()
  
  LoadFont(0, "Impact", 20, #PB_Font_Bold)
  
  Procedure SetCallBackLoadFromIndex(GadgetId.i,CallBackLoadFromIndex.i)
    If IsGadget(GadgetId) And GadgetType(GadgetId)=#PB_GadgetType_Canvas
      LockMutex(param\GdtMutex)
      If FindMapElement(Param\Gdt(), Str(GadgetId))
        Param\Gdt()\CallBackLoadFromIndex=CallBackLoadFromIndex
        param\Gdt()\LoadFromIndex=#True
      EndIf
      UnlockMutex(param\GdtMutex)
    Else
      Debug "Gadget "+Str(GadgetId)+" Not Initialized Or Wrong Type Thumbs::SetCallBackLoadFromIndex()"
    EndIf 
  EndProcedure
  
  Procedure SetCallBackDoubleClick(GadgetId.i,CallBackDoubleClick.i)
    LockMutex(param\GdtMutex)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      Param\Gdt()\CallBackDoubleClick=CallBackDoubleClick
    EndIf
    UnlockMutex(param\GdtMutex)
  EndProcedure
  
  Procedure SetCallBackSimpleClick(GadgetId.i,CallBackSimpleClick.i)
    LockMutex(param\GdtMutex)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      Param\Gdt()\CallBackSimpleClick=CallBackSimpleClick
    EndIf
    UnlockMutex(param\GdtMutex)
  EndProcedure
  
  Procedure ThumbnailsGadget(GadgetId.i,X.l,Y.l,Width.l,Height.l,Size.l)
    Protected Gdt.i
    Gdt=CanvasGadget(GadgetId.i,X.l,Y.l,Width.l,Height.l,#PB_Canvas_Keyboard|#PB_Canvas_DrawFocus)
    If GadgetId=#PB_Any
      GadgetId=Gdt
    EndIf
    LockMutex(param\GdtMutex)
    AddMapElement(Param\Gdt(),Str(GadgetId))
    Param\Gdt()\BufferMutex=CreateMutex()
    Param\Gdt()\FileListMutex=CreateMutex()
    Param\Gdt()\Gadget=GadgetId
    Param\Gdt()\Size=128
    param\Gdt()\BufferMutex=CreateMutex()
    param\Gdt()\MutexThumb=CreateMutex()
    param\Gdt()\LoadFromIndex=#True
    param\Gdt()\GenerateBufferImage=#True
    param\Gdt()\DrawBufferImage=#True
    BindGadgetEvent(GadgetId,@MyEvent(),#PB_All)
    If IsThread(Param\RefreshThread)=#False
    Param\RefreshThread=CreateThread(@Refresh(),10)
    EndIf 
    UnlockMutex(param\GdtMutex)
  EndProcedure
  
  Procedure AddImageToThumb(GadgetId.i,*Ptr)
    LockMutex(param\GdtMutex)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      LockMutex(param\Gdt()\MutexThumb)
      AddElement(param\Gdt()\ThumbPointer())
      param\Gdt()\ThumbPointer()=*Ptr
      UnlockMutex(param\Gdt()\MutexThumb)
    EndIf
    UnlockMutex(param\GdtMutex)
  EndProcedure
  
  Procedure DrawBufferImage(*Gdt.Gdt)
    LockMutex(Param\GdtMutex)
    If *Gdt<>0
      ;DPI Aware
      Protected.l _GadgetWidth=DesktopScaledX(GadgetWidth(*Gdt\Gadget))
      Protected.l _GadgetHeight=DesktopScaledY(GadgetHeight(*Gdt\Gadget))
      Protected.l _Size=DesktopScaledX(*Gdt\Size)
      Protected.l _ScrollWidth=DesktopScaledX(25)
      Protected.l _ScrollHeight=_ScrollWidth*2
      
      If IsImage(*Gdt\BufferImageA)
        If  StartVectorDrawing(CanvasVectorOutput(*Gdt\Gadget))
          MovePathCursor(0,*Gdt\ThumbsDeltaY-_Size)
          LockMutex(*Gdt\BufferMutex)
          DrawVectorImage(ImageID(*Gdt\BufferImageA))
          UnlockMutex(*Gdt\BufferMutex)
          AddPathBox(ImageWidth(*Gdt\BufferImageA),0,_ScrollWidth,_GadgetHeight):VectorSourceColor(RGBA(100, 100, 100, 255)):FillPath()
          AddPathBox(ImageWidth(*Gdt\BufferImageA),_GadgetHeight/2-_ScrollHeight-*Gdt\CursorDeltaY,_ScrollWidth,_ScrollHeight):VectorSourceColor(RGBA(200, 200, 200, 255)):FillPath()
          StopVectorDrawing()
          *Gdt\DrawBufferImage=#False
        EndIf
      Else
        Debug "can't draw BufferImage"
      EndIf 
    EndIf
    UnlockMutex(Param\GdtMutex)
  EndProcedure
  
  Procedure LoadFromIndex(*Gdt.Gdt)
    ;-Load new File on Index
    LockMutex(Param\GdtMutex)
    Protected.l _GadgetWidth=DesktopScaledX(GadgetWidth(*Gdt\Gadget))
    Protected.l _GadgetHeight=DesktopScaledY(GadgetHeight(*Gdt\Gadget))
    Protected.l _Size=DesktopScaledX(*Gdt\Size)
    Protected.l _ScrollWidth=DesktopScaledX(25)
    Protected GdtWidth.l=_GadgetWidth-_ScrollWidth
    Protected NbH.l=Int(GdtWidth/_Size):*Gdt\NbH=NbH
    Protected NbV.l=Int(_GadgetHeight/_Size):*Gdt\NbV=NbV
    
    Protected *Ptr.core::FileData
    If *Gdt\CallBackLoadFromIndex>0
      If *Gdt\LoadFromIndex=#True
        LockMutex(*Gdt\MutexThumb)
        ForEach *Gdt\ThumbPointer()
          *Ptr=*Gdt\ThumbPointer()
          If *Ptr>0
            If *Ptr\State=2:*Ptr\State=1:EndIf
          EndIf
          DeleteElement(*Gdt\ThumbPointer())
        Next
        ;Debug "ListSize:"+Str(ListSize(*Gdt\ThumbPointer()))
        ;ClearList(Param\Gdt()\ThumbPointer()) ; ça plante donc au dessus j'efface au fur et a mesure avec DeleteElement(Param\Gdt()\ThumbPointer())
        UnlockMutex(*Gdt\MutexThumb)
        *Gdt\CallBackLoadFromIndex(*Gdt\Gadget,*Gdt\Index-Nbh,(NbV+2)*Nbh)
       
        *Gdt\LoadFromIndex=#False
        GenerateBufferImage(*Gdt)
        Debug "Index:"+Str(*Gdt\Index)
        Debug "Nbh:"+Str(Nbh)
        Debug "Nbv:"+Str(NbV)
        Debug "param\CallBackLoadFromIndex("+Str(*Gdt\Index-Nbh)+","+Str((NbV+2)*Nbh)+")"
      EndIf 
    Else 
      Delay(10)
      ;Debug "No Set CallBackLoadFromIndex"
    EndIf 
    Cache::AutoLoadStart() 
    UnlockMutex(Param\GdtMutex)
  EndProcedure
  
  Procedure GenerateBufferImage(*Gdt.Gdt)
    LockMutex(Param\GdtMutex)
    Debug"Generate"+Str(ElapsedMilliseconds())
    If *Gdt
      
      Protected.l _GadgetWidth=DesktopScaledX(GadgetWidth(*Gdt\Gadget))
      Protected.l _GadgetHeight=DesktopScaledY(GadgetHeight(*Gdt\Gadget))
      Protected.l _Size=DesktopScaledX(*Gdt\Size)
      Protected.l _ScrollWidth=DesktopScaledX(25)
      Protected GdtWidth.l=_GadgetWidth-_ScrollWidth
      Protected NbH.l=Int(GdtWidth/_Size):*Gdt\NbH=NbH
      Protected NbV.l=Int(_GadgetHeight/_Size):*Gdt\NbV=NbV
      Protected MarginH.l=(GdtWidth-NbH*_Size)/(NbH+1)
      Protected MarginV.l=(_GadgetHeight-NbV*_Size)/(NbV+1)
      
      ;First Draw create the Image
      LockMutex(*Gdt\BufferMutex)
      If IsImage(*Gdt\BufferImageA)=#False
        *Gdt\BufferImageA=CreateImage(#PB_Any,_GadgetWidth-_ScrollWidth,_GadgetHeight+2*_Size)
        *Gdt\BufferImageB=CreateImage(#PB_Any,_GadgetWidth-_ScrollWidth,_GadgetHeight+2*_Size)
        *Gdt\LoadFromIndex=#True
      EndIf 
      UnlockMutex(*Gdt\BufferMutex)
      
      Protected *Ptr.core::FileData
      If IsImage(*Gdt\BufferImageB) And StartVectorDrawing(ImageVectorOutput(*Gdt\BufferImageB))
        VectorSourceColor(RGBA(128, 128, 128, 255))
        FillVectorOutput()
        *Gdt\GenerateBufferImageNotFinish=#False 
        Protected.l nx,ny,x,y,i
        For ny=-1 To NbV+1
          For nx=0 To NbH-1
            ;Position
            x=nx*_Size+MarginH*nx+(MarginH)
            y=ny*_Size+MarginV*ny+(MarginV)
            i=nx+ny*NbH
            
            ;If in Limit
            If i>-1 And i<ListSize(*Gdt\ThumbPointer())
              Protected State.b
              Protected Image.i
              Protected FileName.s
              Protected Selected.b
              LockMutex(*Gdt\MutexThumb)
              *Ptr=0
              If SelectElement(*Gdt\ThumbPointer(),i)
                *Ptr=*Gdt\ThumbPointer()
                Selected=0
                State=0
                Image=-1
                FileName="No File"
                If *Ptr>0
                  State=*Ptr\State
                  Image.i=*Ptr\Image
                  Selected=*Ptr\Selected
                  FileName.s=GetFilePart(*Ptr\FilePath)
                EndIf
              EndIf
              UnlockMutex(*Gdt\MutexThumb)
              
              If *Ptr>0
                If State>0 And IsImage(Image) ;If Image loaded 
                  Protected result.ImgTools::DefDisplayImage
                  ImgTools::ImageToContainer(@result,Image,_Size,_Size,ImgTools::#Image_Style_Fit)
                  ;If element selected display green
                  Protected _Border.l,_BorderX2.l
                  ;Draw Green Border when selected
                  If Selected=1
                    AddPathBox(result\X+x, result\Y+y,result\Width,result\Height)
                    VectorSourceColor(RGBA(0, 255, 0, 255))
                    FillPath()
                    _Border=DesktopScaledX(2)
                    _BorderX2=_Border*2
                  Else
                    _Border=DesktopScaledX(0)
                    _BorderX2=0
                  EndIf
                  ;Draw Image
                  MovePathCursor(result\X+x+_Border,result\Y+y+_Border)
                  DrawVectorImage(ImageID(Image),255,result\Width-_BorderX2,result\Height-_BorderX2)
                  
                  LockMutex(*Gdt\MutexThumb)
                  *Ptr\State=2
                  UnlockMutex(*Gdt\MutexThumb)
                Else
                  AddPathBox(x, y, _Size,_Size)
                  VectorSourceColor(RGBA(0, 0, 0, 255))
                  FillPath()
                  
                  VectorFont(FontID(0), 30)
                  VectorSourceColor(RGBA(0, 125, 0, 255))
                  MovePathCursor(result\X+x+_Border,result\Y+y+_Border+10)
                  DrawVectorText("Wait....")
                  ;It will be necessary to redo the image it is missing
                  *Gdt\GenerateBufferImageNotFinish=#True
                EndIf
                VectorFont(FontID(0), 40)
                MovePathCursor(x+(_Size-VectorTextWidth(FileName))/2,y+result\Height-VectorTextHeight(FileName))
                VectorSourceColor(RGBA(255, 255, 255, 255))
                ;TODO Found a beter way to draw FileName
                ;DrawVectorText(Str(*Ptr\Selected)+FileName)
              EndIf 
              
            Else ;overLimit
              AddPathBox(x, y, _Size,_Size)
              VectorSourceColor(RGBA(255, 0, 0, 50))
              FillPath()
            EndIf
            
          Next
        Next
        StopVectorDrawing()
        LockMutex(*Gdt\BufferMutex)
        Swap *Gdt\BufferImageA,*Gdt\BufferImageB
        UnlockMutex(*Gdt\BufferMutex)
        DrawBufferImage(*Gdt)
        ;-Cache Cleaner
        
        Cache::CacheClean()
        *Gdt\GenerateBufferImage=#False
        PostEvent(#PB_Event_Gadget, GetActiveWindow(), *Gdt\Gadget,#PB_EventType_Focus)
      Else
        Debug "Error GenerateBufferImage"
      EndIf
      
      
      
    EndIf 
    UnlockMutex(param\GdtMutex)
  EndProcedure
  
  Procedure MyEvent()
    ;Debug "EventGadget="+Str(EventGadget())+"    "+Str(ElapsedMilliseconds())
    Protected *Gdt.Gdt
    ;Protected.l DeltaIndex
    LockMutex(Param\GdtMutex)
    *Gdt=FindMapElement(Param\Gdt(), Str(EventGadget()))
    If *Gdt<>0
      ;DPI Aware
      Protected.l _GadgetWidth=DesktopScaledX(GadgetWidth(*Gdt\Gadget))
      Protected.l _GadgetHeight=DesktopScaledY(GadgetHeight(*Gdt\Gadget))
      Protected.l _Size=DesktopScaledX(*Gdt\Size)
      Protected.l _ScrollWidth=DesktopScaledX(25)
      Protected.l _ScrollHeight=_ScrollWidth*2
      
      ;Test if last Index Page is the End
      Protected EndIndex.b=#False
      LockMutex(*Gdt\MutexThumb)
      Protected ni.l=ListSize(*Gdt\ThumbPointer())-*Gdt\NbH
      If ni>0 And SelectElement(*Gdt\ThumbPointer(),ni)
        If Param\Gdt()\ThumbPointer()=-1
          EndIndex=#True
        EndIf
      EndIf
      UnlockMutex(*Gdt\MutexThumb)
      
      ;-ShortCut
      If EventType()=#PB_EventType_KeyDown
        Select GetGadgetAttribute(*Gdt\Gadget,#PB_Canvas_Key)
          Case #PB_Shortcut_Down  
            If EndIndex=#False
              *Gdt\ThumbsDeltaY=*Gdt\ThumbsDeltaY-DesktopScaledY(5)
              *Gdt\DrawBufferImage=#True
            EndIf
          Case #PB_Shortcut_Up 
            If *Gdt\Index>0
              *Gdt\ThumbsDeltaY=*Gdt\ThumbsDeltaY+DesktopScaledY(5)
              *Gdt\DrawBufferImage=#True
            EndIf 
          Case #PB_Shortcut_PageDown
            If EndIndex=#False
              *Gdt\ThumbsDeltaY=0
              *Gdt\Index=*Gdt\Index+*Gdt\NbH
              *Gdt\DrawBufferImage=#False
              *Gdt\LoadFromIndex=#True 
            EndIf 
          Case #PB_Shortcut_PageUp
            If *Gdt\Index>0
              *Gdt\ThumbsDeltaY=0
              *Gdt\Index=*Gdt\Index-*Gdt\NbH
              *Gdt\DrawBufferImage=#False
              *Gdt\LoadFromIndex=#True 
            EndIf
        EndSelect
      EndIf
      ;-Scroll
      ;If cursor is on Scroll or Scroll is started
      If GetGadgetAttribute(*Gdt\Gadget, #PB_Canvas_MouseX)>_GadgetWidth-_ScrollWidth Or *Gdt\CursorStartY>0
        SetGadgetAttribute(*Gdt\Gadget,#PB_Canvas_Cursor,#PB_Cursor_UpDown)
        ;Start Scroll
        If EventType()=#PB_EventType_LeftButtonDown
          If *Gdt\StartScroll=#False
            *Gdt\CursorStartY=GetGadgetAttribute(*Gdt\Gadget, #PB_Canvas_MouseY) 
            *Gdt\CursorPosY=*Gdt\CursorStartY
            *Gdt\StartScroll=#True
            Debug "Start Scroll"
          EndIf 
        EndIf 
      
       ;Scroll is Enable
         If *Gdt\StartScroll=#True
           If GetGadgetAttribute(*Gdt\Gadget, #PB_Canvas_MouseY)<>0 ;PB Bug ? sometime return 0
             *Gdt\CursorPosY=GetGadgetAttribute(*Gdt\Gadget, #PB_Canvas_MouseY)
           EndIf
         *Gdt\CursorDeltaY=*Gdt\CursorStartY-*Gdt\CursorPosY
         *Gdt\ThumbsDeltaY=*Gdt\ThumbsDeltaY+(*Gdt\CursorDeltaY/10)
         *Gdt\DrawBufferImage=#True
       EndIf 
;       
      ;Stop Scroll
      If *Gdt\StartScroll=#True And EventType() = #PB_EventType_LeftButtonUp
        *Gdt\CursorStartY=0
        *Gdt\StartScroll=#False
        *Gdt\CursorDeltaY=0
         Debug "Stop Scroll"
      EndIf
      
    Else
        ;-Select Image
        SetGadgetAttribute(*Gdt\Gadget,#PB_Canvas_Cursor,#PB_Cursor_Default)
    
        If EventType()=#PB_EventType_LeftClick  
          Protected *Ptr.core::FileData
          Protected GdtWidth.l=_GadgetWidth-_ScrollWidth
          Protected NbH.l=Int(GdtWidth/_Size):*Gdt\NbH=NbH
          Protected NbV.l=Int(_GadgetHeight/_Size):*Gdt\NbV=NbV
          Protected MarginH.l=(GdtWidth-NbH* _Size)/(NbH+1)
          Protected MarginV.l=(_GadgetHeight-NbV* _Size)/(NbV+1)
          
          Protected x.l=GetGadgetAttribute(*Gdt\Gadget, #PB_Canvas_MouseX) 
          Protected y.l=GetGadgetAttribute(*Gdt\Gadget, #PB_Canvas_MouseY) 
          
          Protected nx.l=(x-MarginH)/(_Size+MarginH)
          Protected ny.l=(y-MarginV-*Gdt\ThumbsDeltaY)/(_Size+MarginV)
          Protected index.l=nx+ny*NbH+NbH
          Debug "INDEX Selected:"+Str(index)
          Debug "ReadIndex Selecter:"+Str(index+*Gdt\Gadget)
          
          LockMutex(*Gdt\MutexThumb)
          ni=ListSize(*Gdt\ThumbPointer())
          If index>-1 And index<ni
            If SelectElement(*Gdt\ThumbPointer(),index)
              *Ptr=*Gdt\ThumbPointer()
               If *Ptr>0
                  *Ptr\Selected=1-*Ptr\Selected
                  *Gdt\GenerateBufferImage=#True ; You must to redraw image to draw green selected 
                    ; Debug Param\CallBackSimpleClick
                    ; If Param\CallBackSimpleClick<>0 ; check if a @Procedure is added
                    ; Param\CallBackSimpleClick(*Ptr\FilePath,*Ptr\Selected)
                    ; EndIf 
               EndIf
            EndIf 
          EndIf 
          UnlockMutex(*Gdt\MutexThumb)
        EndIf
        
      EndIf 
      
;       If *Gdt\LoadFromIndex=#True
;         CreateThread(@LoadFromIndex(),*Gdt)
;         ;GenerateBufferImage 
;       ElseIf (*Gdt\GenerateBufferImage=#True Or (*Gdt\GenerateBufferImageNotFinish=#True And Cache::GetSignalAndReset()=#True)) And *Gdt\LoadFromIndex=#False
;         CreateThread(@GenerateBufferImage(),*Gdt)
;        ElseIf *Gdt\DrawBufferImage=#True 
;         DrawBufferImage(*Gdt)
;       EndIf
     EndIf
    UnlockMutex(Param\GdtMutex)
  EndProcedure
    
  
  Procedure Refresh(time.l)
    Protected *Gdt.Gdt
    Protected.l DeltaIndex
    Repeat
      LockMutex(param\GdtMutex)
      ForEach Param\Gdt()
        *Gdt=@Param\Gdt()
        
        Protected.l _Size=DesktopScaledX(*Gdt\Size)
        
              ;Test if last Index Page is the End
      Protected EndIndex.b=#False
      LockMutex(*Gdt\MutexThumb)
      Protected ni.l=ListSize(*Gdt\ThumbPointer())-*Gdt\NbH
      If ni>0 And SelectElement(*Gdt\ThumbPointer(),ni)
        If Param\Gdt()\ThumbPointer()=-1
          EndIndex=#True
        EndIf
      EndIf
      UnlockMutex(*Gdt\MutexThumb)
        
        
        ;Scroll is Enable
        If *Gdt\StartScroll=#True
        *Gdt\CursorDeltaY=*Gdt\CursorStartY-*Gdt\CursorPosY
        *Gdt\ThumbsDeltaY=*Gdt\ThumbsDeltaY+(*Gdt\CursorDeltaY/10)
        *Gdt\DrawBufferImage=#True
      EndIf 
      
      
         If *Gdt\ThumbsDeltaY>_Size
        DeltaIndex=Int(*Gdt\ThumbsDeltaY/_Size)* *Gdt\NbH
        *Gdt\Index=*Gdt\Index-DeltaIndex
        *Gdt\ThumbsDeltaY=*Gdt\ThumbsDeltaY%_Size
        *Gdt\DrawBufferImage=#False
        *Gdt\LoadFromIndex=#True
      EndIf
      
      If *Gdt\ThumbsDeltaY<-_Size 
        DeltaIndex=Abs(Int(*Gdt\ThumbsDeltaY/_Size)* *Gdt\NbH)
        *Gdt\Index=*Gdt\Index+DeltaIndex
        *Gdt\ThumbsDeltaY=*Gdt\ThumbsDeltaY%_Size
        *Gdt\DrawBufferImage=#False
        *Gdt\LoadFromIndex=#True
      EndIf
      
            ;-Limit Up Scroll
      If *Gdt\Index<=0 And *Gdt\ThumbsDeltaY>0
        *Gdt\Index=0
        *Gdt\ThumbsDeltaY=0
        Debug "Limit Up Scroll"
      EndIf 
      
      ;-Limit Down Scroll
      If *Gdt\CursorDeltaY<0 And EndIndex=#True;And *Gdt\NumberOfElementsOnBufferImage<(*Gdt\NbH* (*Gdt\NbV-1))
        Debug "Limit Down Scroll "
        *Gdt\ThumbsDeltaY=0
      EndIf
      
      ;-Rendering Gadget
      
       If *Gdt\LoadFromIndex=#True
        CreateThread(@LoadFromIndex(),*Gdt)
        ;GenerateBufferImage 
      ElseIf (*Gdt\GenerateBufferImage=#True Or (*Gdt\GenerateBufferImageNotFinish=#True And Cache::GetSignalAndReset()=#True)) And *Gdt\LoadFromIndex=#False
        CreateThread(@GenerateBufferImage(),*Gdt)
       ElseIf *Gdt\DrawBufferImage=#True 
        DrawBufferImage(*Gdt)
      EndIf
     
      Next
      UnlockMutex(param\GdtMutex)
      
      Delay(25)
    Until param\Quit=#True
    Debug "Refresh Thread Say Bye Bye !"
  EndProcedure
  
  Procedure FreeGdt(GadgetId.i)
    LockMutex(param\GdtMutex)
    UnbindGadgetEvent(GadgetId,@MyEvent(),#PB_All)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      If IsThread(Param\RefreshThread)=#True
        param\Gdt()\LoadFromIndex=#False
        param\Gdt()\GenerateBufferImage=#False
        param\Gdt()\DrawBufferImage=#False
        
        ;KillThread(Param\RefreshThread) ; <- Old Method
        Debug "Thumbs::Stop() Wait Thread Param\RefreshThread"
        WaitThread(Param\RefreshThread)
        Debug "Thumbs::Stop() Stop to Wait"
      EndIf
      
    EndIf
    UnlockMutex(param\GdtMutex)
  EndProcedure
  
  Procedure Stop()
    ;You must stop all Thread Before 
    Debug "Thumbs::Stop() Wait Thread Param\RefreshThread"
    If IsThread(Param\RefreshThread)=#True
      param\Quit=#True
    EndIf 
    WaitThread(Param\RefreshThread)
    
    LockMutex(param\GdtMutex)
    ForEach Param\Gdt()
      UnbindGadgetEvent(Val(MapKey(Param\Gdt())),@MyEvent(),#PB_All)
      
      DeleteMapElement(Param\Gdt())
    Next
    UnlockMutex(param\GdtMutex)
    
    Debug "Thumbs::Stop() Stop to Wait"  
    Cache::Quit()   
    
  EndProcedure
  
  Procedure AddToDisplay(GadgetId.i,Index.i,FullPath.s,Image.i=0)
    Protected *Ptr.Core::FileData
    *Ptr=Cache::GetFileDataFromCache(FullPath,Image)
    LockMutex(param\GdtMutex)
    Protected *Gdt.Gdt=FindMapElement(Param\Gdt(), Str(GadgetId))
    UnlockMutex(param\GdtMutex)
    
    If *Gdt
      LockMutex(*Gdt\MutexThumb)
      If Index>=0 And FullPath<>""
        If *Ptr
          Thumbs::AddImageToThumb(GadgetId,*Ptr)
        Else
          Thumbs::AddImageToThumb(GadgetId,0)
        EndIf 
      Else
        Thumbs::AddImageToThumb(GadgetId,-1)
      EndIf 
      UnlockMutex(*Gdt\MutexThumb)
    EndIf
  EndProcedure
  
  Procedure SetNumberOfElements(GadgetId.i,Number.l)
    LockMutex(param\GdtMutex)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      Param\Gdt()\NumberOfElementsOnBufferImage=Number
    EndIf
    UnlockMutex(param\GdtMutex)
  EndProcedure
  
  Procedure CallBackLoadFromInternalListIndex(GadgetId.i,Index.i,Lenght.l)
    Protected n.l
    Protected TmpIndex.i
    LockMutex(Param\GdtMutex)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      
      Debug "CallBackLoadFromIndex("+Str(Index)+","+Str(Lenght)+")"
      For n=0 To Lenght-1
        TmpIndex=Index+n
        If TmpIndex>=0 And TmpIndex<ListSize(Param\Gdt()\FileList())
          SelectElement(Param\Gdt()\FileList(),TmpIndex)
          Protected *Ptr.Core::FileData
          *Ptr=Cache::GetFileDataFromCache(Param\Gdt()\FileList()\FilePath)
          If *Ptr
            Thumbs::AddImageToThumb(GadgetId,*Ptr)
          Else
            Thumbs::AddImageToThumb(GadgetId,0)
          EndIf 
        Else
          Thumbs::AddImageToThumb(GadgetId,-1)
        EndIf 
      Next
    EndIf
    UnlockMutex(Param\GdtMutex)
  EndProcedure
  
  
  ;-Navigate
  Procedure NavigateGadget(GadgetId.i,X.l,Y.l,Width.l,Height.l,ThumbnailsId.i)
    Protected Gdt.i
    Gdt=CanvasGadget(GadgetId.i,X.l,Y.l,Width.l,Height.l)
    If GadgetId=#PB_Any
      GadgetId=Gdt
    EndIf
    If AddMapElement(Param\GdtNav(),Str(GadgetId),#PB_Map_ElementCheck)
      Param\GdtNav()\Gadget=GadgetId
      Param\GdtNav()\ThumbnailsGadget=ThumbnailsId
    EndIf 
  EndProcedure
  
  Procedure DrawNavigate(Gadget.i)
    ;     Protected FileName.s
    ;     StartVectorDrawing(CanvasVectorOutput(Gadget))
    ;     VectorSourceColor(GetWindowColor(0))
    ;     FillVectorOutput()
    ;     Protected h.l=GadgetWidth(Gadget)
    ;     If FindMapElement(Param\Navigate(),Str(Gadget))
    ;       If FindMapElement(Param\Thumbnails(),Str(Param\Navigate()\ThumbnailsGadget))
    ;         h=GadgetHeight(Param\Navigate()\Gadget)
    ;         index=Param\Thumbnails()\IndexNav
    ;         nb=(Int(GadgetWidth(Param\Navigate()\Gadget)/h)-2)/2
    ;         
    ;         
    ;         For z=1 To nb
    ;           ;Right
    ;           
    ;           LockMutex(Param\FileListMutex)
    ;           
    ;           index_Right.i=index+z
    ;           FileName_Right.s=""
    ;           image_left.i=-1
    ;           If index_Right>=0 And index_Right<ListSize(Param\FileList())
    ;              If SelectElement(Param\FileList(),index_Right)>0
    ;               FileName_Right.s=Param\FileList()\FileName
    ;             EndIf 
    ;           EndIf
    ;           
    ;           ;Left
    ;           index_Left.i=index-z
    ;           FileName_Left.s=""
    ;           image_left.i=-1
    ;           If index_Left>=0 And index_Left<ListSize(Param\FileList())
    ;             
    ;             If SelectElement(Param\FileList(),index_Left)>0
    ;               FileName_Left.s=Param\FileList()\FileName
    ;             EndIf
    ;           EndIf 
    ;           UnlockMutex(Param\FileListMutex)
    ;           
    ;           
    ;           If FileName_Right<>""
    ;             image_right.i=GetImageByName(FileName_Right)
    ;             x=(GadgetWidth(Param\Navigate()\Gadget)/2-h/2)+z*h
    ;             y=0
    ;             AddPathBox(x,y,h,h)
    ;             SaveVectorState()
    ;             ClipPath()
    ;             DrawNavThumb(image_right,x,y,255-(z*200/nb));
    ;             RestoreVectorState()
    ;           EndIf
    ;           
    ;           If FileName_Left<>""
    ;             image_left.i=GetImageByName(FileName_Left.s)
    ;             x=(GadgetWidth(Param\Navigate()\Gadget)/2-GadgetHeight(Param\Navigate()\Gadget)/2)-z*GadgetHeight(Param\Navigate()\Gadget)
    ;             y=0
    ;             AddPathBox(x,y,GadgetHeight(Param\Navigate()\Gadget),GadgetHeight(Param\Navigate()\Gadget))
    ;             SaveVectorState()
    ;             ClipPath()
    ;             DrawNavThumb(image_left,x,y,255-(z*200/nb));-(z*200/nb)
    ;             RestoreVectorState()
    ;           EndIf 
    ;           
    ;           
    ;         Next
    ;         
    ;         If index>=0 And index<ListSize(Param\FileList())
    ;           FileName.s=""
    ;           If SelectElement(Param\FileList(),index)>0
    ;             FileName.s=Param\FileList()\FileName
    ;           EndIf
    ;         EndIf   
    ;         
    ;         
    ;         If FileName<>""
    ;           image=GetImageByName(FileName.s)
    ;           x=GadgetWidth(Param\Navigate()\Gadget)/2-GadgetHeight(Param\Navigate()\Gadget)/2
    ;           y=0
    ;           AddPathBox(x,y,GadgetHeight(Param\Navigate()\Gadget),GadgetHeight(Param\Navigate()\Gadget))
    ;           SaveVectorState()
    ;           ClipPath()
    ;           DrawNavThumb(image,x,y,255)
    ;           RestoreVectorState()
    ;           AddPathBox(x,y,GadgetHeight(Param\Navigate()\Gadget),GadgetHeight(Param\Navigate()\Gadget))
    ;           VectorSourceColor(RGBA(255, 0, 0, 255))
    ;           StrokePath(2)
    ;         EndIf 
    ;         
    ;         
    ;       EndIf 
    ;     EndIf 
    ;     StopVectorDrawing()
  EndProcedure
  
  Procedure CheckEventNav(Event.i)
    ;     If Event=#PB_Event_Timer
    ;       ForEach Param\Navigate()
    ;         If Param\Refresh=#True 
    ;           DrawNavigate(Param\Navigate()\Gadget)
    ;         EndIf 
    ;       Next
    ;       If Param\Refresh=#False
    ;       EndIf 
    ;     EndIf 
    ;     
    ;     ForEach Param\Navigate()
    ;       
    ;       If Event = #PB_Event_Gadget And EventGadget() = Param\Navigate()\Gadget
    ;         
    ;         If EventType()=#PB_EventType_Focus
    ;           Debug "Focus"
    ;         EndIf 
    ;         
    ;         If EventType()=#PB_EventType_LostFocus
    ;           Debug "lost Focus"
    ;         EndIf 
    ;         
    ;         If EventType()=#PB_EventType_LeftButtonDown
    ;           If GetGadgetAttribute(Param\Navigate()\Gadget, #PB_Canvas_MouseX)>GadgetWidth(Param\Navigate()\Gadget)/2
    ;             param\Thumbnails(Str(Param\Navigate()\ThumbnailsGadget))\IndexNav+1
    ;           Else
    ;              param\Thumbnails(Str(Param\Navigate()\ThumbnailsGadget))\IndexNav-1
    ;            EndIf
    ;            
    ;           LockMutex(Param\FileListMutex)
    ;           If param\Thumbnails()\IndexNav<0:param\Thumbnails()\IndexNav=0:EndIf
    ;           If param\Thumbnails()\IndexNav>ListSize(Param\FileList())-1:param\Thumbnails()\IndexNav=ListSize(Param\FileList())-1:EndIf
    ;           
    ;           index=param\Thumbnails()\IndexNav
    ;           
    ;           If index>-1 And SelectElement(Param\FileList(),index)<>0 
    ;             If Param\Thumbnails()\CallBackDoubleClick<>0 ; check if a @Procedure is added
    ;               Param\Thumbnails()\CallBackDoubleClick(Param\FileList()\FileName)
    ;             EndIf
    ;           EndIf 
    ;           UnlockMutex(Param\FileListMutex)
    ;         EndIf
    ;       EndIf 
    ;     Next       
  EndProcedure
  
  
  ;-List
  
  Procedure AddFileToList(GadgetId.i,FilePath.s,nImage.i=0)
    LockMutex(param\GdtMutex)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      ;Select Data from Id
      AddElement(Param\Gdt()\FileList())
      Param\Gdt()\FileList()\FilePath=FilePath
    EndIf 
    UnlockMutex(param\GdtMutex)
  EndProcedure   
  
  Procedure ClearTheList(GadgetId.i)
    LockMutex(param\GdtMutex)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      ClearList(Param\Gdt()\FileList())
    EndIf
    UnlockMutex(param\GdtMutex)
  EndProcedure
  
  Procedure.i GetTheListSize(GadgetId.i)
    LockMutex(param\GdtMutex)
    If FindMapElement(Param\Gdt(), Str(GadgetId))
      Protected n.i
      
      LockMutex(Param\Gdt()\FileListMutex)
      n=ListSize(Param\Gdt()\FileList())
      UnlockMutex(Param\Gdt()\FileListMutex)
    EndIf
    UnlockMutex(param\GdtMutex)
    ProcedureReturn n
  EndProcedure
  
  
EndModule  

CompilerIf #PB_Compiler_IsMainFile 
  UseJPEGImageDecoder()
  UsePNGImageDecoder()
  UseMD5Fingerprint()
  Enumeration
    #Win_main
    #Gdt_Nav
    #Gdt_ThumbA
    #Gdt_ThumbB
  EndEnumeration
  
  Procedure CallBackDoubleClick(FileName.s)
    Debug FileName
  EndProcedure
  
  Global NewList CurrentList.s()
  Global NewList CurrentListB.s()
  
  Procedure CallBackLoadFromIndex(GadgetId.i,Index.i,Lenght.l)
    Protected n.l
    Protected TmpIndex.i
    Protected relativeIndex.l
    Protected FilePath.s
    Debug "CallBackLoadFromIndex("+Str(Index)+","+Str(Lenght)+")"
    For n=1 To Lenght
      TmpIndex=Index+n-1
      If TmpIndex>=0 And TmpIndex<ListSize(CurrentList())
        SelectElement(CurrentList(),TmpIndex)
        FilePath.s=CurrentList()
      Else
        FilePath.s=""
      EndIf 
      Thumbs::AddToDisplay(GadgetId,TmpIndex,FilePath) ;<- You must Add
      relativeIndex=relativeIndex+1
    Next
    Thumbs::SetNumberOfElements(GadgetId,relativeIndex) ;<- You Must Add This or Scroll Down is Locked. This value is to detect End of Scroll
  EndProcedure
  
  Procedure CallBackLoadFromIndexB(GadgetId.i,Index.i,Lenght.l)
    Protected n.l
    Protected TmpIndex.i
    Protected relativeIndex.l
    Debug "CallBackLoadFromIndexB("+Str(Index)+","+Str(Lenght)+")"
    For n=1 To Lenght
      TmpIndex=Index+n-1
      If TmpIndex>=0 And TmpIndex<ListSize(CurrentListB())
        SelectElement(CurrentListB(),TmpIndex)
        relativeIndex=relativeIndex+1
        Protected *Ptr.Core::FileData
        *Ptr=Cache::GetFileDataFromCache(CurrentListB())
        If *Ptr
          Thumbs::AddImageToThumb(GadgetId,*Ptr)
        Else
          Thumbs::AddImageToThumb(GadgetId,0)
        EndIf 
      Else
        Thumbs::AddImageToThumb(GadgetId,-1)
      EndIf 
    Next
    Thumbs::SetNumberOfElements(GadgetId,relativeIndex)
  EndProcedure
  
  If OpenWindow(#Win_main, 0, 0, 1024, 600, "Thumbnails", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    LoadFont(0, "Arial", 20, #PB_Font_Bold)
    Thumbs::ThumbnailsGadget(#Gdt_ThumbA,0,50,512,550,128)
    Thumbs::ThumbnailsGadget(#Gdt_ThumbB,512,50,512,550,128)
    Define Repertoire$
    Define Event.i
    Repertoire$=GetUserDirectory(#PB_Directory_Pictures)
    Repertoire$=PathRequester("Chose Directory", Repertoire$)
    If ExamineDirectory(0, Repertoire$, "*.jpg")  
      While NextDirectoryEntry(0)
        If DirectoryEntryType(0) = #PB_DirectoryEntry_File
          AddElement(CurrentList())
          CurrentList()=Repertoire$+DirectoryEntryName(0)
        EndIf
      Wend
      FinishDirectory(0)
    EndIf
    
    Repertoire$=GetUserDirectory(#PB_Directory_Pictures)
    Repertoire$=PathRequester("Chose Directory", Repertoire$)
    If ExamineDirectory(0, Repertoire$, "*.jpg")  
      While NextDirectoryEntry(0)
        If DirectoryEntryType(0) = #PB_DirectoryEntry_File
          AddElement(CurrentListB())
          CurrentListB()=Repertoire$+DirectoryEntryName(0)
        EndIf
      Wend
      FinishDirectory(0)
    EndIf 
    
    Thumbs::SetCallBackLoadFromIndex(#Gdt_ThumbA,@CallBackLoadFromIndex())
    Thumbs::SetCallBackLoadFromIndex(#Gdt_ThumbB,@CallBackLoadFromIndexB())
    
    Repeat
      Delay(1)
      Event = WaitWindowEvent()
    Until Event = #PB_Event_CloseWindow
    Thumbs::Stop()
  EndIf
CompilerEndIf

Avatar de l’utilisateur
Kwai chang caine
Messages : 6962
Inscription : sam. 23/sept./2006 18:32
Localisation : Isere

Re: Thumbnails Gadget V6

Message par Kwai chang caine »

Beaucoup mieux !!!! 8O et j'ai testé sur un dossier de 1579 images 8)
Tout en sachant que mon PC n'est vraiment pas ce que l'on peut appeler un "foudre de guerre" :oops:

Par contre, je sais pas si c'est l'effet désiré, mais la scrollbar agit plutôt comme un bouton qu'une scrollbar ordinaire
Elle n'est pas synchronisée au défilement, si l'on descend un peu et que l'on arrête en tenant appuyé, la colonne continue à descendre toute seule
Maintenant j'ai déjà vu ce genre de comportement peu usuel, et ça le fait aussi, c'est juste une autre habitude à prendre :wink:
Sinon ...gare aux allers/retours quand on a loupé la gare parce que le train a été trop vite :lol:

En tout cas, merci encore 8)
ImageLe bonheur est une route...
Pas une destination

PureBasic Forum Officiel - Site PureBasic
Avatar de l’utilisateur
Thyphoon
Messages : 2697
Inscription : mer. 25/août/2004 6:31
Localisation : Eragny
Contact :

Re: Thumbnails Gadget V6

Message par Thyphoon »

Salut KCC

Je suis en train de tout réécrire car je ne suis pas satisfait du résultat.

voici le véritable comportement que dois avoir le curseur de scrolling.
ça permet de scroller dans des contenus énorme. Mon but est de scroller dans 65 000 Photos ....
Pour rien ne te cacher je travaille sur un remplacement de Picasa Et Thumbnails Gadget est une toute petite partie du projet

Code : Tout sélectionner

CompilerIf #PB_Compiler_Thread=#False
  CompilerError("You must enable Compiler threadsafe")
  End
CompilerEndIf

Structure Gdt
  GadgetId.i                ;Canvas Gadget number
  Size.l                    ;Thumb Size Width and Height 
  Index.i                   ;ThumbIndex
  NbH.l                     ;Number of horizontal thumbnails
  NbV.l                     ;Number of Vertical thumbnails
  
  ;Scroll
  StartScroll.b             ;#True if click or #False
  CursorStartY.l
  CursorDeltaY.l
  ThumbsDeltaY.l
  
  ;DPI Aware Value
  _GadgetWidth.l
  _GadgetHeight.l
  _Size.l
  _ScrollWidth.l
  _ScrollHeight.l
  _ThumbsWidth.l
  _ThumbsHeight.l
  _MarginH.l
  _MarginV.l
  
  Quit.b
  ThreadDrawCanvasImage.i
EndStructure

Structure param
  Map Gdt.Gdt()
EndStructure

Global param.param

Procedure InitGadgetValue(GadgetId.i)
  Protected *Gdt.Gdt
  *Gdt=GetGadgetData(GadgetId)
  *Gdt\_GadgetWidth=DesktopScaledX(GadgetWidth(GadgetId))
  *Gdt\_GadgetHeight=DesktopScaledY(GadgetHeight(GadgetId))
  *Gdt\_Size=DesktopScaledX(*Gdt\Size)
  *Gdt\_ScrollWidth=DesktopScaledX(25)
  *Gdt\_ScrollHeight=*Gdt\_ScrollWidth*2
  *Gdt\_ThumbsWidth.l=*Gdt\_GadgetWidth-*Gdt\_ScrollWidth
  *Gdt\_ThumbsHeight.l=*Gdt\_GadgetHeight
  *Gdt\NbH.l=Int(*Gdt\_ThumbsWidth/*Gdt\_Size)
  *Gdt\NbV.l=Int(*Gdt\_GadgetHeight/*Gdt\_Size)
  *Gdt\_MarginH.l=(*Gdt\_ThumbsWidth-*Gdt\NbH**Gdt\_Size)/(*Gdt\NbH+1)
  *Gdt\_MarginV.l=(*Gdt\_GadgetHeight-*Gdt\NbV**Gdt\_Size)/(*Gdt\NbV+1)
EndProcedure

Procedure DrawCanvasImage(GadgetId.i)
  Protected *Gdt.gdt
  Protected CursorY.l
  *Gdt=GetGadgetData(GadgetId)
  
  Repeat 
    
    If *Gdt\StartScroll=#True
     
      *Gdt\ThumbsDeltaY=*Gdt\ThumbsDeltaY+(*Gdt\CursorDeltaY/10)
    EndIf 
    
     
    CursorY=*Gdt\_GadgetHeight/2-*Gdt\_ScrollHeight-*Gdt\CursorDeltaY
    ;Limit Cursor Up
    If CursorY<0
      CursorY=0
      *Gdt\ThumbsDeltaY=*Gdt\_Size ;<-Fast Mode
    EndIf
    
    ;Limit Cursor Down
    If CursorY>*Gdt\_GadgetHeight-*Gdt\_ScrollHeight
      CursorY=*Gdt\_GadgetHeight-*Gdt\_ScrollHeight
      *Gdt\ThumbsDeltaY=-*Gdt\_Size  ;<-Fast Mode
    EndIf
    
    Protected DeltaIndex.l
    If *Gdt\ThumbsDeltaY>_Size
      DeltaIndex=Int(*Gdt\ThumbsDeltaY/*Gdt\_Size)* *Gdt\NbH
      *Gdt\Index=*Gdt\Index-DeltaIndex
      *Gdt\ThumbsDeltaY=*Gdt\ThumbsDeltaY%*Gdt\_Size
    EndIf
    
    If *Gdt\ThumbsDeltaY<-_Size 
      DeltaIndex=Abs(Int(*Gdt\ThumbsDeltaY/*Gdt\_Size)* *Gdt\NbH)
      *Gdt\Index=*Gdt\Index+DeltaIndex
      *Gdt\ThumbsDeltaY=*Gdt\ThumbsDeltaY%*Gdt\_Size
    EndIf
    
    
    ;Limit Scroll
    If *Gdt\Index<=0
      *Gdt\Index=0
      If *Gdt\ThumbsDeltaY>0:*Gdt\ThumbsDeltaY=0:EndIf 
    EndIf
    
    If *Gdt\Index>=100-*Gdt\NbH**Gdt\NbV
      *Gdt\Index=100-*Gdt\NbH**Gdt\NbV
      If *Gdt\ThumbsDeltaY<0:*Gdt\ThumbsDeltaY=0:EndIf 
    EndIf
    
    
    If StartVectorDrawing(CanvasVectorOutput(*Gdt\GadgetId))
      VectorSourceColor(RGBA(128, 128, 128, 255))
      FillVectorOutput()
      For ny=-1 To *Gdt\NbV+1
        For nx=0 To *Gdt\NbH-1
          ;Position
          x=nx * *Gdt\_Size+ *Gdt\_MarginH * nx + ( *Gdt\_MarginH )
          y=ny * *Gdt\_Size+ *Gdt\_MarginV * ny + ( *Gdt\_MarginV ) + *Gdt\ThumbsDeltaY
          i=nx+ny* *Gdt\NbH + *Gdt\Index
          AddPathBox(x, y,*Gdt\_Size,*Gdt\_Size)
          VectorSourceColor(RGBA(100, 100, 100, 255))
          FillPath()
          
          VectorSourceColor(RGBA(255, 255, 255, 255))
          MovePathCursor(x+5,y+5)
          DrawVectorText(Str(i))
        Next
      Next
      ;ScrollBar
      AddPathBox(*Gdt\_ThumbsWidth,0,*Gdt\_ScrollWidth,*Gdt\_GadgetHeight):VectorSourceColor(RGBA(100, 100, 100, 255)):FillPath()
      AddPathBox(*Gdt\_ThumbsWidth,CursorY,*Gdt\_ScrollWidth,*Gdt\_ScrollHeight):VectorSourceColor(RGBA(200, 200, 200, 255)):FillPath()
      StopVectorDrawing()
    EndIf 
    Delay(25)
  Until *Gdt\Quit=#True
  Debug "DrawCanvasImage "+Str(*Gdt\GadgetId)+" Say Bye Bye !"
EndProcedure

Procedure ThumbsEvent()
  Protected *Gdt.gdt
  *Gdt=GetGadgetData(EventGadget())
  Protected.l Mx,My
  Mx=GetGadgetAttribute(*Gdt\GadgetId, #PB_Canvas_MouseX)
  My=GetGadgetAttribute(*Gdt\GadgetId, #PB_Canvas_MouseY)
  ;scroll Bar Event
  If Mx>*Gdt\_ThumbsWidth
    SetGadgetAttribute(*Gdt\GadgetId,#PB_Canvas_Cursor,#PB_Cursor_UpDown)
    If EventType()=#PB_EventType_LeftButtonDown
      If *Gdt\StartScroll=#False
        *Gdt\CursorStartY=My 
        *Gdt\StartScroll=#True
        Debug "Start Scroll"
      EndIf 
    EndIf 
    
    If *Gdt\StartScroll=#True
      If GetGadgetAttribute(*Gdt\GadgetId, #PB_Canvas_MouseY)<>0 ;PB Bug ? sometime return 0
        *Gdt\CursorDeltaY=*Gdt\CursorStartY-GetGadgetAttribute(*Gdt\GadgetId, #PB_Canvas_MouseY)
      EndIf
    EndIf  
    
    ;Stop Scroll
    If *Gdt\StartScroll=#True And EventType() = #PB_EventType_LeftButtonUp
      *Gdt\CursorStartY=0
      *Gdt\StartScroll=#False
      *Gdt\CursorDeltaY=0
      Debug "Stop Scroll"
    EndIf
    
    ;Thumbs  
  Else
    SetGadgetAttribute(*Gdt\GadgetId,#PB_Canvas_Cursor,#PB_Cursor_Default)
    ;Start Scroll
    
  EndIf 
  Select EventType()
  EndSelect
EndProcedure


Procedure ThumbsGadget(GadgetId.i,X.l,Y.l,Width.l,Height.l,Size.l)
  Protected newGadgetId.i
  newGadgetId=CanvasGadget(GadgetId.i,X.l,Y.l,Width.l,Height.l,#PB_Canvas_Keyboard|#PB_Canvas_DrawFocus|#PB_Canvas_Border)
  
  If GadgetId=#PB_Any
    GadgetId=newGadgetId
  EndIf
  Protected *Gdt.Gdt
  *Gdt=AddMapElement(param\Gdt(),Str(GadgetId))
  Debug *Gdt
  If *Gdt
    *Gdt\GadgetId=GadgetId
    *Gdt\Size=Size
    Debug *Gdt\Size
    SetGadgetData(GadgetId, *Gdt)
    InitGadgetValue(GadgetId)
    *Gdt\ThreadDrawCanvasImage=CreateThread(@DrawCanvasImage(),GadgetId)
    BindGadgetEvent(GadgetId,@ThumbsEvent(),#PB_All)
  Else
    Debug "Error to Init ThumbsGadget"
  EndIf
EndProcedure

Procedure FreeThumbsGadget(GadgetId.i)
  Protected *Gdt.gdt
  *Gdt=GetGadgetData(GadgetId)
  *Gdt\Quit=#True
  WaitThread(*Gdt\ThreadDrawCanvasImage)
  DeleteMapElement(param\Gdt(),Str(GadgetId))
EndProcedure

Enumeration
  #Win_main
  #Gdt_Nav
  #Gdt_ThumbA
  #Gdt_ThumbB
EndEnumeration

If OpenWindow(#Win_main, 0, 0, 1024, 600, "Thumbnails", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  LoadFont(0, "Arial", 20, #PB_Font_Bold)
  ThumbsGadget(#Gdt_ThumbA,0,50,512,550,128)
  ThumbsGadget(#Gdt_ThumbB,512,50,512,550,128)
  Repeat
    Delay(1)
    Event = WaitWindowEvent()
  Until Event = #PB_Event_CloseWindow
  FreeThumbsGadget(#Gdt_ThumbA)
  FreeThumbsGadget(#Gdt_ThumbB)
EndIf

Avatar de l’utilisateur
Kwai chang caine
Messages : 6962
Inscription : sam. 23/sept./2006 18:32
Localisation : Isere

Re: Thumbnails Gadget V6

Message par Kwai chang caine »

J'ai compris pour le Scroll :wink:

Un remplacement de PICASA, et ben... 8O
Perso je ne l'ai jamais utilisé :oops: donc je le connais que de nom, et le nom est drôlement connu
Donc je suppose que t'as du taf
Surement quelques nuits blanches à venir :mrgreen:
ImageLe bonheur est une route...
Pas une destination

PureBasic Forum Officiel - Site PureBasic
Avatar de l’utilisateur
Thyphoon
Messages : 2697
Inscription : mer. 25/août/2004 6:31
Localisation : Eragny
Contact :

Re: Thumbnails Gadget V6

Message par Thyphoon »

oui surement ça fait des années que je suis dessus.
le module PBMap a été fait pour ça (pour pouvoir mettre les photos sur une carte)
https://www.purebasic.fr/french/viewtop ... ilit=PBMap
et aussi le module Exiftools pour la gestion des métadonnées
https://www.purebasic.fr/french/viewtop ... ls#p202643
Mais bon je manque de temps, et faut avouer qu'il y a beaucoup d'obstacle et que c'est un gros travail... mais un jour peut être :wink:
Avatar de l’utilisateur
Kwai chang caine
Messages : 6962
Inscription : sam. 23/sept./2006 18:32
Localisation : Isere

Re: Thumbnails Gadget V6

Message par Kwai chang caine »

Merci de ton explication et bon courage :wink:
ImageLe bonheur est une route...
Pas une destination

PureBasic Forum Officiel - Site PureBasic
Avatar de l’utilisateur
Thyphoon
Messages : 2697
Inscription : mer. 25/août/2004 6:31
Localisation : Eragny
Contact :

Re: Thumbnails Gadget V6

Message par Thyphoon »

Kwai chang caine a écrit : mer. 11/août/2021 18:37 Merci de ton explication et bon courage :wink:
Merci !
Nouvelle version je touche au but ! :P
Enfin un affichage fluide, et un comportement comme je le souhaite du scrollbar.

Code : Tout sélectionner

; ******************************************************************** 
; Program:           Thumbnails
; Description:       add a Thumbnails to select image 
; Version:           6Beta
; Author:            Thyphoon
; Date:              August, 2021
; License:           Free, unrestricted, credit 
;                    appreciated but not required.
; Note:              Please share improvement !
; ******************************************************************** 

CompilerIf #PB_Compiler_Thread=#False
  CompilerError("You must enable Compiler threadsafe")
  End
CompilerEndIf

CompilerIf Not Defined(Core,#PB_Module)
  DeclareModule Core
    Structure FileData
      FilePath.s
      Selected.b
      State.b                 ; 0 No Loaded ; 1 loaded; 2 Displayed
      Image.i
      ;You can Add All You want After
      Map MetaData.s()
    EndStructure
    
  EndDeclareModule
  
  Module Core
  EndModule
CompilerEndIf 

DeclareModule ImgTools
  Structure DefDisplayImage
    X.l
    Y.l
    Width.l
    Height.l
  EndStructure
  
  Enumeration
    #Image_Style_Fit
    #Image_Style_Fill
    #Image_Style_Stretch
  EndEnumeration 
  
  Declare ImageToContainer(*result.DefDisplayImage,Image,ContainerWidth.l,ContainerHeight.l,Style.l=#Image_Style_Fit)
EndDeclareModule

Module ImgTools
  Procedure ImageToContainer(*result.DefDisplayImage,Image,ContainerWidth.l,ContainerHeight.l,Style.l=#Image_Style_Fit)
    
    If IsImage(Image)
      Protected ImgRatio.l
      Protected ContRatio.l
      Protected ContWidth.l,ContHeight.l
      ImgRatio.l = ImageWidth(Image) / ImageHeight(Image)
      ContRatio.l = ContainerWidth /ContainerHeight
      
      Select Style
        Case #Image_Style_Fit
          If ImgRatio<ContRatio
            *result\Width=ImageWidth(Image)*ContainerHeight/ImageHeight(Image)
            *result\Height=ContainerHeight
            *result\X=(ContainerWidth-*result\Width)/2
            *result\Y=0
          Else
            *result\Width=ContainerWidth
            *result\Height=ImageHeight(Image)*ContainerWidth/ImageWidth(Image)
            *result\X=0
            *result\Y=(ContainerHeight-*result\Height)/2
          EndIf
        Case #Image_Style_Fill
          If ImgRatio<ContRatio
            *result\Width=ImageWidth(Image)*ContainerHeight/ImageHeight(Image)
            *result\Height=ContainerHeight
            *result\X=(ContainerWidth-*result\Width)/2
            *result\Y=0
          Else
            *result\Width=ContainerWidth
            *result\Height=ImageHeight(Image)*ContainerWidth/ImageWidth(Image)
            *result\X=0
            *result\Y=(ContainerHeight-*result\Height)/2
          EndIf
          
        Case #Image_Style_Stretch
          *result\X=0
          *result\Y=0
          *result\Width=ContainerWidth
          *result\Height=ContainerHeight
      EndSelect
      
    EndIf            
  EndProcedure
EndModule

;-Cache Module

DeclareModule Cache
  EnableExplicit
  
  Prototype.i CallBackLoadMedia(*Ptr.Core::FileData)
  
  Structure Param
    CallBackLoadMedia.CallBackLoadMedia
    ;LoadList  
    LoadListSemaphore.i
    LoadListMutex.i
    List LoadList.i()
    ;CacheList
    CacheListMutex.i
    Map CacheList.Core::FileData()
    BackGroundThread.i
    ;Signal New Image Loaded
    SignalMutex.i
    Signal.b ;Signal When new image is loaded
             ;Message
    QuitMutex.i
    Quit.b
  EndStructure
  
  Global Param.Param
  Param\LoadListSemaphore=CreateSemaphore(16)
  Param\LoadListMutex=CreateMutex()
  Param\CacheListMutex=CreateMutex()
  param\SignalMutex=CreateMutex()
  Param\QuitMutex=CreateMutex()
  
  Declare SetCallBackLoadMedia(CallBackLoadMedia.i)
  Declare AddFileToLoadList(FilePath.s)  
  Declare CacheClean()  
  Declare AutoLoadStart() 
  Declare.i GetFileDataFromCache(FilePath.s,Image.i=0)
  Declare.b GetSignalAndReset()
  Declare Quit()
EndDeclareModule

Module Cache
  
  Procedure SetCallBackLoadMedia(CallBackLoadMedia.i)
    Param\CallBackLoadMedia=CallBackLoadMedia
  EndProcedure
  
  Procedure AddFileToLoadList(FilePath.s)
    Protected *Ptr
    LockMutex(param\CacheListMutex)
    *Ptr=AddMapElement(param\CacheList(),FilePath)
    param\CacheList()\FilePath=FilePath
    param\CacheList()\State=0
    UnlockMutex(param\CacheListMutex)
    
    LockMutex(Param\LoadListMutex)
    AddElement(param\LoadList())
    param\LoadList()=*Ptr
    UnlockMutex(Param\LoadListMutex)
  EndProcedure
  
  Procedure LoadCacheDataThread(*Ptr.core::FileData)
    If *Ptr\Image=0 And FileSize(*Ptr\FilePath)>0
      Debug "Cache Load:"+*Ptr\FilePath
      ;Param\CallBackLoadMedia=0 ;To Force to use Internal Loader
      If Param\CallBackLoadMedia<>0 ; <- Use extern procedure to Load Image
        LockMutex(Param\CacheListMutex) 
        Param\CallBackLoadMedia(*Ptr)
        UnlockMutex(Param\CacheListMutex)
      Else
        LockMutex(Param\CacheListMutex)           ; <- Or intern with PB Plugin
        *Ptr\Image=LoadImage(#PB_Any,*Ptr\FilePath)
        If IsImage(*Ptr\Image)
        Else
          *Ptr\Image=0
        EndIf
        UnlockMutex(Param\CacheListMutex)
      EndIf  
      
      ;Resize Image to Thumnails MaxSize
      If IsImage(*Ptr\Image)
        Protected result.ImgTools::DefDisplayImage
        ImgTools::ImageToContainer(@result,*Ptr\Image,256,256,ImgTools::#Image_Style_Fit)
        ResizeImage(*Ptr\Image,result\Width,result\Height,#PB_Image_Smooth) 
        *Ptr\State=1; Ready to Display Image
                    ;We Have a new Image i Signal it           
        LockMutex (Param\SignalMutex)
        Param\Signal=#True
        UnlockMutex (Param\SignalMutex)
        ;If can't load image  
      Else
        ;MessageRequester("Thumbnails Error","ERROR THREAD LOAD IMAGE"+Chr(13)+FilePath+Chr(13)+" in "+#PB_Compiler_Procedure+"()",#PB_MessageRequester_Ok | #PB_MessageRequester_Error)
        Debug "ERROR THREAD LOAD IMAGE"+Chr(13)+*Ptr\FilePath
        *Ptr\Image=CreateImage(#PB_Any,320,200)
        StartDrawing(ImageOutput(*Ptr\Image))
        Box(0,0,320,200,RGB(0,255,0))
        StopDrawing()
        *Ptr\State=1
      EndIf
    Else
      Debug "Cache Load Error:"+*Ptr\FilePath
    EndIf   
    SignalSemaphore(Param\LoadListSemaphore)
  EndProcedure
  
  Procedure BackgroundThread(n.l)
    Protected Quit.b
    Repeat 
      Repeat 
        LockMutex(Param\LoadListMutex)
        ;Select Data from Id
        If FirstElement(Param\LoadList())<>0
          Protected *Ptr.core::FileData
          *Ptr=Param\LoadList()
          WaitSemaphore(Param\LoadListSemaphore)
          If CreateThread(@LoadCacheDataThread(),*Ptr)<>0
            
            DeleteElement(Param\LoadList())
          Else
            Debug "######################Can't Start Thread"
            End
          EndIf   
          
        EndIf 
        UnlockMutex(Param\LoadListMutex)
        ;Else
        ;  Debug "Wait Semaphore"
        ;  Delay(200)
        ; EndIf 
        
        ; Debug "LOADLIST SIZE:"+Str(ListSize(Param\LoadList()))
        LockMutex(Param\QuitMutex)
        Quit=Param\Quit
        UnlockMutex(Param\QuitMutex)
        
      Until ListSize(Param\LoadList())=0 Or Quit=#True
      Delay(5)
    Until Quit=#True
    Debug "Bye Bye ! Cache::BackgroundThread()"
  EndProcedure
  
  Procedure Quit()
    Debug "QUIT CACHE"
    If IsThread(Param\BackGroundThread)
      LockMutex(Param\QuitMutex)
      Param\Quit=#True
      UnlockMutex(Param\QuitMutex)
      Debug "Cache::Quit Wait Param\BackGroundThread"
      WaitThread(Param\BackGroundThread)
      Debug "Cache::Quit Finish"
    EndIf 
  EndProcedure
  
  Procedure AutoLoadStart()
    If IsThread(Param\BackGroundThread)=#False
      Param\BackGroundThread=CreateThread(@BackgroundThread(),0)
    EndIf 
  EndProcedure
  
  Procedure Free(*Ptr.core::FileData)
    LockMutex(Param\CacheListMutex)
    If IsImage(*Ptr\Image):FreeImage(*Ptr\Image):EndIf
    FreeMap(*Ptr\MetaData())
    UnlockMutex(Param\CacheListMutex)
  EndProcedure
  
  
  ;TODO remake it
  Procedure CacheClean()
    Protected *Ptr.core::FileData
    LockMutex(Param\CacheListMutex)
    ForEach Param\CacheList() 
      If MapSize(Param\CacheList())<500
        Break;
      Else
        *Ptr=Param\CacheList()
        If *Ptr\State=1 And *Ptr\Selected=#False
          Debug "Free Cache :"+GetFilePart(*Ptr\FilePath)+" State:"+Str(*Ptr\State)
          Free(*Ptr)
          DeleteMapElement(Param\CacheList())
        EndIf 
      EndIf 
    Next
    UnlockMutex(Param\CacheListMutex)
  EndProcedure
  
  Procedure.i GetFileDataFromCache(FilePath.s,Image.i=0)
    LockMutex(Param\CacheListMutex)
    Protected *Ptr.core::FileData
    *Ptr=FindMapElement(Param\CacheList(),FilePath)
    UnlockMutex(Param\CacheListMutex)
    If *Ptr=0
      ;AddToLoadList
      LockMutex(Param\CacheListMutex)
      *Ptr=AddMapElement(Param\CacheList(),FilePath)
      *Ptr\FilePath=FilePath
      
      If Image=0
        *Ptr\State=0
        *Ptr\Image=0
      Else ;If We have Image (Ex when use Blob in DB)
        *Ptr\State=1
        *Ptr\Image=Image
      EndIf
      UnlockMutex(Param\CacheListMutex)
      LockMutex(Param\LoadListMutex)
      AddElement(param\LoadList())
      Param\LoadList()=*Ptr
      UnlockMutex(Param\LoadListMutex)
      AutoLoadStart()
    EndIf 
    ProcedureReturn *Ptr
    
  EndProcedure
  
  Procedure.b GetSignalAndReset()
    Protected Signal.b
    LockMutex (Param\SignalMutex)
    Signal=Param\Signal
    Param\Signal=#False
    UnlockMutex (Param\SignalMutex)
    ProcedureReturn Signal
  EndProcedure
  
EndModule
;-Thumbs
DeclareModule Thumbs
  Declare SetCallBackLoadFromIndex(GadgetId.i,CallBackLoadFromIndex.i)
  Declare AddImageToThumb(GadgetId.i,Index.i,*Ptr)
  Declare LimitIndex(GadgetId.i,IndexMax.i=-1)
  Declare  ThumbsGadget(GadgetId.i,X.l,Y.l,Width.l,Height.l,Size.l,CallBack.i=0)
  Declare  FreeThumbsGadget(GadgetId.i)
  Declare ForceUpdate(GadgetId.i)
  
EndDeclareModule
Module Thumbs
  EnableExplicit
  Prototype CallBackLoadFromIndex(GadgetId.i,Index.i,Lenght.l)
  
  Structure Gdt
    GadgetId.i                ;Canvas Gadget number
    Size.l                    ;Thumb Size Width and Height 
    Index.i                   ;ThumbIndex
    IndexMax.i                ; -1 infinity else Maximum index to limit scroll
    NbH.l                     ;Number of horizontal thumbnails
    NbV.l                     ;Number of Vertical thumbnails
    
    ;Scroll
    StartScroll.b             ;#True if click or #False
    CursorStartY.l
    CursorDeltaY.l
    ThumbsDeltaY.l
    ZoneClick.l               ;1 ScrollBar 2;Thumbs
    
    ;DPI Aware Value
    _GadgetWidth.l
    _GadgetHeight.l
    _Size.l
    _ScrollWidth.l
    _ScrollHeight.l
    _ThumbsWidth.l
    _ThumbsHeight.l
    _MarginH.l
    _MarginV.l
    
    ThumbPointerlistMutex.i  
    Map ThumbPointerList.i()
    
    LoadFromIndexInitialized.b     ;#True CallBack is Ok # Else not initialized (See SetCallBackLoadFromindex() )
    CallBackLoadFromIndex.CallBackLoadFromIndex
    
    
    
    Quit.b
    ThreadDrawCanvasImage.i
  EndStructure
  
  Structure param
    Map Gdt.Gdt()
    DrawAlphaImageMutex.i
  EndStructure
  
  Global param.param
  param\DrawAlphaImageMutex=CreateMutex()
  Procedure InitGadgetValue(GadgetId.i)
    Protected *Gdt.Gdt
    *Gdt=GetGadgetData(GadgetId)
    *Gdt\_GadgetWidth=DesktopScaledX(GadgetWidth(GadgetId))
    *Gdt\_GadgetHeight=DesktopScaledY(GadgetHeight(GadgetId))
    *Gdt\_Size=DesktopScaledX(*Gdt\Size)
    *Gdt\_ScrollWidth=DesktopScaledX(25)
    *Gdt\_ScrollHeight=*Gdt\_ScrollWidth*2
    *Gdt\_ThumbsWidth.l=*Gdt\_GadgetWidth-*Gdt\_ScrollWidth
    *Gdt\_ThumbsHeight.l=*Gdt\_GadgetHeight
    *Gdt\NbH.l=Int(*Gdt\_ThumbsWidth/*Gdt\_Size)
    *Gdt\NbV.l=Int(*Gdt\_GadgetHeight/*Gdt\_Size)
    *Gdt\_MarginH.l=(*Gdt\_ThumbsWidth-*Gdt\NbH**Gdt\_Size)/(*Gdt\NbH+1)
    *Gdt\_MarginV.l=(*Gdt\_GadgetHeight-*Gdt\NbV**Gdt\_Size)/(*Gdt\NbV+1)
  EndProcedure
  
  Procedure AddImageToThumb(GadgetId.i,Index.i,*Ptr.core::FileData)
    If *Ptr>0
      Debug "Add "+Str(Index)+" "+GetFilePart(*Ptr\FilePath)
    Else
      Debug "Add "+Str(Index)+" - - - "
    EndIf 
    Protected *Gdt.gdt
    *Gdt=GetGadgetData(GadgetId)
    LockMutex(*Gdt\ThumbPointerlistMutex)
    AddMapElement(*Gdt\ThumbPointerlist(),Str(Index))
    *Gdt\ThumbPointerlist()=*Ptr
    UnlockMutex(*Gdt\ThumbPointerlistMutex)
  EndProcedure
  
  Procedure LoadFromIndex(GadgetId.i)
    Protected *Gdt.gdt
    Protected *Ptr.core::FileData
    *Gdt=GetGadgetData(GadgetId)
    If *Gdt\LoadFromIndexInitialized=#True
      Protected Index.i=*Gdt\Index-*Gdt\Nbh
      Protected NThumbs.l=(*Gdt\NbV+2)**Gdt\Nbh ;Number of Thumbs must be Loaded
                                                ;-Clean index
      LockMutex(*Gdt\ThumbPointerlistMutex)
      ForEach *Gdt\ThumbPointerList()
        *Ptr=*Gdt\ThumbPointerList()
        Protected Value.i=Val(MapKey(*Gdt\ThumbPointerList()))
        If *Ptr>0 And (Value<Index Or Value>Index+Nthumbs-1)
          If *Ptr\State=2:*Ptr\State=1:EndIf ;Image not Display 
          Debug "Clean Map:"+Str(Value)
        EndIf
        DeleteMapElement(*Gdt\ThumbPointerList())
      Next
      UnlockMutex(*Gdt\ThumbPointerlistMutex)
      
      ;-Load new File on Index
      
      If *Gdt\CallBackLoadFromIndex>0
        *Gdt\CallBackLoadFromIndex(*Gdt\GadgetId,Index,NThumbs)
        Debug "param\CallBackLoadFromIndex("+Str(Index)+","+Str(NThumbs)+")"
      Else 
        Delay(10)
        Debug "No Set CallBackLoadFromIndex"
      EndIf 
      Cache::AutoLoadStart() 
    EndIf
  EndProcedure
  
  Procedure LimitIndex(GadgetId.i,IndexMax.i=-1)
    Protected *Gdt.gdt
    If IndexMax>=0
      *Gdt=GetGadgetData(GadgetId)
      Debug "Index:"+Str(IndexMax)
      Debug "*Gdt\NbH="+Str(*Gdt\NbH)
      Debug "*Gdt\NbV="+Str(*Gdt\NbV)
      *Gdt\IndexMax=IndexMax-(*Gdt\NbH*(*Gdt\NbV))
      If *Gdt\IndexMax<0
        *Gdt\IndexMax=0
      EndIf 
      Debug "IndexMax:"+Str(*Gdt\IndexMax)
    ElseIf IndexMax=-1
      *Gdt\IndexMax=-1
    EndIf
  EndProcedure
  
  Procedure DrawCanvasImage(GadgetId.i)
    Protected *Gdt.gdt
    Protected CursorY.l
    *Gdt=GetGadgetData(GadgetId)
    Repeat 
      
      If *Gdt\StartScroll=#True
        *Gdt\ThumbsDeltaY=*Gdt\ThumbsDeltaY+(*Gdt\CursorDeltaY/10)
      EndIf 
      
      CursorY=*Gdt\_GadgetHeight/2-*Gdt\_ScrollHeight-*Gdt\CursorDeltaY
      ;Limit Cursor Up
      If CursorY<0
        CursorY=0
        *Gdt\ThumbsDeltaY=*Gdt\_Size ;<-Fast Mode
      EndIf
      
      ;Limit Cursor Down
      If CursorY>*Gdt\_GadgetHeight-*Gdt\_ScrollHeight
        CursorY=*Gdt\_GadgetHeight-*Gdt\_ScrollHeight
        *Gdt\ThumbsDeltaY=-*Gdt\_Size  ;<-Fast Mode
      EndIf
      
      Protected DeltaIndex.l
      If *Gdt\ThumbsDeltaY>=*Gdt\_Size
        DeltaIndex=Int(*Gdt\ThumbsDeltaY/*Gdt\_Size)* *Gdt\NbH
        *Gdt\Index=*Gdt\Index-DeltaIndex
        *Gdt\ThumbsDeltaY=*Gdt\ThumbsDeltaY%*Gdt\_Size
        CreateThread(@LoadFromIndex(),GadgetId)
      EndIf
      
      If *Gdt\ThumbsDeltaY<=-*Gdt\_Size 
        DeltaIndex=Abs(Int(*Gdt\ThumbsDeltaY/*Gdt\_Size)* *Gdt\NbH)
        *Gdt\Index=*Gdt\Index+DeltaIndex
        *Gdt\ThumbsDeltaY=*Gdt\ThumbsDeltaY%*Gdt\_Size
        CreateThread(@LoadFromIndex(),GadgetId)
      EndIf
      
      ;Limit Scroll
      If *Gdt\Index<=0
        *Gdt\Index=0
        If *Gdt\ThumbsDeltaY>0:*Gdt\ThumbsDeltaY=0:EndIf 
      EndIf
      
      If *Gdt\IndexMax>-1
        If *Gdt\Index>=*Gdt\IndexMax
          *Gdt\Index=*Gdt\IndexMax
          If *Gdt\ThumbsDeltaY<0:*Gdt\ThumbsDeltaY=0:EndIf 
        EndIf
      EndIf 
      
      Protected *Ptr.core::FileData
      Protected State.l
      Protected Image.i
      Protected Selected.b
      Protected FileName.s
      LockMutex(param\DrawAlphaImageMutex)
      If StartVectorDrawing(CanvasVectorOutput(*Gdt\GadgetId))
        VectorSourceColor(RGBA(128, 128, 128, 255))
        FillVectorOutput()
        Protected ListIndex.l=-1
        Protected.l nx,ny,x,y
        Protected i.i
        For ny=-1 To *Gdt\NbV+1
          For nx=0 To *Gdt\NbH-1
            ListIndex=ListIndex+1
            ;Position
            x=nx * *Gdt\_Size+ *Gdt\_MarginH * nx + ( *Gdt\_MarginH )
            y=ny * *Gdt\_Size+ *Gdt\_MarginV * ny + ( *Gdt\_MarginV ) + *Gdt\ThumbsDeltaY
            i=nx+ny* *Gdt\NbH + *Gdt\Index
            
            AddPathBox(x, y,*Gdt\_Size,*Gdt\_Size)
            VectorSourceColor(RGBA(100, 100, 100, 255))
            FillPath()
            Selected=0
            State=0
            Image=-1
            FileName=""
            LockMutex(*Gdt\ThumbPointerlistMutex)
            If FindMapElement(*Gdt\ThumbPointerList(),Str(i))
              *Ptr=*Gdt\ThumbPointerList()
              
            Else
              *Ptr=0
            EndIf
            UnlockMutex(*Gdt\ThumbPointerlistMutex)
            If *Ptr>0
              If *Ptr\State>0 And IsImage(*Ptr\Image) ;If Image loaded 
                *Ptr\State=2                          ; 0 No Loaded ; 1 loaded; 2 Displayed
                State=2 
                Image.i=*Ptr\Image
                Selected=*Ptr\Selected
                FileName.s=GetFilePart(*Ptr\FilePath)
                
                Protected result.ImgTools::DefDisplayImage
                ImgTools::ImageToContainer(@result,Image,*Gdt\_Size,*Gdt\_Size,ImgTools::#Image_Style_Fit)
                ;If element selected display green
                Protected _Border.l,_BorderX2.l
                ;Draw Green Border when selected
                If Selected=1
                  AddPathBox(result\X+x, result\Y+y,result\Width,result\Height)
                  VectorSourceColor(RGBA(0, 255, 0, 255))
                  FillPath()
                  _Border=DesktopScaledX(2)
                  _BorderX2=_Border*2
                Else
                  _Border=0
                  _BorderX2=0
                EndIf
                ;Draw Image
                
                AddPathBox(result\X+x+_Border, result\Y+y+_Border,result\Width-_BorderX2,result\Height-_BorderX2)
                VectorSourceColor(RGBA(0, 0, 0, 255))
                FillPath()
                MovePathCursor(result\X+x+_Border,result\Y+y+_Border)
                DrawVectorImage(ImageID(Image),255,result\Width-_BorderX2,result\Height-_BorderX2)
              Else
                AddPathBox(result\X+x, result\Y+y,result\Width,result\Height)
                VectorSourceColor(RGBA(255, 255, 0, 255))
                FillPath()
              EndIf
              ;If *Ptr=0
            Else
              AddPathBox(result\X+x, result\Y+y,result\Width,result\Height)
              VectorSourceColor(RGBA(0, 255, 255, 128))
              FillPath()  
            EndIf
            VectorSourceColor(RGBA(255, 255, 255, 255))
            MovePathCursor(x+5,y+5)
            DrawVectorText(Str(i)+" "+Filename)
          Next
        Next
        ;ScrollBar
        AddPathBox(*Gdt\_ThumbsWidth,0,*Gdt\_ScrollWidth,*Gdt\_GadgetHeight):VectorSourceColor(RGBA(100, 100, 100, 255)):FillPath()
        AddPathBox(*Gdt\_ThumbsWidth,CursorY,*Gdt\_ScrollWidth,*Gdt\_ScrollHeight):VectorSourceColor(RGBA(200, 200, 200, 255)):FillPath()
        StopVectorDrawing()
        UnlockMutex(param\DrawAlphaImageMutex)
      EndIf 
      Delay(50)
    Until *Gdt\Quit=#True
    Debug "DrawCanvasImage "+Str(*Gdt\GadgetId)+" Say Bye Bye !"
  EndProcedure
  
  Procedure ThumbsEvent()
    Protected *Gdt.gdt
    *Gdt=GetGadgetData(EventGadget())
    Protected.l Mx,My
    Mx=GetGadgetAttribute(*Gdt\GadgetId, #PB_Canvas_MouseX)
    My=GetGadgetAttribute(*Gdt\GadgetId, #PB_Canvas_MouseY)
    If Mx=0
      Mx=GetGadgetAttribute(*Gdt\GadgetId, #PB_Canvas_MouseX)
    EndIf 
    If My=0
      My=GetGadgetAttribute(*Gdt\GadgetId, #PB_Canvas_MouseY)
    EndIf
    
    Select EventType()
      Case #PB_EventType_KeyDown
        Select GetGadgetAttribute(*Gdt\GadgetId,#PB_Canvas_Key)
          Case #PB_Shortcut_Down  
            If *Gdt\Index<*Gdt\IndexMax
              *Gdt\ThumbsDeltaY=*Gdt\ThumbsDeltaY-DesktopScaledY(5)
            EndIf
          Case #PB_Shortcut_Up 
            If *Gdt\Index>0
              *Gdt\ThumbsDeltaY=*Gdt\ThumbsDeltaY+DesktopScaledY(5)
            EndIf 
          Case #PB_Shortcut_PageDown
            If *Gdt\Index<*Gdt\IndexMax
              *Gdt\ThumbsDeltaY=0
              *Gdt\Index=*Gdt\Index+*Gdt\NbH
              CreateThread(@LoadFromIndex(),*Gdt\GadgetId)
            EndIf 
          Case #PB_Shortcut_PageUp
            If *Gdt\Index>0
              *Gdt\ThumbsDeltaY=0
              *Gdt\Index=*Gdt\Index-*Gdt\NbH
              CreateThread(@LoadFromIndex(),*Gdt\GadgetId)
            EndIf
        EndSelect
        
      Case #PB_EventType_LostFocus
        *Gdt\StartScroll=#False
        *Gdt\CursorDeltaY=0
      Case #PB_EventType_MouseMove
        ;Icon dans la zone
        If Mx>*Gdt\_ThumbsWidth
          SetGadgetAttribute(*Gdt\GadgetId,#PB_Canvas_Cursor,#PB_Cursor_UpDown)
        Else
          SetGadgetAttribute(*Gdt\GadgetId,#PB_Canvas_Cursor,#PB_Cursor_Default)
        EndIf
        
        If *Gdt\StartScroll=#True
          If GetGadgetAttribute(*Gdt\GadgetId, #PB_Canvas_MouseY)<>0 ;PB Bug ? sometime return 0
            *Gdt\CursorDeltaY=*Gdt\CursorStartY-GetGadgetAttribute(*Gdt\GadgetId, #PB_Canvas_MouseY)
          EndIf
        EndIf  
        
      Case #PB_EventType_LeftButtonDown 
        ;scroll Bar Event
        If Mx>*Gdt\_ThumbsWidth
          *Gdt\ZoneClick=1 ; You click in Scroll Zone
          
          If *Gdt\StartScroll=#False
            *Gdt\CursorStartY=My 
            *Gdt\StartScroll=#True
            Debug "Start Scroll"
          EndIf 
        Else
          *Gdt\ZoneClick=2 ; You Click in Thumbs Zone
        EndIf 
        
      Case #PB_EventType_LeftButtonUp
        ;Stop Scroll
        If *Gdt\StartScroll=#True
          *Gdt\CursorStartY=0
          *Gdt\StartScroll=#False
          *Gdt\CursorDeltaY=0
          Debug "Stop Scroll"
        EndIf
        
        ;Thumbs  
      Case #PB_EventType_LeftClick 
        ;Select Image
        If *Gdt\ZoneClick=2 And *Gdt\StartScroll=#False
          Protected *Ptr.core::FileData
          Protected nx.l=(mx-*Gdt\_MarginH)/(*Gdt\_Size+*Gdt\_MarginH)
          Protected ny.l=(my-*Gdt\_MarginV-*Gdt\ThumbsDeltaY)/(*Gdt\_Size+*Gdt\_MarginV)
          Protected index.l=nx+ny**Gdt\NbH
          index=*Gdt\Index+index
          LockMutex(*Gdt\ThumbPointerlistMutex)
          FindMapElement(*Gdt\ThumbPointerList(),Str(index))
          *Ptr=*Gdt\ThumbPointerList()
          *Ptr\Selected=1-*Ptr\Selected
          UnlockMutex(*Gdt\ThumbPointerlistMutex)
        EndIf 
    EndSelect 
    
  EndProcedure
  
  Procedure SetCallBackLoadFromIndex(GadgetId.i,CallBackLoadFromIndex.i)
    If IsGadget(GadgetId) And GadgetType(GadgetId)=#PB_GadgetType_Canvas
      Protected *Gdt.gdt
      *Gdt=GetGadgetData(GadgetId)
      *Gdt\CallBackLoadFromIndex=CallBackLoadFromIndex
      *Gdt\LoadFromIndexInitialized=#True
    Else
      Debug "Gadget "+Str(GadgetId)+" Not Initialized Or Wrong Type Thumbs::SetCallBackLoadFromIndex()"
    EndIf 
  EndProcedure
  
  Procedure ThumbsGadget(GadgetId.i,X.l,Y.l,Width.l,Height.l,Size.l,CallBack.i=0)
    Protected newGadgetId.i
    newGadgetId=CanvasGadget(GadgetId.i,X.l,Y.l,Width.l,Height.l,#PB_Canvas_Keyboard|#PB_Canvas_DrawFocus|#PB_Canvas_Border)
    
    If GadgetId=#PB_Any
      GadgetId=newGadgetId
    EndIf
    Protected *Gdt.Gdt
    *Gdt=AddMapElement(param\Gdt(),Str(GadgetId))
    Debug *Gdt
    If *Gdt
      *Gdt\ThumbPointerlistMutex=CreateMutex()
      *Gdt\GadgetId=GadgetId
      *Gdt\Size=Size
      Debug *Gdt\Size
      SetGadgetData(GadgetId, *Gdt)
      InitGadgetValue(GadgetId)
      If CallBack
        SetCallBackLoadFromIndex(GadgetId,CallBack)
        CreateThread(@LoadFromIndex(),GadgetId)
      EndIf
      ;DrawCanvasImage(GadgetId)
      *Gdt\ThreadDrawCanvasImage=CreateThread(@DrawCanvasImage(),GadgetId)
      BindGadgetEvent(GadgetId,@ThumbsEvent(),#PB_All)
    Else
      Debug "Error to Init ThumbsGadget"
    EndIf
  EndProcedure
  
  Procedure FreeThumbsGadget(GadgetId.i)
    Protected *Gdt.gdt
    If IsGadget(GadgetID)
      *Gdt=GetGadgetData(GadgetId)
      *Gdt\Quit=#True
      WaitThread(*Gdt\ThreadDrawCanvasImage)
      FreeMutex(*Gdt\ThumbPointerListMutex)
      FreeMap(*Gdt\ThumbPointerList())
      DeleteMapElement(param\Gdt(),Str(GadgetId))
    EndIf 
  EndProcedure
  
  
  ; Procedure RemoveThumbPointerList(GadgetId.i,Index.i,Number.i)
  ;   Protected *Gdt.gdt
  ;   Protected *Ptr.core::FileData
  ;   Protected n.l
  ;   *Gdt=GetGadgetData(GadgetId)
  ;   LockMutex(*Gdt\ThumbPointerMutex)
  ;   For n=0 To Number
  ;     If SelectElement(*Gdt\ThumbPointerList(),Index)
  ;       *Ptr=*Gdt\ThumbPointer()
  ;           If *Ptr>0
  ;             If *Ptr\State=2:*Ptr\State=1:EndIf
  ;           EndIf
  ;       DeleteElement(*Gdt\ThumbPointerList())
  ;     EndIf
  ;   Next
  ;   LockMutex(*Gdt\ThumbPointerMutex)
  ; EndProcedure
  
  Procedure ForceUpdate(GadgetId.i)
    Protected *Gdt.gdt
    Protected *Ptr.core::FileData
    *Gdt=GetGadgetData(GadgetId)
    *Gdt\Index=0
    LockMutex(*Gdt\ThumbPointerlistMutex)
    ForEach *Gdt\ThumbPointerList()
      *Ptr=*Gdt\ThumbPointerList()
      If *Ptr>0
        If *Ptr\State=2:*Ptr\State=1:EndIf ;Image not Display 
      EndIf 
      DeleteMapElement(*Gdt\ThumbPointerList())
    Next
    UnlockMutex(*Gdt\ThumbPointerlistMutex)
    LoadFromIndex(GadgetId)
  EndProcedure
EndModule

;- TEST PART

CompilerIf #PB_Compiler_IsMainFile 
  UseJPEGImageDecoder()
  UsePNGImageDecoder()
  UseMD5Fingerprint()
  
  Global NewList CurrentList.s()
  
  Procedure CallBackLoadFromIndexB(GadgetId.i,Index.i,Lenght.l)
    Protected n.l
    Protected TmpIndex.i
    Protected relativeIndex.l
    Debug "CallBackLoadFromIndexB("+Str(Index)+","+Str(Lenght)+")"
    For n=1 To Lenght
      TmpIndex=Index+n-1
      If TmpIndex>=0 And TmpIndex<ListSize(CurrentList())
        SelectElement(CurrentList(),TmpIndex)
        relativeIndex=relativeIndex+1
        Protected *Ptr.Core::FileData
        *Ptr=Cache::GetFileDataFromCache(CurrentList())
        If *Ptr
          Thumbs::AddImageToThumb(GadgetId,TmpIndex,*Ptr)
        Else
          Thumbs::AddImageToThumb(GadgetId,TmpIndex,0)
        EndIf 
      Else
        Thumbs::AddImageToThumb(GadgetId,TmpIndex,-1)
      EndIf 
    Next
    Thumbs::LimitIndex(GadgetId,ListSize(CurrentList()))
    ;Thumbs::SetNumberOfElements(GadgetId,relativeIndex)
  EndProcedure
  
  Define Repertoire$
  Define Event.i
  Repertoire$="C:\Users\413\Pictures\Photos\"
  Repertoire$=PathRequester("Chose Directory", Repertoire$)
  If ExamineDirectory(0, Repertoire$, "*.jpg")  
    While NextDirectoryEntry(0)
      If DirectoryEntryType(0) = #PB_DirectoryEntry_File
        AddElement(CurrentList())
        CurrentList()=Repertoire$+DirectoryEntryName(0)
      EndIf
    Wend
    FinishDirectory(0)
  EndIf
  
  Enumeration
    #Win_main
    #Gdt_Nav
    #Gdt_Folder
    #Gdt_ThumbA
    #Gdt_ThumbB
  EndEnumeration
  
  If OpenWindow(#Win_main, 0, 0, 1024, 600, "Thumbnails", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    ButtonGadget(#Gdt_Folder,0,0,100,25,"Choose Folder")
    Thumbs::ThumbsGadget(#Gdt_ThumbA,0,50,512,550,128,@CallBackLoadFromIndexB())
    Thumbs::ThumbsGadget(#Gdt_ThumbB,512,50,512,550,128,@CallBackLoadFromIndexB())
    Repeat
      Delay(1)
      Event = WaitWindowEvent()
      If Event=#PB_Event_Gadget
        If EventGadget()=#Gdt_Folder
          Repertoire$="C:\Users\413\Pictures\Photos\"
          Repertoire$=PathRequester("Chose Directory", Repertoire$)
          If Repertoire$<>""
            ClearList(CurrentList())
            If ExamineDirectory(0, Repertoire$, "*.jpg")  
              While NextDirectoryEntry(0)
                If DirectoryEntryType(0) = #PB_DirectoryEntry_File
                  AddElement(CurrentList())
                  CurrentList()=Repertoire$+DirectoryEntryName(0)
                EndIf
              Wend
              FinishDirectory(0)
              Debug "LISTSIZE="+Str(ListSize(CurrentList()))
              Thumbs::LimitIndex(#Gdt_ThumbA,ListSize(CurrentList()))
              Thumbs::ForceUpdate(#Gdt_ThumbA)
            EndIf
          EndIf
        EndIf
      EndIf 
    Until Event = #PB_Event_CloseWindow
    Thumbs::FreeThumbsGadget(#Gdt_ThumbA)
    Thumbs::FreeThumbsGadget(#Gdt_ThumbB)
  EndIf
CompilerEndIf
Répondre