; ******************************************************************** ; Program: Thumbnails ; Description: add a Thumbnails to select image ; Version: 8.2 ; 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 0 FirstElement(Param\LoadList()) *Ptr = Param\LoadList() DeleteElement(Param\LoadList()) Else *Ptr = 0 EndIf UnlockMutex(Param\LoadListMutex) If *Ptr If *Ptr\Image = 0 And FileSize(*Ptr\FilePath) > 0 Debug "Worker " + ThreadID + " charge: " + GetFilePart(*Ptr\FilePath) If Param\CallBackLoadMedia <> 0 Param\CallBackLoadMedia(*Ptr) Else *Ptr\Image = LoadImage(#PB_Any, *Ptr\FilePath) EndIf 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 LockMutex(Param\SignalMutex) Param\Signal = #True UnlockMutex(Param\SignalMutex) Else Debug "ERREUR de chargement: " + *Ptr\FilePath *Ptr\Image = CreateImage(#PB_Any, 256, 256, 24, RGB(255, 0, 0)) *Ptr\State = 1 EndIf EndIf EndIf EndIf Until Quit = #True Debug "Cache Worker " + ThreadID + " terminé." EndProcedure Procedure StartWorkers() If IsThread(Param\WorkerThreads(0)) = #False Debug "Démarrage du pool de " + #WORKER_THREADS + " threads." Protected i For i = 0 To #WORKER_THREADS - 1 Param\WorkerThreads(i) = CreateThread(@CacheWorkerThread(), i) Next EndIf EndProcedure Procedure AddFileToLoadList(FilePath.s) Protected *Ptr LockMutex(Param\CacheListMutex) If FindMapElement(Param\CacheList(), FilePath) = #False *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) SignalSemaphore(Param\NewTaskSemaphore) Else UnlockMutex(Param\CacheListMutex) EndIf EndProcedure Procedure.i GetFileDataFromCache(FilePath.s, Image.i = 0) Protected *Ptr.core::FileData LockMutex(Param\CacheListMutex) *Ptr = FindMapElement(Param\CacheList(), FilePath) UnlockMutex(Param\CacheListMutex) If *Ptr = 0 StartWorkers() LockMutex(Param\CacheListMutex) *Ptr = AddMapElement(Param\CacheList(), FilePath) *Ptr\FilePath = FilePath If Image = 0 *Ptr\State = 0 *Ptr\Image = 0 LockMutex(Param\LoadListMutex) AddElement(Param\LoadList()) Param\LoadList() = *Ptr UnlockMutex(Param\LoadListMutex) SignalSemaphore(Param\NewTaskSemaphore) Else *Ptr\State = 1 *Ptr\Image = Image EndIf UnlockMutex(Param\CacheListMutex) EndIf ProcedureReturn *Ptr EndProcedure Procedure QuitCache() Debug "Arrêt du cache..." If IsThread(Param\WorkerThreads(0)) LockMutex(Param\QuitMutex) Param\Quit = #True UnlockMutex(Param\QuitMutex) Protected i For i = 0 To #WORKER_THREADS - 1 SignalSemaphore(Param\NewTaskSemaphore) Next For i = 0 To #WORKER_THREADS - 1 If IsThread(Param\WorkerThreads(i)) WaitThread(Param\WorkerThreads(i)) EndIf Next Debug "Tous les workers sont arrêtés." EndIf FreeMutex(Param\LoadListMutex) FreeMutex(Param\CacheListMutex) FreeMutex(Param\SignalMutex) FreeMutex(Param\QuitMutex) FreeSemaphore(Param\NewTaskSemaphore) EndProcedure Procedure.b GetSignalAndReset() Protected Signal.b LockMutex(Param\SignalMutex) Signal = Param\Signal Param\Signal = #False UnlockMutex(Param\SignalMutex) ProcedureReturn Signal EndProcedure Procedure Free(*Ptr.core::FileData) If IsImage(*Ptr\Image): FreeImage(*Ptr\Image): EndIf FreeMap(*Ptr\MetaData()) EndProcedure 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 "Nettoyage cache : " + GetFilePart(*Ptr\FilePath) Free(*Ptr) DeleteMapElement(Param\CacheList()) EndIf EndIf Next UnlockMutex(Param\CacheListMutex) 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 BufferCanvasImage.i[1] SelectedBufferCanvasImage.b Size.l ;Thumb Size Width and Height Index.i ;ThumbIndex OldIndex.i ;Last Index to Clean 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 LastIndexSelected.i ; Last Index Selected ;DPI Aware Value _GadgetWidth.l _GadgetHeight.l _Size.l _ScrollWidth.l _ScrollHeight.l _ThumbsWidth.l _ThumbsHeight.l _MarginH.l _MarginV.l _TimeLineWidth.l ThumbPointerlistMutex.i Map ThumbPointerList.i() LoadFromIndexInitialized.b ;#True CallBack is Ok # Else not initialized (See SetCallBackLoadFromindex() ) CallBackLoadFromIndex.CallBackLoadFromIndex CallBackUpdateThread.i CallBackMutex.i CallBackIndex.i CallBackNbThumbs.l CallBackNeedReload.b DrawSemaphore.i 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\_TimeLineWidth=DesktopScaledX(50) *Gdt\_ThumbsWidth.l=*Gdt\_GadgetWidth-*Gdt\_ScrollWidth-*Gdt\_TimeLineWidth *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 UpdateIndexs(GadgetId.i) Protected *Gdt.gdt Protected *Ptr.core::FileData Static counter.l *Gdt=GetGadgetData(GadgetId) If *Gdt\LoadFromIndexInitialized=#True Debug "COUNTER="+Str(counter) counter=counter+1 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) Protected.l DeltaIndex=Index-*Gdt\OldIndex *Gdt\OldIndex=Index Protected n.l For n=0 To Abs(DeltaIndex) If DeltaIndex>0 If FindMapElement(*Gdt\ThumbPointerList(),Str(*Gdt\OldIndex+DeltaIndex)) DeleteMapElement(*Gdt\ThumbPointerList()) EndIf ElseIf DeltaIndex<0 If FindMapElement(*Gdt\ThumbPointerList(),Str(*Gdt\OldIndex+NThumbs-DeltaIndex)) DeleteMapElement(*Gdt\ThumbPointerList()) EndIf EndIf Next UnlockMutex(*Gdt\ThumbPointerlistMutex) LockMutex(*Gdt\CallBackMutex) *Gdt\CallBackIndex=Index+DeltaIndex *Gdt\CallBackNbThumbs=NThumbs-DeltaIndex *Gdt\CallBackNeedReload=#True UnlockMutex(*Gdt\CallBackMutex) EndIf EndProcedure Procedure UpdateImage2Index(GadgetId.i) Protected Index.i,Thumbs.l Protected NeedReload.b=#False Protected *Gdt.gdt *Gdt=GetGadgetData(GadgetId) ;-Load new File on Index Repeat LockMutex(*Gdt\CallBackMutex) If *Gdt\CallBackNeedReload=#True Index.i=*Gdt\CallBackIndex Thumbs.l=*Gdt\CallBackNbThumbs *Gdt\CallBackNeedReload=#False NeedReload=#True Else NeedReload=#False EndIf UnlockMutex(*Gdt\CallBackMutex) If NeedReload=#True If *Gdt\CallBackLoadFromIndex>0 *Gdt\CallBackLoadFromIndex(*Gdt\GadgetId,*Gdt\CallBackIndex,*Gdt\CallBackNbThumbs) Debug "param\CallBackLoadFromIndex("+Str(*Gdt\CallBackIndex)+","+Str(*Gdt\CallBackNbThumbs)+")" Else Delay(10) Debug "No Set CallBackLoadFromIndex" EndIf Else Delay(50) EndIf Until *Gdt\Quit=#True EndProcedure Procedure LimitIndex(GadgetId.i,IndexMax.i=-1) Protected *Gdt.gdt *Gdt=GetGadgetData(GadgetId) If IndexMax>=0 *Gdt=GetGadgetData(GadgetId) IndexMax=Round((IndexMax/*Gdt\NbH),#PB_Round_Up)**Gdt\NbH -(*Gdt\NbH*(*Gdt\NbV)) *Gdt\IndexMax=IndexMax 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 UpdateIndexs(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 UpdateIndexs(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 (Cache::GetSignalAndReset()=#True Or TrySemaphore(*Gdt\DrawSemaphore)) And StartVectorDrawing(CanvasVectorOutput(*Gdt\GadgetId)) Debug"DRAW IMAGE" 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)) SaveVectorState() ClipPath(#PB_Path_Preserve) 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 ;If Image no Loaded ;AddPathBox(result\X+x, result\Y+y,result\Width,result\Height) ;VectorSourceColor(RGBA(255, 255, 0, 255)) ;FillPath() EndIf ;If *Ptr=0 No Image 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) RestoreVectorState() 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() ;Timeline If *Gdt\_TimeLineWidth>0 Protected.l Date_Start=1921 Protected.l Date_End=2021 Protected.l TH=VectorTextHeight("Ty") Protected.l Nb=*Gdt\_GadgetHeight/(Date_End-Date_Start) Protected.l z,zy=0 For z=0 To (Date_End-Date_Start) zy=zy+TH If zy>=TH*2 zy=0 VectorSourceColor(RGBA(255, 255, 255, 255)) Protected date.s=Str(Date_End-z) MovePathCursor(*Gdt\_GadgetWidth-VectorTextWidth(Date),z*TH) DrawVectorText(date) EndIf Next EndIf 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) 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) SignalSemaphore(*Gdt\DrawSemaphore) ; Redraw Image EndIf Case #PB_Shortcut_Up If *Gdt\Index>0 *Gdt\ThumbsDeltaY=*Gdt\ThumbsDeltaY+DesktopScaledY(5) SignalSemaphore(*Gdt\DrawSemaphore) ; Redraw Image EndIf Case #PB_Shortcut_PageDown If *Gdt\Index<*Gdt\IndexMax *Gdt\ThumbsDeltaY=0 *Gdt\Index=*Gdt\Index+*Gdt\NbH UpdateIndexs(*Gdt\GadgetId) SignalSemaphore(*Gdt\DrawSemaphore) ; Redraw Image EndIf Case #PB_Shortcut_PageUp If *Gdt\Index>0 *Gdt\ThumbsDeltaY=0 *Gdt\Index=*Gdt\Index-*Gdt\NbH UpdateIndexs(*Gdt\GadgetId) SignalSemaphore(*Gdt\DrawSemaphore) ; Redraw Image EndIf EndSelect Case #PB_EventType_Resize InitGadgetValue(*Gdt\GadgetId) UpdateIndexs(*Gdt\GadgetId) SignalSemaphore(*Gdt\DrawSemaphore) ; Redraw Image Case #PB_EventType_LostFocus *Gdt\StartScroll=#False *Gdt\CursorDeltaY=0 SignalSemaphore(*Gdt\DrawSemaphore) ; Redraw Image 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) SignalSemaphore(*Gdt\DrawSemaphore) ; Redraw Image ;EndIf EndIf Case #PB_EventType_LeftButtonDown ;scroll Bar Event If Mx>*Gdt\_ThumbsWidth *Gdt\ZoneClick=1 ; You click in Scroll Zone SignalSemaphore(*Gdt\DrawSemaphore) ; Redraw Image If *Gdt\StartScroll=#False *Gdt\CursorStartY=My *Gdt\StartScroll=#True Debug "Start Scroll"+Str(My) 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" SignalSemaphore(*Gdt\DrawSemaphore) ; Redraw Image 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 ;If GetGadgetAttribute(*Gdt\GadgetId,#PB_Canvas_Modifiers)=#PB_Canvas_Shift LockMutex(*Gdt\ThumbPointerlistMutex) Protected i.i Debug "Select "+Str(*Gdt\LastIndexSelected)+" to "+Str(index) For i=*Gdt\LastIndexSelected To index Debug FindMapElement(*Gdt\ThumbPointerList(),Str(i)) If FindMapElement(*Gdt\ThumbPointerList(),Str(i)) *Ptr=*Gdt\ThumbPointerList() If *Ptr>0 *Ptr\Selected=1-*Ptr\Selected Debug "["+Str(i)+"] Selected="+Str(*Ptr\Selected) EndIf EndIf Next ;EndIf *Gdt\LastIndexSelected=index UnlockMutex(*Gdt\ThumbPointerlistMutex) SignalSemaphore(*Gdt\DrawSemaphore) ; Redraw Image 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_Border) ; Note that the #PB_Canvas_DrawFocus actually involves a 2nd image to draw the focus rectangle and make flicker / black Flash If GadgetId=#PB_Any GadgetId=newGadgetId EndIf Protected *Gdt.Gdt *Gdt=AddMapElement(param\Gdt(),Str(GadgetId)) Debug *Gdt If *Gdt *Gdt\DrawSemaphore=CreateSemaphore(1) *Gdt\CallBackMutex=CreateMutex() *Gdt\ThumbPointerlistMutex=CreateMutex() *Gdt\GadgetId=GadgetId *Gdt\Size=Size Debug *Gdt\Size SetGadgetData(GadgetId, *Gdt) InitGadgetValue(GadgetId) If CallBack SetCallBackLoadFromIndex(GadgetId,CallBack) UpdateIndexs(GadgetId) EndIf *Gdt\CallBackUpdateThread=CreateThread(@UpdateImage2Index(),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) WaitThread(*Gdt\CallBackUpdateThread) FreeMutex(*Gdt\CallBackMutex) FreeSemaphore(*Gdt\DrawSemaphore) 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 DeleteMapElement(*Gdt\ThumbPointerList()) EndIf Next UnlockMutex(*Gdt\ThumbPointerlistMutex) UpdateIndexs(GadgetId) EndProcedure EndModule ;- TEST PART CompilerIf #PB_Compiler_IsMainFile Enumeration #Win_main #Gdt_Nav #Gdt_Folder #Gdt_ThumbA #Gdt_ThumbB EndEnumeration Global NewList CurrentList.s() Global CurrentListMutex.i Procedure CallBackLoadFromIndexB(GadgetId.i,Index.i,Lenght.l) Protected n.l Protected TmpIndex.i Protected relativeIndex.l Protected *Ptr.Core::FileData Debug "CallBackLoadFromIndexB("+Str(Index)+","+Str(Lenght)+")" LockMutex(CurrentListMutex) For n=1 To Lenght TmpIndex=Index+n-1 If TmpIndex>=0 And TmpIndex "" If ExamineDirectory(0, Repertoire$, "*.jpg") LockMutex(CurrentListMutex) ClearList(CurrentList()) While NextDirectoryEntry(0) If DirectoryEntryType(0) = #PB_DirectoryEntry_File AddElement(CurrentList()) CurrentList()=Repertoire$+DirectoryEntryName(0) EndIf Wend FinishDirectory(0) UnlockMutex(CurrentListMutex) Debug "LISTSIZE="+Str(ListSize(CurrentList())) Thumbs::LimitIndex(#Gdt_ThumbA, ListSize(CurrentList())) Thumbs::ForceUpdate(#Gdt_ThumbA) EndIf EndIf EndIf EndIf If Event=#PB_Event_SizeWindow Debug "coucou" ResizeGadget(#Gdt_ThumbA,0,50,WindowWidth(#Win_main),WindowHeight(#Win_main)-50) EndIf Until Event = #PB_Event_CloseWindow Thumbs::FreeThumbsGadget(#Gdt_ThumbA) Cache::QuitCache() EndIf CompilerEndIf ; IDE Options = PureBasic 6.21 (Windows - x64) ; CursorPosition = 1000 ; FirstLine = 954 ; Folding = ------ ; EnableThread ; EnableXP ; DPIAware