ListView Background Image & OwnerDraw Items

Share your advanced PureBasic knowledge/code with the community.
User avatar
Fluid Byte
Addict
Addict
Posts: 2336
Joined: Fri Jul 21, 2006 4:41 am
Location: Berlin, Germany

ListView Background Image & OwnerDraw Items

Post by Fluid Byte »

Code: Select all

; ** LIST-VIEW GADGET BACKGROUND IMAGE & OWNERDRAW ITEMS ** 
;
; This code is based on code originally written by Sparkie,
; adapted by Srod and now heavily modified by me. It demonstrates
; how to display a background image in a ListView gadget featuring
; various types of drawing it. Furthermore this is an complete
; example of using listboxes with owner-draw items.
; 
;
; Original Forum-Thread by Sparkie:
; http://www.purebasic.fr/english/viewtopic.php?t=13623
;
; Adapted Version by Srod:
; http://www.purebasic.fr/english/viewtopic.php?t=24449
;
;
; =================================================================
; Author    : Fluid Byte
; Version   : PB 4.XX
; Platform  : Windows
; E-Mail:  fluidbyte@web.de
; ================================================================= 
;
;
; Sourcecode Changes:
; -------------------
;
; * Cleanup:
;
;    - Naming Conventions
;    - Spacing / Tabbing
;    - Paragraphs
;    - Command Concatenation (:)
;    - Codeflow
;
; * Optimizations:
;
;    - Procedures have been split up and merged
;    - Removed redundant code
;    - Removed dublicated code
;    - Error Checking
;    - Linear stretchmode is now #COLORONCOLOR
;    - API Usage Compatibility
;
; * Fixed:
;
;    - Gadget didn't refresh after and while selecting multiple items via mouse dragging
;    - Following default listbox messages were missing:
;       >> #WM_CHAR
;       >> #WM_SETFOCUS
;       >> #WM_KILLFOCUS
;
; * Added:
;
;    - Backdrop is now set with a single extra command: SetBackgroundImage()
;    - Gadget / Image can be a Handle or Identifier
;    - Stretch Mode: smooth / linear
;    - Drawing Mode: fixed / tiled / stretched
;    - Item Height: Height of current gadget font / user defined
;    - Automatic item label positioning
;    - Color Sheme: Operating System / user defined
;    - Item Background Rectangle
;    - Item Text Status: highlighted / disabled
;    - Item Focus Rectangle: Operating System / user defined

EnableExplicit

Enumeration
	#ODLV_FIXED
	#ODLV_TILED
	#ODLV_STRETCH
	#ODLV_LINEAR
	#ODLV_SMOOTH
EndEnumeration

Structure ODLV_DATA
	DrawMode.b
	StretchMode.b
	ItemHeight.b
	bSysColors.b
	clrText.l	
	clrHiLight.l
	clrGrayed.l
	clrBack.l
	clrFocus.l
	dwReserved1.i
	dwReserved2.i
EndStructure

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

