Page 1 of 1

Clipboard UI Inspector

Posted: Tue Sep 09, 2025 2:28 pm
by dige
When you select and copy text in my browser, it is transferred to the clipboard as text and HTML. I didn't want to copy the text to Notepad, but rather the HTML. This is what happened:

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




Re: Clipboard UI Inspector

Posted: Wed Sep 10, 2025 9:54 am
by idle
Thanks, that'll be very handy.

Re: Clipboard UI Inspector

Posted: Wed Sep 10, 2025 11:01 am
by Axolotl
Thanks for sharing.

I only glanced at it briefly and didn't compile or try it out....
Are you sure that the following part works as it should?

Code: Select all

; 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.s, encodingOut.i)
  Select tab
    Case 0 : filenameOut = "clipboard_formats.txt" : encodingOut = #PB_UTF8   : ProcedureReturn #True
    Case 1 : filenameOut = "clip_html_raw.txt"     : encodingOut = #PB_Ascii  : ProcedureReturn #True
    Case 2 : filenameOut = "clip.html"             : encodingOut = #PB_UTF8   : ProcedureReturn #True
    Case 3 : filenameOut = "clip.rtf"              : encodingOut = #PB_Ascii  : ProcedureReturn #True
    Case 4 : filenameOut = "clip.txt"              : encodingOut = #PB_UTF8   : ProcedureReturn #True
    Case 5 : filenameOut = "clip_ansi.txt"         : encodingOut = #PB_UTF8   : ProcedureReturn #True
    Case 6 : filenameOut = "clip_files.txt"        : encodingOut = #PB_UTF8   : ProcedureReturn #True
    Case 7 : filenameOut = "clip_dib.png"          : encodingOut = -1         : ProcedureReturn #True
    Case 8 : filenameOut = "clip_bitmap.png"       : encodingOut = -1         : ProcedureReturn #True
  EndSelect
  ProcedureReturn #False
EndProcedure
; ......
Procedure SaveActiveTab()
  Define tab = GetGadgetState(#G_Panel)
  Define defName$, enc.i
  If DefaultSaveInfo(tab, defName$, enc) = #False           ; <==  defName$ = "", enc = 0 always .. CORRECT? 
    MessageRequester("Save", "This tab cannot be saved.")
    ProcedureReturn
  EndIf
; ..... 
My suggestion:

Code: Select all

; 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
; ......
Procedure SaveActiveTab()
  Define tab = GetGadgetState(#G_Panel)
;  Define defName$, enc.i
  Define defName.STRING, enc.i 
;  If DefaultSaveInfo(tab, defName$, enc) = #False
  If DefaultSaveInfo(tab, @defName, @enc) = #False
    MessageRequester("Save", "This tab cannot be saved.")
    ProcedureReturn
  EndIf
; ..... 
;   
; replace defName$ with defName\s  in all places  

Re: Clipboard UI Inspector

Posted: Wed Sep 10, 2025 12:21 pm
by Piero
Axolotl wrote: Wed Sep 10, 2025 11:01 amI only glanced at it briefly
Wow; this Forum also showcases sophisticated English! 👍 🥹
:mrgreen: ;)

…but it's very slow today… bots? (3129 users online)

Re: Clipboard UI Inspector

Posted: Fri Sep 12, 2025 8:01 am
by dige
Thx Axolotl. I have applied your bug fix in the initial post.
Axolotl wrote: Wed Sep 10, 2025 11:01 am [..]
My suggestion:
Maps a panel tab index to a sensible default filename + encoding
[..]

Re: Clipboard UI Inspector

Posted: Sat Sep 13, 2025 3:48 pm
by minimy
Thanks for share idle, This is very nice code. Some times clipboard is a nightmare depend you clip.