Code: Select all
; ===================================================================
; Clipboard UI Inspector (Windows)
; v1.0 09.9.25 by Dige
; v1.1 12.9.25 bug fixed, thanks Axolotl
; ===================================================================
EnableExplicit
CompilerIf #PB_Compiler_OS <> #PB_OS_Windows
MessageRequester("Clipboard", "This program is Windows-only.")
End
CompilerEndIf
; ---- Image encoders for saving (PNG/JPEG/BMP) ----
UsePNGImageEncoder()
UseJPEGImageEncoder()
; ---- Windows clipboard constants ----
#CF_TEXT = 1
#CF_UNICODETEXT = 13
#CF_HDROP = 15
#CF_DIB = 8
#CF_BITMAP = 2
#MAX_PATH = 260
#DIB_RGB_COLORS = 0
; ---- Gadget IDs ----
Enumeration 1000
#G_Panel
#G_EditFormats
#G_EditHtmlRaw
#G_EditHtmlFrag
#G_EditRtf
#G_EditUnicode
#G_EditAnsi
#G_EditFiles
#G_ImageDIB
#G_ImageBMP
#G_BtnRefresh
#G_BtnSave
#G_Status
EndEnumeration
; ===================================================================
; Utilities
; ===================================================================
Procedure.s DesktopFolder()
; Returns the user Desktop path (with trailing backslash)
Protected path$ = GetUserDirectory(#PB_Directory_Desktop)
If Right(path$, 1) <> "\" : path$ + "\" : EndIf
ProcedureReturn path$
EndProcedure
Procedure.i SaveTextFile(path$, text$, encoding = #PB_UTF8)
; Saves text content with selected encoding; returns #True on success
Protected ok = #False
Protected f = CreateFile(#PB_Any, path$)
If f
Select encoding
Case #PB_UTF8 : WriteStringFormat(f, #PB_UTF8)
Case #PB_Ascii : WriteStringFormat(f, #PB_Ascii)
Case #PB_Unicode: WriteStringFormat(f, #PB_Unicode)
EndSelect
WriteString(f, text$)
CloseFile(f)
ok = #True
EndIf
ProcedureReturn ok
EndProcedure
Procedure.i SaveImageByExt(img, path$)
; Saves image depending on file extension (png/jpg/jpeg/bmp)
Protected ext$ = LCase(GetExtensionPart(path$))
Protected ok = #False
If ext$ = "png"
ok = SaveImage(img, path$, #PB_ImagePlugin_PNG)
ElseIf ext$ = "jpg" Or ext$ = "jpeg"
ok = SaveImage(img, path$, #PB_ImagePlugin_JPEG, 8) ; quality 0..10
ElseIf ext$ = "bmp"
ok = SaveImage(img, path$, #PB_ImagePlugin_BMP)
Else
; Default to PNG if unknown extension
ok = SaveImage(img, path$, #PB_ImagePlugin_PNG)
EndIf
ProcedureReturn ok
EndProcedure
; Maps a panel tab index to a sensible default filename + encoding
; Text tabs return encoding; image tabs return encoding=-1 (not used)
Procedure.i DefaultSaveInfo(tab, *filenameOut.STRING, *encodingOut.INTEGER)
Select tab
Case 0 : *filenameOut\s = "clipboard_formats.txt" : *encodingOut\i = #PB_UTF8 : ProcedureReturn #True
Case 1 : *filenameOut\s = "clip_html_raw.txt" : *encodingOut\i = #PB_Ascii : ProcedureReturn #True
Case 2 : *filenameOut\s = "clip.html" : *encodingOut\i = #PB_UTF8 : ProcedureReturn #True
Case 3 : *filenameOut\s = "clip.rtf" : *encodingOut\i = #PB_Ascii : ProcedureReturn #True
Case 4 : *filenameOut\s = "clip.txt" : *encodingOut\i = #PB_UTF8 : ProcedureReturn #True
Case 5 : *filenameOut\s = "clip_ansi.txt" : *encodingOut\i = #PB_UTF8 : ProcedureReturn #True
Case 6 : *filenameOut\s = "clip_files.txt" : *encodingOut\i = #PB_UTF8 : ProcedureReturn #True
Case 7 : *filenameOut\s = "clip_dib.png" : *encodingOut\i = -1 : ProcedureReturn #True
Case 8 : *filenameOut\s = "clip_bitmap.png" : *encodingOut\i = -1 : ProcedureReturn #True
EndSelect
ProcedureReturn #False
EndProcedure
; ===================================================================
; Format name helper
; ===================================================================
Procedure.s FormatName(uFormat)
; Maps format IDs to human-readable names. Falls back to GetClipboardFormatName_
Protected name$ = ""
Select uFormat
Case #CF_TEXT : name$ = "CF_TEXT"
Case #CF_UNICODETEXT : name$ = "CF_UNICODETEXT"
Case #CF_HDROP : name$ = "CF_HDROP"
Case #CF_DIB : name$ = "CF_DIB"
Case #CF_BITMAP : name$ = "CF_BITMAP"
Default
Protected *buf = AllocateMemory(256 * SizeOf(Character))
If *buf
If GetClipboardFormatName_(uFormat, *buf, 255)
name$ = PeekS(*buf, -1)
Else
name$ = "Format #" + Str(uFormat)
EndIf
FreeMemory(*buf)
Else
name$ = "Format #" + Str(uFormat)
EndIf
EndSelect
ProcedureReturn name$
EndProcedure
; ===================================================================
; Clipboard readers (textual formats)
; ===================================================================
Procedure.s GetUnicodeText()
Protected s$ = ""
If IsClipboardFormatAvailable_(#CF_UNICODETEXT)
Protected h = GetClipboardData_(#CF_UNICODETEXT)
If h
Protected *p = GlobalLock_(h)
If *p : s$ = PeekS(*p, -1, #PB_Unicode) : EndIf
If *p : GlobalUnlock_(h) : EndIf
EndIf
EndIf
ProcedureReturn s$
EndProcedure
Procedure.s GetAnsiText()
Protected s$ = ""
If IsClipboardFormatAvailable_(#CF_TEXT)
Protected h = GetClipboardData_(#CF_TEXT)
If h
Protected *p = GlobalLock_(h)
If *p : s$ = PeekS(*p, -1, #PB_Ascii) : EndIf
If *p : GlobalUnlock_(h) : EndIf
EndIf
EndIf
ProcedureReturn s$
EndProcedure
Procedure.s GetRtf()
Protected s$ = ""
Protected cf = RegisterClipboardFormat_("Rich Text Format")
If cf And IsClipboardFormatAvailable_(cf)
Protected h = GetClipboardData_(cf)
If h
Protected *p = GlobalLock_(h)
If *p : s$ = PeekS(*p, -1, #PB_Ascii) : EndIf
If *p : GlobalUnlock_(h) : EndIf
EndIf
EndIf
ProcedureReturn s$
EndProcedure
Procedure.s GetHtmlRaw()
; CF_HTML raw payload (ASCII; includes header with offsets)
Protected s$ = ""
Protected cf = RegisterClipboardFormat_("HTML Format")
If cf And IsClipboardFormatAvailable_(cf)
Protected h = GetClipboardData_(cf)
If h
Protected *p = GlobalLock_(h)
If *p : s$ = PeekS(*p, -1, #PB_Ascii) : EndIf
If *p : GlobalUnlock_(h) : EndIf
EndIf
EndIf
ProcedureReturn s$
EndProcedure
; ---------- CF_HTML fragment extraction ----------
Procedure.i ParseOffsetAfterLabel(html$, label$)
; Parses a decimal number immediately following a label (e.g., "StartHTML:")
; Returns -1 if not found.
Protected pos = FindString(html$, label$, 1)
If pos = 0 : ProcedureReturn -1 : EndIf
pos + Len(label$)
Protected digits$ = "", ch$
While pos <= Len(html$)
ch$ = Mid(html$, pos, 1)
If ch$ >= "0" And ch$ <= "9"
digits$ + ch$
pos + 1
Else
Break
EndIf
Wend
If digits$ = "" : ProcedureReturn -1 : EndIf
ProcedureReturn Val(digits$)
EndProcedure
Procedure.s ExtractHtmlFragment(html$)
; Prefers <!--StartFragment--> ... <!--EndFragment-->
; Falls back to StartHTML/EndHTML 0-based byte offsets.
Protected startTag$ = "<!--StartFragment-->"
Protected endTag$ = "<!--EndFragment-->"
Protected s = FindString(html$, startTag$, 1)
Protected e = FindString(html$, endTag$, 1)
If s And e And e > s
s + Len(startTag$)
ProcedureReturn Mid(html$, s, e - s)
EndIf
Protected si = ParseOffsetAfterLabel(html$, "StartHTML:")
Protected ei = ParseOffsetAfterLabel(html$, "EndHTML:")
If si >= 0 And ei > si And ei <= Len(html$)
ProcedureReturn Mid(html$, si + 1, ei - si) ; Mid is 1-based; offsets are 0-based
EndIf
ProcedureReturn html$
EndProcedure
; ---------- CF_HDROP (files list) ----------
Procedure.s FilesFromHDrop()
Protected result$ = ""
If IsClipboardFormatAvailable_(#CF_HDROP)
Protected hDrop = GetClipboardData_(#CF_HDROP)
If hDrop
Protected count = DragQueryFile_(hDrop, $FFFFFFFF, 0, 0)
Protected i
For i = 0 To count - 1
Protected *buf = AllocateMemory((#MAX_PATH + 1) * SizeOf(Character))
If *buf
DragQueryFile_(hDrop, i, *buf, #MAX_PATH)
result$ + PeekS(*buf) + #CRLF$
FreeMemory(*buf)
EndIf
Next
EndIf
EndIf
ProcedureReturn result$
EndProcedure
; ===================================================================
; Image helpers (CF_DIB / CF_BITMAP)
; ===================================================================
; Build a BMP file in memory from a CF_DIB HGLOBAL.
; Returns pointer and size via out parameters; caller must FreeMemory().
Procedure.i BuildBmpFromCFDIB(hGlobal, *outPtr.Integer, *outSize.Integer)
Protected total = GlobalSize_(hGlobal)
If total = 0 : ProcedureReturn #False : EndIf
Protected *pDib = GlobalLock_(hGlobal)
If *pDib = 0 : ProcedureReturn #False : EndIf
; Read BITMAPINFOHEADER fields for bfOffBits calculation
Protected biSize.l = PeekL(*pDib + 0)
Protected biBitCount.w = PeekW(*pDib + 14)
Protected biCompression.l= PeekL(*pDib + 16)
Protected biClrUsed.l = PeekL(*pDib + 32)
Protected paletteBytes.l = 0
If biBitCount <= 8
Protected colors.l = biClrUsed
If colors = 0 : colors = 1 << biBitCount : EndIf
paletteBytes = colors * 4
ElseIf biCompression = 3 And (biBitCount = 16 Or biBitCount = 32)
paletteBytes = 12 ; three DWORD masks
EndIf
Protected offBits.l = 14 + biSize + paletteBytes
; Allocate BMP buffer: 14-byte file header + DIB block
Protected fileSize.l = 14 + total
Protected *bmp = AllocateMemory(fileSize)
If *bmp = 0
GlobalUnlock_(hGlobal)
ProcedureReturn #False
EndIf
; BITMAPFILEHEADER
PokeW(*bmp + 0, $4D42) ; bfType = 'BM'
PokeL(*bmp + 2, fileSize) ; bfSize
PokeW(*bmp + 6, 0) ; bfReserved1
PokeW(*bmp + 8, 0) ; bfReserved2
PokeL(*bmp + 10, offBits) ; bfOffBits
; Copy CF_DIB block after the 14-byte header
CopyMemory(*pDib, *bmp + 14, total)
GlobalUnlock_(hGlobal)
PokeI(*outPtr, *bmp)
PokeI(*outSize, fileSize)
ProcedureReturn #True
EndProcedure
; Structure BITMAP
; bmType.l
; bmWidth.l
; bmHeight.l
; bmWidthBytes.l
; bmPlanes.w
; bmBitsPixel.w
; bmBits.i
; EndStructure
; Build a BMP file in memory from HBITMAP using GetDIBits (32bpp BI_RGB).
; Returns pointer and size via out parameters; caller must FreeMemory().
Procedure.i BuildBmpFromHBitmap(hBmp, *outPtr.Integer, *outSize.Integer)
If hBmp = 0 : ProcedureReturn #False : EndIf
Protected bm.BITMAP
If GetObject_(hBmp, SizeOf(BITMAP), @bm) = 0 : ProcedureReturn #False : EndIf
Protected width.l = bm\bmWidth
Protected height.l = bm\bmHeight
If width <= 0 Or height = 0 : ProcedureReturn #False : EndIf
; Prepare BITMAPINFOHEADER (32bpp, BI_RGB)
Protected biSize.l = 40
Protected bpp.w = 32
Protected stride.l = ((width * bpp + 31) / 32) * 4
Protected imgSize.l= stride * Abs(height)
Protected bi = AllocateMemory(biSize)
If bi = 0 : ProcedureReturn #False : EndIf
FillMemory(bi, biSize, 0)
PokeL(bi + 0, biSize) ; biSize
PokeL(bi + 4, width) ; biWidth
PokeL(bi + 8, height) ; biHeight (positive = bottom-up)
PokeW(bi + 12, 1) ; biPlanes
PokeW(bi + 14, bpp) ; biBitCount
PokeL(bi + 16, 0) ; biCompression = BI_RGB
PokeL(bi + 20, imgSize) ; biSizeImage
PokeL(bi + 24, 2835) ; biXPelsPerMeter (~72 DPI)
PokeL(bi + 28, 2835) ; biYPelsPerMeter
; Get pixel bits
Protected *bits = AllocateMemory(imgSize)
If *bits = 0
FreeMemory(bi)
ProcedureReturn #False
EndIf
Protected hdc = GetDC_(0)
If hdc = 0
FreeMemory(bi) : FreeMemory(*bits)
ProcedureReturn #False
EndIf
If GetDIBits_(hdc, hBmp, 0, height, *bits, bi, #DIB_RGB_COLORS) = 0
ReleaseDC_(0, hdc)
FreeMemory(bi) : FreeMemory(*bits)
ProcedureReturn #False
EndIf
ReleaseDC_(0, hdc)
; Compose BMP file memory: 14 + 40 + imgSize
Protected fileSize.l = 14 + biSize + imgSize
Protected *bmp = AllocateMemory(fileSize)
If *bmp = 0
FreeMemory(bi) : FreeMemory(*bits)
ProcedureReturn #False
EndIf
; BITMAPFILEHEADER
PokeW(*bmp + 0, $4D42) ; 'BM'
PokeL(*bmp + 2, fileSize)
PokeW(*bmp + 6, 0)
PokeW(*bmp + 8, 0)
PokeL(*bmp + 10, 14 + biSize) ; pixel data offset
; Copy headers + bits
CopyMemory(bi, *bmp + 14, biSize)
CopyMemory(*bits, *bmp + 14+biSize, imgSize)
FreeMemory(bi)
FreeMemory(*bits)
PokeI(*outPtr, *bmp)
PokeI(*outSize, fileSize)
ProcedureReturn #True
EndProcedure
; ===================================================================
; Globals holding the latest clipboard snapshot
; ===================================================================
Global gFormats$ = ""
Global gHtmlRaw$ = ""
Global gHtmlFrag$ = ""
Global gRtf$ = ""
Global gUnicode$ = ""
Global gAnsi$ = ""
Global gFiles$ = ""
Global gImgDIB.i = -1
Global gImgBMP.i = -1
; ===================================================================
; Refresh clipboard snapshot + update UI
; ===================================================================
Procedure RefreshClipboardAndUI()
; Clear previous text buffers
gFormats$ = "" : gHtmlRaw$ = "" : gHtmlFrag$ = "" : gRtf$ = ""
gUnicode$ = "" : gAnsi$ = "" : gFiles$ = ""
; Free previous images to avoid leaks
If IsImage(gImgDIB) : FreeImage(gImgDIB) : EndIf : gImgDIB = -1
If IsImage(gImgBMP) : FreeImage(gImgBMP) : EndIf : gImgBMP = -1
If OpenClipboard_(0)
; Enumerate formats
gFormats$ + "== Available Clipboard Formats ==" + #CRLF$
Define fmt.i = 0
Repeat
fmt = EnumClipboardFormats_(fmt)
If fmt = 0 : Break : EndIf
gFormats$ + Str(fmt) + " -> " + FormatName(fmt) + #CRLF$
ForEver
; Read textual payloads
gHtmlRaw$ = GetHtmlRaw()
If gHtmlRaw$ <> "" : gHtmlFrag$ = ExtractHtmlFragment(gHtmlRaw$) : EndIf
gRtf$ = GetRtf()
gUnicode$ = GetUnicodeText()
If gUnicode$ = "" : gAnsi$ = GetAnsiText() : EndIf
gFiles$ = FilesFromHDrop()
; Build DIB image (CF_DIB)
If IsClipboardFormatAvailable_(#CF_DIB)
Define hDib = GetClipboardData_(#CF_DIB)
If hDib
Define *buf.Integer, size.Integer
If BuildBmpFromCFDIB(hDib, @*buf, @size)
gImgDIB = CatchImage(#PB_Any, *buf, size)
FreeMemory(*buf)
EndIf
EndIf
EndIf
; Build BMP image (CF_BITMAP)
If IsClipboardFormatAvailable_(#CF_BITMAP)
Define hBmp = GetClipboardData_(#CF_BITMAP)
If hBmp
Define *buf2.Integer, size2.Integer
If BuildBmpFromHBitmap(hBmp, @*buf2, @size2)
gImgBMP = CatchImage(#PB_Any, *buf2, size2)
FreeMemory(*buf2)
EndIf
EndIf
EndIf
CloseClipboard_()
Else
gFormats$ = "Could not open the clipboard."
EndIf
; ---- Push into editors ----
If IsGadget(#G_EditFormats)
If gFormats$ <> "" : SetGadgetText(#G_EditFormats, gFormats$)
Else : SetGadgetText(#G_EditFormats, "Not available") : EndIf
EndIf
If IsGadget(#G_EditHtmlRaw)
If gHtmlRaw$ <> "" : SetGadgetText(#G_EditHtmlRaw, gHtmlRaw$)
Else : SetGadgetText(#G_EditHtmlRaw, "Not available") : EndIf
EndIf
If IsGadget(#G_EditHtmlFrag)
If gHtmlFrag$ <> "" : SetGadgetText(#G_EditHtmlFrag, gHtmlFrag$)
Else : SetGadgetText(#G_EditHtmlFrag, "Not available") : EndIf
EndIf
If IsGadget(#G_EditRtf)
If gRtf$ <> "" : SetGadgetText(#G_EditRtf, gRtf$)
Else : SetGadgetText(#G_EditRtf, "Not available") : EndIf
EndIf
If IsGadget(#G_EditUnicode)
If gUnicode$ <> "" : SetGadgetText(#G_EditUnicode, gUnicode$)
Else : SetGadgetText(#G_EditUnicode, "Not available") : EndIf
EndIf
If IsGadget(#G_EditAnsi)
If gAnsi$ <> "" : SetGadgetText(#G_EditAnsi, gAnsi$)
Else : SetGadgetText(#G_EditAnsi, "Not available") : EndIf
EndIf
If IsGadget(#G_EditFiles)
If gFiles$ <> "" : SetGadgetText(#G_EditFiles, gFiles$)
Else : SetGadgetText(#G_EditFiles, "Not available") : EndIf
EndIf
; ---- Push images into image gadgets ----
If IsGadget(#G_ImageDIB)
If IsImage(gImgDIB)
SetGadgetState(#G_ImageDIB, ImageID(gImgDIB))
Else
SetGadgetState(#G_ImageDIB, 0)
EndIf
EndIf
If IsGadget(#G_ImageBMP)
If IsImage(gImgBMP)
SetGadgetState(#G_ImageBMP, ImageID(gImgBMP))
Else
SetGadgetState(#G_ImageBMP, 0)
EndIf
EndIf
If IsGadget(#G_Status)
SetGadgetText(#G_Status, "Clipboard refreshed.")
EndIf
EndProcedure
; ===================================================================
; Save routine for active tab (text or image)
; ===================================================================
Procedure SaveActiveTab()
Define tab = GetGadgetState(#G_Panel)
Define defName.STRING, enc.i
If DefaultSaveInfo(tab, @defName, @enc) = #False
MessageRequester("Save", "This tab cannot be saved.")
ProcedureReturn
EndIf
; Text tabs 0..6
If tab <= 6
Define content$ = ""
Select tab
Case 0 : content$ = gFormats$
Case 1 : content$ = gHtmlRaw$
Case 2 : content$ = gHtmlFrag$
Case 3 : content$ = gRtf$
Case 4 : content$ = gUnicode$
Case 5 : content$ = gAnsi$
Case 6 : content$ = gFiles$
EndSelect
If content$ = "" Or content$ = "Not available"
MessageRequester("Save", "No content on this tab.")
ProcedureReturn
EndIf
Define filter$ = "Text (*.txt)|*.txt|All files (*.*)|*.*"
If LCase(Right(defName\s, 4)) = ".rtf"
filter$ = "RTF (*.rtf)|*.rtf|Text (*.txt)|*.txt|All files (*.*)|*.*"
ElseIf LCase(Right(defName\s, 5)) = ".html"
filter$ = "HTML (*.html;*.htm)|*.html;*.htm|Text (*.txt)|*.txt|All files (*.*)|*.*"
EndIf
Define path$ = SaveFileRequester("Save content as...", DesktopFolder() + defName\s, filter$, 0)
If path$ = "" : ProcedureReturn : EndIf
If SaveTextFile(path$, content$, enc)
If IsGadget(#G_Status) : SetGadgetText(#G_Status, "Saved: " + path$) : EndIf
Else
MessageRequester("Save", "Could not save the file.")
EndIf
; Image tabs 7..8
Else
Define img.i = -1
If tab = 7 : img = gImgDIB : endif
If tab = 8 : img = gImgBMP : endif
If IsImage(img) = 0
MessageRequester("Save", "No image on this tab.")
ProcedureReturn
EndIf
Define filterImg$ = "PNG (*.png)|*.png|BMP (*.bmp)|*.bmp|JPEG (*.jpg;*.jpeg)|*.jpg;*.jpeg|All files (*.*)|*.*"
Define path2$ = SaveFileRequester("Save image as...", DesktopFolder() + defName\s, filterImg$, 0)
If path2$ = "" : ProcedureReturn : EndIf
If SaveImageByExt(img, path2$)
If IsGadget(#G_Status) : SetGadgetText(#G_Status, "Saved: " + path2$) : EndIf
Else
MessageRequester("Save", "Could not save the image.")
EndIf
EndIf
EndProcedure
; ===================================================================
; UI Setup
; ===================================================================
Define winW = 980, winH = 760
If OpenWindow(0, 0, 0, winW, winH, "Clipboard UI Inspector", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
PanelGadget(#G_Panel, 10, 10, winW - 20, winH - 110)
AddGadgetItem(#G_Panel, -1, "Formats")
EditorGadget(#G_EditFormats, 10, 10, GadgetWidth(#G_Panel) - 30, GadgetHeight(#G_Panel) - 50, #PB_Editor_ReadOnly)
AddGadgetItem(#G_Panel, -1, "HTML (raw)")
EditorGadget(#G_EditHtmlRaw, 10, 10, GadgetWidth(#G_Panel) - 30, GadgetHeight(#G_Panel) - 50, #PB_Editor_ReadOnly)
AddGadgetItem(#G_Panel, -1, "HTML (fragment)")
EditorGadget(#G_EditHtmlFrag, 10, 10, GadgetWidth(#G_Panel) - 30, GadgetHeight(#G_Panel) - 50, #PB_Editor_ReadOnly)
AddGadgetItem(#G_Panel, -1, "RTF")
EditorGadget(#G_EditRtf, 10, 10, GadgetWidth(#G_Panel) - 30, GadgetHeight(#G_Panel) - 50, #PB_Editor_ReadOnly)
AddGadgetItem(#G_Panel, -1, "Unicode Text")
EditorGadget(#G_EditUnicode, 10, 10, GadgetWidth(#G_Panel) - 30, GadgetHeight(#G_Panel) - 50, #PB_Editor_ReadOnly)
AddGadgetItem(#G_Panel, -1, "ANSI Text")
EditorGadget(#G_EditAnsi, 10, 10, GadgetWidth(#G_Panel) - 30, GadgetHeight(#G_Panel) - 50, #PB_Editor_ReadOnly)
AddGadgetItem(#G_Panel, -1, "Files")
EditorGadget(#G_EditFiles, 10, 10, GadgetWidth(#G_Panel) - 30, GadgetHeight(#G_Panel) - 50, #PB_Editor_ReadOnly)
AddGadgetItem(#G_Panel, -1, "DIB Image (CF_DIB)")
ImageGadget(#G_ImageDIB, 10, 10, GadgetWidth(#G_Panel) - 30, GadgetHeight(#G_Panel) - 50, 0)
AddGadgetItem(#G_Panel, -1, "Bitmap Image (CF_BITMAP)")
ImageGadget(#G_ImageBMP, 10, 10, GadgetWidth(#G_Panel) - 30, GadgetHeight(#G_Panel) - 50, 0)
CloseGadgetList()
; Bottom bar: buttons + status
ButtonGadget(#G_BtnRefresh, winW - 220, winH - 90, 90, 28, "Refresh")
ButtonGadget(#G_BtnSave , winW - 120, winH - 90, 90, 28, "Save...")
TextGadget(#G_Status, 10, winH - 85, winW - 250, 20, "", #PB_Text_Border)
; Initial load
RefreshClipboardAndUI()
; Event loop
Define evt, gad
Repeat
evt = WaitWindowEvent()
Select evt
Case #PB_Event_Gadget
gad = EventGadget()
Select gad
Case #G_BtnRefresh
RefreshClipboardAndUI()
Case #G_BtnSave
SaveActiveTab()
EndSelect
EndSelect
Until evt = #PB_Event_CloseWindow
EndIf
End