Procedure ODLV_ListboxProc(hWnd,uMsg,wParam,lParam)
	Protected *odlv.ODLV_DATA, Result, bRepaint

	*odlv = GetWindowLongPtr_(hwnd,#GWL_USERDATA)
		
	; // REPAINT TRIGGER //
	Result = CallWindowProc_(*odlv\dwReserved2,hwnd,uMsg,wParam,lParam)

	Select uMsg	
		Case #WM_KEYDOWN 
		If wParam = #VK_DOWN Or wParam = #VK_UP Or wParam = #VK_NEXT Or wParam = #VK_PRIOR Or wParam = #VK_HOME Or wParam = #VK_END 
			bRepaint = #True 
		EndIf		
					
		Case #WM_MOUSEMOVE
		If wParam <> 0 
			bRepaint = #True : Result =  0
		EndIf

		Case #WM_ERASEBKGND
		bRepaint = #True : Result = 1
		
		Case #WM_PAINT 
		bRepaint = #False : Result = 0
				
		Case #WM_SETFOCUS, #WM_KILLFOCUS, #WM_CHAR, #WM_LBUTTONDOWN, #WM_LBUTTONUP, #WM_VSCROLL, #WM_MOUSEWHEEL
		bRepaint = #True : Result =  0
	EndSelect

	; // REPAINT SETUP (logical order) //
	If bRepaint		
		Protected crc.RECT, hbmOutput, hDC, hdcMem, bmp.BITMAP, hbrBackground
		Protected ItemIndex, ItemCount, i, SelStart, SelEnd, SelDir
		Protected bHighlight, irc.RECT, lpBuffer, hdcLB, si.SCROLLINFO				
		
		Static prevScrollPos
		
		GetClientRect_(hwnd,crc)
	
	  	hbmOutput = CreateImage(#PB_Any,crc\right-crc\left,crc\bottom-crc\top) 
	  	  
	  	hdc = StartDrawing(ImageOutput(hbmOutput))
	  	SelectObject_(hdc,GetGadgetFont(GetDlgCtrlID_(hwnd)))	  	
	  	SetBkMode_(hdc,#TRANSPARENT)
		SetTextAlign_(hdc,#TA_NOUPDATECP)
		
		; // DRAW BACKGROUND //
		hdcMem = CreateCompatibleDC_(hdc)
		SelectObject_(hdcMem,*odlv\dwReserved1)
		GetObject_(*odlv\dwReserved1,SizeOf(BITMAP),bmp)	
			
		Select *odlv\DrawMode
			Case #ODLV_FIXED
			FillRect_(hdc,crc,GetSysColorBrush_(#COLOR_WINDOW))
			BitBlt_(hdc,0,0,crc\right-crc\left,crc\bottom-crc\top,hdcMem,0,0,#SRCCOPY)
			
			Case #ODLV_TILED
			hbrBackground = CreatePatternBrush_(*odlv\dwReserved1)
			FillRect_(hdc,crc,hbrBackground)
			DeleteObject_(hbrBackground)
			
			Case #ODLV_STRETCH
			If *odlv\StretchMode = #ODLV_SMOOTH
				SetStretchBltMode_(hdc,#HALFTONE)
			Else
				SetStretchBltMode_(hdc,#COLORONCOLOR)
			EndIf
			
			StretchBlt_(hdc,0,0,crc\right-crc\left,crc\bottom-crc\top,hdcMem,0,0,bmp\bmWidth,bmp\bmHeight,#SRCCOPY) 			
		EndSelect
		
		DeleteDC_(hdcMem)				
		
		; // ENUMERATE ITEMS //
		ItemIndex = SendMessage_(hwnd,#LB_GETTOPINDEX,0,0) 	
		ItemCount = SendMessage_(hwnd,#LB_GETCOUNT,0,0) - 1

		For i = ItemIndex To ItemCount 
			SendMessage_(hwnd,#LB_GETITEMRECT,i,irc)
			
			If irc\bottom <= crc\bottom
 				; // ITEM SELECTION //
 				If (uMsg = #WM_MOUSEMOVE And wParam & #MK_LBUTTON) Or (uMsg = #WM_LBUTTONDOWN And wParam & #MK_SHIFT)
					SelStart = SendMessage_(hwnd,#LB_GETANCHORINDEX,0,0)
					SelEnd = SendMessage_(hwnd,#LB_GETCARETINDEX,0,0)
					
					SelDir = SelEnd - SelStart
					
					If SelDir > 0
						If i >= SelStart And i <= SelEnd : bHighlight = #True : EndIf
					Else
						If i>= SelEnd And i <= SelStart : bHighlight = #True : EndIf
					EndIf					
  				EndIf
   				
  				If SendMessage_(hwnd,#LB_GETSEL,i,0) : bHighlight = #True : EndIf
 				
				; // DRAW ITEMS //
				lpBuffer = AllocateMemory(SendMessage_(hwnd,#LB_GETTEXTLEN,i,0))
				SendMessage_(hwnd,#LB_GETTEXT,i,lpBuffer)
				
				If bHighlight				
					SetTextColor_(hdc,*odlv\clrHiLight)					
					Box(irc\left,irc\top,irc\right - irc\left,irc\bottom - irc\top,*odlv\clrBack)
					irc\left + 5
					DrawText_(hdc,lpBuffer,-1,irc,#DT_SINGLELINE | #DT_VCENTER)
					irc\left - 5
					
					If i = SendMessage_(hwnd,#LB_GETANCHORINDEX,0,0) And GetFocus_() = hwnd
						If *odlv\clrFocus = -1
							SetTextColor_(hdc,0)
							DrawFocusRect_(hdc,irc)				
						Else
							DrawingMode(#PB_2DDrawing_Transparent | #PB_2DDrawing_Outlined)
							Box(irc\left,irc\top,irc\right - irc\left,irc\bottom - irc\top,*odlv\clrFocus)
						EndIf
					EndIf				
				Else
					If IsWindowEnabled_(hwnd)
						SetTextColor_(hdc,*odlv\clrText)
					Else
						SetTextColor_(hdc,*odlv\clrGrayed)
					EndIf

					irc\left + 5				
					DrawText_(hdc,lpBuffer,-1,irc,#DT_SINGLELINE | #DT_VCENTER)				
				EndIf
								
				FreeMemory(lpBuffer) : bHighlight = #False
			Else
				Break 
			EndIf
		Next

		; // OUTPUT IMAGE + CLEANUP //
		hdcLB = GetDC_(hwnd)
 		BitBlt_(hdcLB,0,0,crc\right - crc\left,crc\bottom - crc\top,hdc,0,0,#SRCCOPY) 
		ReleaseDC_(hwnd,hdcLB)
		StopDrawing() 
		
		FreeImage(hbmOutput)
		
		; // REFRESH SCROLLBAR (KEYBOARD INPUT) //
		si\cbSize = SizeOf(SCROLLINFO)
		si\fMask = #SIF_POS
		GetScrollInfo_(hwnd,#SB_VERT,@si) 
		
		If si\nPos <> prevScrollPos
			SetScrollInfo_(hwnd,#SB_VERT,@si,#True)
		EndIf
		
		prevScrollPos = si\nPos		
	EndIf
	
	ProcedureReturn Result 
EndProcedure

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

Procedure SetBackgroundImage(GadgetID,ImageID,*odlv.ODLV_DATA)
	Protected hwndListBox, fsz.SIZE, hdc
	
	If IsGadget(GadgetID)
		hwndListBox = GadgetID(GadgetID)
	ElseIf IsWindow_(GadgetID)
		hwndListBox = GadgetID
	Else
		ProcedureReturn 0
	EndIf

	If *odlv\ItemHeight <= 0
		hdc = GetDC_(hwndListBox)
		SelectObject_(hdc,SendMessage_(hwndListBox,#WM_GETFONT,0,0))
		GetTextExtentPoint32_(hdc,"ABCDEF",6,fsz)		
		*odlv\ItemHeight = fsz\cy
		ReleaseDC_(hwndListBox,hdc)
	EndIf
	
	If *odlv\bSysColors
		*odlv\clrText = GetSysColor_(#COLOR_WINDOWTEXT)
		*odlv\clrHiLight = GetSysColor_(#COLOR_HIGHLIGHTTEXT)
		*odlv\clrGrayed = GetSysColor_(#COLOR_GRAYTEXT)
		*odlv\clrBack = GetSysColor_(#COLOR_HIGHLIGHT)		
	EndIf
	
	SendMessage_(hwndListBox,#LB_SETITEMHEIGHT,0,*odlv\ItemHeight)
	SendMessage_(hwndListBox,#WM_SETREDRAW,0,0)
	
	SetWindowLongPtr_(hwndListBox,#GWL_USERDATA,*odlv)
	
	If IsImage(ImageID)
		*odlv\dwReserved1 = ImageID(ImageID)
	ElseIf GetObjectType_(ImageID) = #OBJ_BITMAP
		*odlv\dwReserved1 = ImageID
	Else
		ProcedureReturn 0
	EndIf
	
	*odlv\dwReserved2 = SetWindowLongPtr_(hwndListBox,#GWL_WNDPROC,@ODLV_ListboxProc())	
	
	ProcedureReturn #True
EndProcedure

; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

#LST_BackdropDemo = 100

Define i, odlv.ODLV_DATA, EventID

; Create custom background image
CreateImage(0,250,300)

StartDrawing(ImageOutput(0))
For i=0 To 299 : Box(0,i,250,1,RGB(255,55 + (i * 200 / 399),25)) : Next
StopDrawing()

; Create Listbox with background
OpenWindow(0,0,0,380,315,"Listbox Image Backdrop",#WS_OVERLAPPEDWINDOW | 1)
ListViewGadget(#LST_BackdropDemo,10,10,0,0,#PB_ListView_MultiSelect | #LBS_OWNERDRAWFIXED)

For i = 1 To 100
	AddGadgetItem(#LST_BackdropDemo,-1,"Item #" + RSet(Str(i),3,"0") + " of the Listview") 
Next

odlv\DrawMode    = #ODLV_STRETCH	; Background will sized to fit window
odlv\StretchMode = #ODLV_LINEAR	; Background will not be smoothed (much faster when using #ODLV_STRETCH!)
odlv\ItemHeight  = 0					; Use height of the default gadget font
odlv\bSysColors  = #True			; Use Windows colors to draw items
odlv\clrText     = 0					; Color of normal item labels
odlv\clrHiLight  = 0					; Color of selected item labels
odlv\clrGrayed   = 0					; Color of disbaled item labels
odlv\clrBack     = 0					; Color of the highlight background
odlv\clrFocus    = -1				; Color of the focus rect (-1 = system rect)

SetBackgroundImage(#LST_BackdropDemo,0,odlv)

; Main loop
Repeat
	EventID = WaitWindowEvent()

	If EventID = #PB_Event_SizeWindow
		ResizeGadget(#LST_BackdropDemo,5,5,WindowWidth(0)-10,WindowHeight(0)-10)
	EndIf
Until EventID = #WM_CLOSE
*** Just updated to x64 + several bug fixes ***
Last edited by Fluid Byte on Sun Jan 22, 2017 2:16 pm, edited 8 times in total.
Windows 10 Pro, 64-Bit / Whose Hoff is it anyway?
User avatar
Joakim Christiansen
Addict
Addict
Posts: 2452
Joined: Wed Dec 22, 2004 4:12 pm
Location: Norway
Contact:

Post by Joakim Christiansen »

That's sexy!
I like logic, hence I dislike humans but love computers.
rsts
Addict
Addict
Posts: 2736
Joined: Wed Aug 24, 2005 8:39 am
Location: Southwest OH - USA

Post by rsts »

Nice!
Post Reply