My simple, unoptimised blob database if anyone wants it. Or not.
Code: Select all
;- Window Constants
Enumeration 1
  #Window_Blobby
  #Window_BlobbyData
EndEnumeration
#WindowIndex = #PB_Compiler_EnumerationValue
;- Gadget Constants
Enumeration 1
  ; Window_Blobby
  #Gadget_Blobby_cBlobList
  #Gadget_Blobby_BlobList
  #Gadget_Blobby_cPicture
  #Gadget_Blobby_Picture
  #Gadget_Blobby_cDetails
  #Gadget_Blobby_cControl
  #Gadget_Blobby_Add
  #Gadget_Blobby_Delete
  #Gadget_Blobby_Edit
  #Gadget_Blobby_Save
  #Gadget_Blobby_cMessages
  #Gadget_Blobby_Details
  #Gadget_Blobby_iMessages
  #Gadget_Blobby_Messages
  #Gadget_Blobby_Exit
  ; Window_BlobbyData
  #Gadget_BlobbyData_cPicture
  #Gadget_BlobbyData_Picture
  #Gadget_BlobbyData_cDetails
  #Gadget_BlobbyData_Details
  #Gadget_BlobbyData_cControl
  #Gadget_BlobbyData_Save
  #Gadget_BlobbyData_Camera
  #Gadget_BlobbyData_Exit
  #Gadget_BlobbyData_Mode
  #Gadget_BlobbyData_Record
  #Gadget_BlobbyData_lMode
EndEnumeration
#GadgetIndex = #PB_Compiler_EnumerationValue
;- Image Constants
Enumeration 1
  #Image_Blobby_Add
  #Image_Blobby_Delete
  #Image_Blobby_Edit
  #Image_Blobby_Save
  #Image_Blobby_iMessages
  #Image_Blobby_Exit
  #Image_BlobbyData_Save
  #Image_BlobbyData_Camera
  #Image_BlobbyData_Exit
EndEnumeration
#ImageIndex = #PB_Compiler_EnumerationValue
;- Load Images
CatchImage(#Image_Blobby_Add,         ?_OPT_Blobby_Add)
CatchImage(#Image_Blobby_Delete,      ?_OPT_Blobby_Delete)
CatchImage(#Image_Blobby_Edit,        ?_OPT_Blobby_Edit)
CatchImage(#Image_Blobby_Save,        ?_OPT_Blobby_Save)
CatchImage(#Image_Blobby_iMessages,   ?_OPT_Blobby_iMessages)
CatchImage(#Image_Blobby_Exit,        ?_OPT_Blobby_Exit)
CatchImage(#Image_BlobbyData_Save,    ?_OPT_Blobby_Save)
CatchImage(#Image_BlobbyData_Camera,  ?_OPT_BlobbyData_Camera)
CatchImage(#Image_BlobbyData_Exit,    ?_OPT_Blobby_Exit)
DataSection
  _OPT_Blobby_Add         : IncludeBinary "Images\add32x32.ico"
  _OPT_Blobby_Delete      : IncludeBinary "Images\delete32x32.ico"
  _OPT_Blobby_Edit        : IncludeBinary "Images\edit32x32.ico"
  _OPT_Blobby_Save        : IncludeBinary "Images\save32x32.ico"
  _OPT_Blobby_iMessages   : IncludeBinary "Images\messages16x16.ico"
  _OPT_Blobby_Exit        : IncludeBinary "Images\exit32x32.ico"
  _OPT_BlobbyData_Camera  : IncludeBinary "Images\camera32x32.ico"
EndDataSection
Procedure.l Window_Blobby()
  If OpenWindow(#Window_Blobby, 52, 73, 805, 650, "View BLOB picture in database", #PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_ScreenCentered|#PB_Window_Invisible)
      SetWindowColor(#Window_Blobby, $A2A2A2)
      ContainerGadget(#Gadget_Blobby_cBlobList, 5, 5, 300, 545, #PB_Container_Flat|#PB_Container_BorderLess)
        SetGadgetColor(#Gadget_Blobby_cBlobList, #PB_Gadget_BackColor, $BFBFBF)
      ListIconGadget(#Gadget_Blobby_BlobList, 5, 5, 290, 535, "Picture name", 286, #PB_ListIcon_FullRowSelect|#PB_ListIcon_AlwaysShowSelection)
        SetGadgetColor(#Gadget_Blobby_BlobList, #PB_Gadget_BackColor, $DBDBDB)
        AddGadgetColumn(#Gadget_Blobby_BlobList, 1, "Record", 0)
        SetGadgetFont(#Gadget_Blobby_BlobList, LoadFont(#Gadget_Blobby_BlobList, "Comic Sans MS", 10, 0))
      CloseGadgetList()
      ContainerGadget(#Gadget_Blobby_cPicture, 310, 5, 490, 440, #PB_Container_Flat|#PB_Container_BorderLess)
        SetGadgetColor(#Gadget_Blobby_cPicture, #PB_Gadget_BackColor, $BFBFBF)
      ImageGadget(#Gadget_Blobby_Picture, 5, 5, 480, 430, 0)
      CloseGadgetList()
      ContainerGadget(#Gadget_Blobby_cDetails, 310, 450, 490, 100, #PB_Container_Flat|#PB_Container_BorderLess)
        SetGadgetColor(#Gadget_Blobby_cDetails, #PB_Gadget_BackColor, $BFBFBF)
      EditorGadget(#Gadget_Blobby_Details, 5, 5, 480, 90, #PB_Editor_ReadOnly|#PB_Editor_WordWrap)
        SetGadgetColor(#Gadget_Blobby_Details, #PB_Gadget_BackColor, $DBDBDB)
        SetGadgetFont(#Gadget_Blobby_Details, LoadFont(#Gadget_Blobby_Details, "Comic Sans MS", 10, 0))
      CloseGadgetList()
      ContainerGadget(#Gadget_Blobby_cControl, 5, 555, 795, 60, #PB_Container_Flat|#PB_Container_BorderLess)
        SetGadgetColor(#Gadget_Blobby_cControl, #PB_Gadget_BackColor, $BFBFBF)
      ButtonImageGadget(#Gadget_Blobby_Add, 5, 5, 45, 45, ImageID(#Image_Blobby_Add))
      ButtonImageGadget(#Gadget_Blobby_Delete, 50, 5, 45, 45, ImageID(#Image_Blobby_Delete))
      ButtonImageGadget(#Gadget_Blobby_Edit, 95, 5, 45, 45, ImageID(#Image_Blobby_Edit))
      ButtonImageGadget(#Gadget_Blobby_Save, 140, 5, 45, 45, ImageID(#Image_Blobby_Save))
      ButtonImageGadget(#Gadget_Blobby_Exit, 745, 5, 45, 45, ImageID(#Image_Blobby_Exit))
      CloseGadgetList()
      ContainerGadget(#Gadget_Blobby_cMessages, 5, 620, 795, 24, #PB_Container_Flat|#PB_Container_BorderLess)
        SetGadgetColor(#Gadget_Blobby_cMessages, #PB_Gadget_BackColor, $BFBFBF)
      ImageGadget(#Gadget_Blobby_iMessages, 5, 3, 16, 16, ImageID(#Image_Blobby_iMessages))
      StringGadget(#Gadget_Blobby_Messages, 25, 0, 765, 21, "", #PB_String_BorderLess)
        SetGadgetColor(#Gadget_Blobby_Messages, #PB_Gadget_BackColor, $BFBFBF)
        SetGadgetFont(#Gadget_Blobby_Messages, LoadFont(#Gadget_Blobby_Messages, "Comic Sans MS", 11, 0))
      CloseGadgetList()
      HideWindow(#Window_Blobby, 0)
    ProcedureReturn WindowID(#Window_Blobby)
  EndIf
EndProcedure
Procedure.l Window_BlobbyData()
  If OpenWindow(#Window_BlobbyData, 80, 75, 500, 630, "", #PB_Window_SystemMenu|#PB_Window_ScreenCentered|#PB_Window_Invisible, WindowID(#Window_Blobby))
      SetWindowColor(#Window_BlobbyData, $A2A2A2)
      ContainerGadget(#Gadget_BlobbyData_cPicture, 5, 5, 490, 440, #PB_Container_Flat|#PB_Container_BorderLess)
        SetGadgetColor(#Gadget_BlobbyData_cPicture, #PB_Gadget_BackColor, $BFBFBF)
      ImageGadget(#Gadget_BlobbyData_Picture, 5, 5, 480, 430, 0)
      CloseGadgetList()
      ContainerGadget(#Gadget_BlobbyData_cDetails, 5, 450, 490, 110, #PB_Container_Flat|#PB_Container_BorderLess)
        SetGadgetColor(#Gadget_BlobbyData_cDetails, #PB_Gadget_BackColor, $BFBFBF)
      EditorGadget(#Gadget_BlobbyData_Details, 5, 5, 480, 100, #PB_Editor_WordWrap)
        SetGadgetColor(#Gadget_BlobbyData_Details, #PB_Gadget_BackColor, $DBDBDB)
        SetGadgetFont(#Gadget_BlobbyData_Details, LoadFont(#Gadget_BlobbyData_Details, "Comic Sans MS", 10, 0))
      CloseGadgetList()
      ContainerGadget(#Gadget_BlobbyData_cControl, 5, 565, 490, 60, #PB_Container_Flat|#PB_Container_BorderLess)
        SetGadgetColor(#Gadget_BlobbyData_cControl, #PB_Gadget_BackColor, $BFBFBF)
      ButtonImageGadget(#Gadget_BlobbyData_Save, 5, 5, 45, 45, ImageID(#Image_BlobbyData_Save))
      ButtonImageGadget(#Gadget_BlobbyData_Camera, 50, 5, 45, 45, ImageID(#Image_BlobbyData_Camera))
      ButtonImageGadget(#Gadget_BlobbyData_Exit, 440, 5, 45, 45, ImageID(#Image_BlobbyData_Exit))
      StringGadget(#Gadget_BlobbyData_Mode, 105, 10, 145, 20, "", #PB_String_ReadOnly|#PB_String_BorderLess)
        SetGadgetColor(#Gadget_BlobbyData_Mode, #PB_Gadget_BackColor, $DBDBDB)
        SetGadgetColor(#Gadget_BlobbyData_Mode, #PB_Gadget_FrontColor, $FF0000)
        SetGadgetFont(#Gadget_BlobbyData_Mode, LoadFont(#Gadget_BlobbyData_Mode, "Comic Sans MS", 10, 0))
      StringGadget(#Gadget_BlobbyData_Record, 285, 10, 145, 20, "", #PB_String_ReadOnly|#PB_String_BorderLess)
        SetGadgetColor(#Gadget_BlobbyData_Record, #PB_Gadget_BackColor, $DBDBDB)
        SetGadgetColor(#Gadget_BlobbyData_Record, #PB_Gadget_FrontColor, $FF0000)
        SetGadgetFont(#Gadget_BlobbyData_Record, LoadFont(#Gadget_BlobbyData_Record, "Comic Sans MS", 10, 0))
      TextGadget(#Gadget_BlobbyData_lMode, 105, 35, 325, 15, " Current mode                                     Record number")
        SetGadgetColor(#Gadget_BlobbyData_lMode, #PB_Gadget_BackColor, $BFBFBF)
        SetGadgetFont(#Gadget_BlobbyData_lMode, LoadFont(#Gadget_BlobbyData_lMode, "Comic Sans MS", 8, 0))
      CloseGadgetList()
      HideWindow(#Window_BlobbyData, 0)
    ProcedureReturn WindowID(#Window_BlobbyData)
  EndIf
EndProcedure
; 
Declare   OpenSystemDatabase()                                                          ; Try to open the system database and create missing tables
Declare.s DatabaseLastInsertRowId()                                                     ; Get the ID of the last inserted record
; 
Declare.s ByteCalc(Byte.q, NbDecimals = 2)                                              ; AND51's correct file size calculation code
Declare   CloseMyWindow(WindowId.i)                                                     ; Fade the window out gently, close it and reset the mutex
Declare.s KillQuote(Instring.s)                                                         ; Kill double quotes in strings for display purposes
Declare   LastLine(Gadget.i, LineNumber.i)                                              ; Go to the last line of a ListIconGadget
Declare.s LastRecord(TableField.s, TableName.s)                                         ; Find the last record number in the database, not necessarily the last record though due to deletions (fang)
Declare.s RepQuote(Instring.s)                                                          ; Uncle Berikco's routine to properly replace single quotes with double for SQL passing
; 
Declare   ListPicturesMainForm()                                                        ; Load and display all pictures in the database
Declare   DisplayPictureMainForm()                                                      ; Show the picture for a selected record
; 
Declare   AddPicture()                                                                  ; Add a new picture to the database
Declare   DeletePictureFromDatabase()                                                   ; Delete a picture from the database
Declare   EditPicture()                                                                 ; Edit the currently selected picture
Declare   LoadPictureFromDisk()                                                         ; Load a picture from disk into the data form
Declare   SavePictureToDatabase()                                                       ; Save a new or edited picture to the database
Declare   SavePictureToDisk()                                                           ; Save a picture back to disk from the database
; Use for viewing various picture formats
UseJPEG2000ImageDecoder()
UseJPEGImageDecoder()
UsePNGImageDecoder()
UseTGAImageDecoder()
UseTIFFImageDecoder()
; Use the SQLite database environment
UseSQLiteDatabase()
  
; Keep track of various program parameters
Structure ProgramData
  QuitFlag.i
  MutexVal.i
  
  LastWindow.s
  
  DatabaseHandle.i
  DatabaseName.s
  
  CurrentDir.s
  CurrentLine.i
  CurrentRecord.s
  
  ImageWidth.i
  ImageHeight.i
  
  OldPictureName.s
  NewPictureName.s
EndStructure
; Database structure
Structure DatabaseData
  BlobData.s     ; 0
  BlobName.s     ; 1
  BlobPath.s     ; 2
  BlobSize.s     ; 3
  BlobDate.s     ; 4
  BlobComment.s  ; 5
  BlobRecord.s   ; 6
EndStructure
; Make sure the structure data is globally available
Global Program.ProgramData
Global Database.DatabaseData
; 
Program\CurrentDir    = GetCurrentDirectory()
Program\DatabaseName  = Program\CurrentDir + "Database\Blobby.db3"
; 
Program\ImageWidth    = 480
Program\ImageHeight   = 430
  
; 
Enumeration #ImageIndex
  #Image_Blobby_BlobList
EndEnumeration
; 
CatchImage(#Image_Blobby_BlobList, ?_OPT_Blobby_BlobList)
; 
DataSection
  _OPT_Blobby_BlobList  : IncludeBinary "Images\blob16x16.ico"
EndDataSection
; Get the ID of the last inserted record
Procedure.s DatabaseLastInsertRowId()
  If DatabaseQuery(Program\DatabaseHandle, "SELECT last_insert_rowid()")
    If FirstDatabaseRow(Program\DatabaseHandle)
      RecordId.s = GetDatabaseString(Program\DatabaseHandle, 0)
    EndIf
    FinishDatabaseQuery(Program\DatabaseHandle)
  Else
    RecordId.s = ""
  EndIf
  ProcedureReturn RecordId.s
EndProcedure
; Find the last record number in the database, not necessarily the last record though due to deletions (fang)
Procedure.s LastRecord(TableField.s, TableName.s)
  ; Always create long winded queries for readability
  SqlQueryString.s = "SELECT MAX(" + TableField.s + ") As LASTREC FROM " + TableName.s + ""
  If DatabaseQuery(Program\DatabaseHandle, SqlQueryString.s)
    While NextDatabaseRow(Program\DatabaseHandle)
      LastRecord.s = GetDatabaseString(Program\DatabaseHandle,  0)
    Wend
    FinishDatabaseQuery(Program\DatabaseHandle)
    ProcedureReturn LastRecord.s
  Else
    SetGadgetText(#Gadget_Blobby_Messages, "Failed to get the last record inserted: " + DatabaseError())
  EndIf  
EndProcedure
; Try to open the system database and create missing tables
Procedure OpenSystemDatabase()
  ; Create the database if it doesn't already exist.
  DbFileHandle.i = OpenFile(#PB_Any, Program\DatabaseName)
  If DbFileHandle.i
    CloseFile(DbFileHandle.i)
    Program\DatabaseHandle = OpenDatabase(#PB_Any, Program\DatabaseName, "", "")
    If Program\DatabaseHandle
      ; Turn on auto database vacuum
      If Not DatabaseUpdate(Program\DatabaseHandle, "PRAGMA auto_vacuum = on")
        SetGadgetText(#Gadget_Blobby_Messages, "Could not turn on automatic wasted space vacuuming." + DatabaseError())
      EndIf
      ; Write the Blobby table out
      SqlQueryString.s = "CREATE TABLE IF NOT EXISTS Blobby("
      SqlQueryString.s + "BlobData BLOB, BlobName Text, BlobPath Text, BlobSize Text, BlobDate Text, "
      SqlQueryString.s + "BlobComment Text, "
      SqlQueryString.s + "BlobRecord INTEGER PRIMARY KEY AUTOINCREMENT)"
      If Not DatabaseUpdate(Program\DatabaseHandle, SqlQueryString.s)
        SetGadgetText(#Gadget_Blobby_Messages, "Could not add missing tables to blobtest database." + DatabaseError())
      EndIf
      ; We had a problem getting the database handle
    Else
      MessageRequester("Database open error", "Error attempting to connect to system database.", #PB_MessageRequester_Ok)
    EndIf 
  Else
    MessageRequester("Database open error", "Could not open or create raw database file.", #PB_MessageRequester_Ok)
  EndIf
EndProcedure
; AND51's correct file size calculation code
Procedure.s ByteCalc(Byte.q, NbDecimals = 2)
  Protected Unit.b = Round(Log(Byte) / Log(1024), 0)
  ProcedureReturn StrD(Byte / Pow(1024, Unit), NbDecimals * (Unit & 1)) + " " + StringField("Byte,KB,MB,GB,TB,PB,EB", Unit + 1, ",")
EndProcedure
; Fade the window out gently, close it and reset the mutex
Procedure CloseMyWindow(WindowId.i)
  ; Close the requested form window if it is not the master window
  If WindowId.i <> #Window_Blobby
    ; Close the specified window
    CloseWindow(WindowId.i)
    ; Re-enable main window
    DisableWindow(#Window_Blobby, 0)
    ; Set focus on main window
    SetActiveWindow(#Window_Blobby)
    ; Activate the item list
    SetActiveGadget(#Gadget_Blobby_BlobList)
    ; Go to the previously selected line
    LastLine(#Gadget_Blobby_BlobList, Program\CurrentLine)
    ; Activate the previously selected line
    SetGadgetState(#Gadget_Blobby_BlobList, Program\CurrentLine)
    ; Trigger a change event on the selected line
    SetGadgetItemState(#Gadget_Blobby_BlobList, Program\CurrentLine, #PB_ListIcon_Selected)
    ; Clean up all loose variables
    Program\MutexVal        =  0                                                                    ; Make sure other windows can open now  
    Program\CurrentLine     = -1                                                                    ; Make sure no line can be selected accidentally
    Program\CurrentRecord   = ""                                                                    ; Nullify the current record number to avoid mistakes
    Program\LastWindow      = ""                                                                    ; Clear last used windows state
    Program\NewPictureName  = ""                                                                    ; Clear the new picture name on any exit
    Program\OldPictureName  = ""                                                                    ; Clear the old picture name on any exit
  EndIf
EndProcedure
; Kill double quotes in strings for display purposes
Procedure.s KillQuote(Instring.s)
  ProcedureReturn ReplaceString(Instring.s, "''", "'", 1, 1)
EndProcedure
; Go to the last line of a ListIconGadget
Procedure LastLine(Gadget.i, LineNumber.i)
  ; Make sure the current line is visible
  SendMessage_(GadgetID(Gadget.i), #LVM_ENSUREVISIBLE, LineNumber.i, 0)
EndProcedure
; Uncle Berikco's routine to properly replace single quotes with double for SQL passing
Procedure.s RepQuote(Instring.s)
  For i = 1 To Len(Instring.s)
    If Mid(Instring.s, i, 1) = "'"
      TemporaryString.s = TemporaryString.s + "''"
    Else
      TemporaryString.s = TemporaryString.s + Mid(Instring.s, i, 1)
    EndIf
  Next i
  ProcedureReturn TemporaryString.s
EndProcedure
; Show the picture for a selected record
Procedure DisplayPictureMainForm()
  ; Get the currently selected line
  Program\CurrentLine = GetGadgetState(#Gadget_Blobby_BlobList)
  ; Only proceed if there is a current line
  If  Program\CurrentLine <> -1
    ; Get the record number from the selected line
    Program\CurrentRecord = GetGadgetItemText(#Gadget_Blobby_BlobList, Program\CurrentLine, 1)
    ; Only proceed if there was a valid record number
    If Program\CurrentRecord
      SqlQueryString.s = "SELECT BlobData, BlobComment FROM Blobby WHERE BlobRecord = '" + Program\CurrentRecord + "'"
      If DatabaseQuery(Program\DatabaseHandle, SqlQueryString.s)
        While NextDatabaseRow(Program\DatabaseHandle)
          BlobImageSize.i   = DatabaseColumnSize(Program\DatabaseHandle, 0)
          If BlobImageSize.i
            *BlobbyBuffer = AllocateMemory(BlobImageSize)
            If *BlobbyBuffer
              If GetDatabaseBlob(Program\DatabaseHandle, 0, *BlobbyBuffer, BlobImageSize.i)
                ImageNumber.i = CatchImage(#PB_Any, *BlobbyBuffer)
                If ImageNumber.i
                  ResizeImage(ImageNumber.i, Program\ImageWidth, Program\ImageHeight, #PB_Image_Smooth)
                  SetGadgetState(#Gadget_Blobby_Picture, ImageID(ImageNumber.i))
                  SetGadgetText(#Gadget_Blobby_Details, KillQuote(GetDatabaseString(Program\DatabaseHandle, 1)))
                Else
                  SetGadgetText(#Gadget_Blobby_Messages, "Did not get a valid image handle to display this picture")
                EndIf
              Else
                SetGadgetText(#Gadget_Blobby_Messages, "Could not retrieve the blob data from the database")
              EndIf
              FreeMemory(*BlobbyBuffer)
            Else
              SetGadgetText(#Gadget_Blobby_Messages, "Could not allocate a buffer for the blob data")
            EndIf
          Else
            SetGadgetText(#Gadget_Blobby_Messages, "There is no image data stored in that recprd")
          EndIf
        Wend
        FinishDatabaseQuery(Program\DatabaseHandle)
      Else
        SetGadgetText(#Gadget_Blobby_Messages, "The database query failed: " + DatabaseError())
      EndIf
    Else
      SetGadgetText(#Gadget_Blobby_Messages, "Could not get a record number from the first line")
    EndIf
  Else
    SetGadgetText(#Gadget_Blobby_Messages, "No active line selected")
  EndIf
EndProcedure
; Load and display all pictures in the database
Procedure ListPicturesMainForm()
  SqlQueryString.s = "SELECT * FROM Blobby"
  If DatabaseQuery(Program\DatabaseHandle, SqlQueryString.s)
    While NextDatabaseRow(Program\DatabaseHandle)
;     BlobData.s    =  ?
      BlobName.s    =  KillQuote(GetDatabaseString(Program\DatabaseHandle, 1))
      BlobPath.s    =  KillQuote(GetDatabaseString(Program\DatabaseHandle, 2))
      BlobSize.s    =            GetDatabaseString(Program\DatabaseHandle, 3) 
      BlobDate.s    =            GetDatabaseString(Program\DatabaseHandle, 4) 
      BlobComment.s =            GetDatabaseString(Program\DatabaseHandle, 5) 
      BlobRecord.s  =            GetDatabaseString(Program\DatabaseHandle, 6) 
      AddGadgetItem(#Gadget_Blobby_BlobList, -1, BlobName.s + Chr(10) + BlobRecord.s, ImageID(#Image_Blobby_BlobList))
    Wend
    FinishDatabaseQuery(Program\DatabaseHandle)
  EndIf
  If CountGadgetItems(#Gadget_Blobby_BlobList) <> 0
    SetActiveGadget(#Gadget_Blobby_BlobList)
    SetGadgetState(#Gadget_Blobby_BlobList, 0)
  Else
    SetGadgetText(#Gadget_Blobby_Messages, "No items in the list to display pictures for")
  EndIf
EndProcedure
; Add a new picture to the database
Procedure AddPictureWindow()
  If Program\MutexVal <> 1
    If Window_BlobbyData()
      Program\MutexVal = 1
      DisableWindow(#Window_Blobby, 1)
      ; Get the last record number in the database and give the next probable record number
      NextRecord.i = Val(LastRecord("BlobRecord", "Blobby")) + 1      
      ; Setup the common window message
      Message.s = "Add New BlobTest Entry"
      Program\LastWindow = Message.s
      SetWindowTitle(#Window_BlobbyData, Message.s + "  -  (Record: " + Str(NextRecord.i) + " ?)")
      SetGadgetText(#Gadget_BlobbyData_Mode, "Add new picture")
      SetGadgetText(#Gadget_BlobbyData_Record, Str(NextRecord.i) + " ?")
    Else
      SetGadgetText(#Gadget_Blobby_Messages, "Could not open the blobby data window.")
    EndIf
  Else
    SetGadgetText(#Gadget_Blobby_Messages, "Another program window is already open, close that one first.")
  EndIf
EndProcedure
; Delete a picture from the database
Procedure DeletePictureFromDatabase()
  ; Get the currently selected line
  Program\CurrentLine = GetGadgetState(#Gadget_Blobby_BlobList)
  ; Only proceed if there is a current line
  If  Program\CurrentLine <> -1
    ; Get the record number from the selected line
    Program\CurrentRecord = GetGadgetItemText(#Gadget_Blobby_BlobList, Program\CurrentLine, 1)
    ; Only proceed if there was a valid record number
    If Program\CurrentRecord
      SqlQueryString.s = "DELETE FROM Blobby WHERE BlobRecord = '" + Program\CurrentRecord + "'"
      If DatabaseUpdate(Program\DatabaseHandle, SqlQueryString.s)
        RemoveGadgetItem(#Gadget_Blobby_BlobList, Program\CurrentLine)
        SetActiveGadget(#Gadget_Blobby_BlobList)
        SetGadgetState(#Gadget_Blobby_BlobList, Program\CurrentLine)
      Else
        SetGadgetText(#Gadget_Blobby_Messages, "Could not delete the selected picture from the database")
      EndIf
    Else
      SetGadgetText(#Gadget_Blobby_Messages, "No record number found on the selected line.")
    EndIf
  Else
    SetGadgetText(#Gadget_Blobby_Messages, "No active line selected")
  EndIf
EndProcedure
; Edit the currently selected picture
Procedure EditPictureWindow()
  ; Get the currently selected line
  Program\CurrentLine = GetGadgetState(#Gadget_Blobby_BlobList)
  ; Only proceed if there is a current line
  If  Program\CurrentLine <> -1
    ; Get the record number from the selected line
    Program\CurrentRecord = GetGadgetItemText(#Gadget_Blobby_BlobList, Program\CurrentLine, 1)
    ; Only proceed if there was a valid record number
    If Program\CurrentRecord
      If Program\MutexVal <> 1
        If Window_BlobbyData()
          Program\MutexVal = 1
          ; Setup the common window message
          Message.s = "Edit Old BlobTest Entry"
          Program\LastWindow = Message.s
          SetWindowTitle(#Window_BlobbyData, Message.s)
          SetGadgetText(#Gadget_BlobbyData_Mode, "Edit record mode")
          SqlQueryString.s = "SELECT * FROM Blobby WHERE BlobRecord = '" + Program\CurrentRecord + "'"
          If DatabaseQuery(Program\DatabaseHandle, SqlQueryString.s)
            While NextDatabaseRow(Program\DatabaseHandle)
              BlobImageSize.i = DatabaseColumnSize(Program\DatabaseHandle, 0)
              If BlobImageSize.i
                *BlobbyBuffer = AllocateMemory(BlobImageSize)
                If *BlobbyBuffer
                  If GetDatabaseBlob(Program\DatabaseHandle, 0, *BlobbyBuffer, BlobImageSize.i)
                    ImageNumber.i = CatchImage(#PB_Any, *BlobbyBuffer)
                    If ImageNumber.i
                      ResizeImage(ImageNumber.i, Program\ImageWidth, Program\ImageHeight, #PB_Image_Smooth)
                      SetGadgetState(#Gadget_BlobbyData_Picture, ImageID(ImageNumber.i))
                      BlobName.s      = KillQuote(GetDatabaseString(Program\DatabaseHandle, 1))
                      BlobPath.s      = KillQuote(GetDatabaseString(Program\DatabaseHandle, 2))
                      BlobSize.s      =           GetDatabaseString(Program\DatabaseHandle, 3) 
                      BlobDate.s      =           GetDatabaseString(Program\DatabaseHandle, 4) 
                      BlobComment.s   = KillQuote(GetDatabaseString(Program\DatabaseHandle, 5))
                      BlobRecord.s    =           GetDatabaseString(Program\DatabaseHandle, 6) 
                      SetGadgetText(#Gadget_BlobbyData_Details, BlobComment.s)
                      SetGadgetText(#Gadget_BlobbyData_Record, BlobRecord.s)
                      Program\OldPictureName = BlobPath.s + BlobName.s
                    Else
                      SetGadgetText(#Gadget_Blobby_Messages, "Did not get a valid image handle to display this picture")
                    EndIf
                  Else
                    SetGadgetText(#Gadget_Blobby_Messages, "Could not retrieve the blob data from the database")
                  EndIf
                  FreeMemory(*BlobbyBuffer)
                Else
                  SetGadgetText(#Gadget_Blobby_Messages, "Could not allocate a buffer for the blob data")
                EndIf
              Else
                SetGadgetText(#Gadget_Blobby_Messages, "There is no image data stored in that recprd")
              EndIf
            Wend
            FinishDatabaseQuery(Program\DatabaseHandle)
          Else
            SetGadgetText(#Gadget_Blobby_Messages, "The database query failed: " + DatabaseError())
          EndIf
        Else
          SetGadgetText(#Gadget_Blobby_Messages, "Could not open the edit picture window")
        EndIf
      Else
        SetGadgetText(#Gadget_Blobby_Messages, "Another program window is already open, close that one first.")
      EndIf
    Else
      SetGadgetText(#Gadget_Blobby_Messages, "No record number found on the selected line.")
    EndIf
  Else
    SetGadgetText(#Gadget_Blobby_Messages, "No active line selected")
  EndIf
EndProcedure
; Load a picture from disk into the data form
Procedure LoadPictureFromDisk()
  PictureToLoad.s = OpenFileRequester("Select picture", "", "Jpeg (*.jpg *.jpeg *.jfif)|*.jpg;*.jpeg;*.jfif|Bitmap (*.bmp)|*.bmp|Targa (*.tga)|*.tga|Tiff (*.tif)|*.tif|Png (*.png)|*.png|Icon (*.ico)|*.ico|All files (*.*)|*.*", 0)
  If PictureToLoad.s
    ImageNumber.i = LoadImage(#PB_Any, PictureToLoad.s)
    If ImageNumber.i
      ResizeImage(ImageNumber.i, Program\ImageWidth, Program\ImageHeight, #PB_Image_Smooth)
      SetGadgetState(#Gadget_BlobbyData_Picture, ImageID(ImageNumber.i))
      Program\NewPictureName = PictureToLoad.s
    Else
      SetGadgetText(#Gadget_Blobby_Messages, "Could not get the image handle, nothing loaded")
      Program\NewPictureName = ""
    EndIf
  Else
    SetGadgetText(#Gadget_Blobby_Messages, "Nothing to load, user cancelled the process")
  EndIf
EndProcedure
; Save a new or edited picture to the database
Procedure SavePictureToDatabase()
  ; BlobData.s                                                  The actual binary data to be stored
  BlobName.s    = RepQuote(GetFilePart(Program\NewPictureName))
  BlobPath.s    = RepQuote(GetPathPart(Program\NewPictureName))
  BlobSize.s    = ByteCalc(FileSize(BlobFile.s), 2)
  BlobDate.s    = FormatDate("%dd/%mm/%yyyy", GetFileDate(BlobFile.s, #PB_Date_Created))
  BlobComment.s = RepQuote(GetGadgetText(#Gadget_BlobbyData_Details))
; Record.s                                                    Automatically generated field
  Select Program\LastWindow
    Case "Add New BlobTest Entry"
      If Program\NewPictureName <> ""
        FileIn.i = ReadFile(#PB_Any, Program\NewPictureName)
        If FileIn.i
          FileInSize.i = Lof(FileIn.i)
          *Buffer = AllocateMemory(FileInSize.i)
          If *Buffer
            If ReadData(FileIn.i, *Buffer, FileInSize.i) = FileInSize.i
              SetDatabaseBlob(Program\DatabaseHandle, 0, *Buffer, FileInSize.i)
              SqlQueryString.s = "INSERT INTO Blobby ("
              SqlQueryString.s + "BlobData, BlobName, BlobPath, BlobSize, BlobDate, BlobComment) "
              SqlQueryString.s + "VALUES ("
              SqlQueryString.s + "?, '"                                  ; This is the picture data to be filled in from the buffer
              SqlQueryString.s + BlobName.s         + "', '"
              SqlQueryString.s + BlobPath.s         + "', '"
              SqlQueryString.s + BlobSize.s         + "', '"
              SqlQueryString.s + BlobDate.s         + "', '"
              SqlQueryString.s + BlobComment.s      + "'"
              SqlQueryString.s +                      ")"
              If DatabaseUpdate(Program\DatabaseHandle, SqlQueryString.s)
                NewRecord.s = DatabaseLastInsertRowId()
                If NewRecord.s
                  AddGadgetItem(#Gadget_Blobby_BlobList, -1, BlobName.s + Chr(10) + NewRecord.s, ImageID(#Image_Blobby_BlobList))
                  ; New record was inserted okay
                Else
                  SetGadgetText(#Gadget_Blobby_Messages, "Could not get the record number for this stored record???" + DatabaseError())
                EndIf
              Else
                SetGadgetText(#Gadget_Blobby_Messages, "The database insertion failed: " + DatabaseError())
              EndIf
            Else
              SetGadgetText(#Gadget_Blobby_Messages, "Could not read data from designated input file")
            EndIf
            FreeMemory(*Buffer)
          Else
            SetGadgetText(#Gadget_Blobby_Messages, "Could not allocate memory to the input buffer")
          EndIf
          CloseFile(FileIn.i)
        Else
          SetGadgetText(#Gadget_Blobby_Messages, "Could not open the designated file to read from")
        EndIf
      Else
        SetGadgetText(#Gadget_Blobby_Messages, "Nothing to save, user didn't choose a picture")
      EndIf
    Case "Edit Old BlobTest Entry"
      If Program\NewPictureName <> ""
        FileIn.i = ReadFile(#PB_Any, Program\NewPictureName)
        If FileIn.i
          FileInSize.i = Lof(FileIn.i)
          *Buffer = AllocateMemory(FileInSize.i)
          If *Buffer
            If ReadData(FileIn.i, *Buffer, FileInSize.i) = FileInSize.i
              SetDatabaseBlob(Program\DatabaseHandle, 0, *Buffer, FileInSize.i)
              SqlQueryString.s = "UPDATE Blobby SET "
              SqlQueryString.s + "BlobData = ?, "                                  ; This is the picture data to be filled in from the buffer
              SqlQueryString.s + "BlobName = '"         + BlobName.s            + "', '"
              SqlQueryString.s + "BlobName = '"         + BlobName.s            + "', '"
              SqlQueryString.s + "BlobPath = '"         + BlobPath.s            + "', '"
              SqlQueryString.s + "BlobSize = '"         + BlobSize.s            + "', '"
              SqlQueryString.s + "BlobDate = '"         + BlobDate.s            + "', '"
              SqlQueryString.s + "BlobComment = '"      + BlobComment.s         + "' "
              SqlQueryString.s + "WHERE BlobRecord = '" + Program\CurrentRecord + "'"
              If DatabaseUpdate(Program\DatabaseHandle, SqlQueryString.s)
                SetGadgetText(#Gadget_Blobby_Messages, "The changed record was saved back to the database.")
              Else
                SetGadgetText(#Gadget_Blobby_Messages, "The database record update failed: " + DatabaseError())
              EndIf
            Else
              SetGadgetText(#Gadget_Blobby_Messages, "Could not read data from designated input file")
            EndIf
            FreeMemory(*Buffer)
          Else
            SetGadgetText(#Gadget_Blobby_Messages, "Could not allocate memory to the input buffer")
          EndIf
          CloseFile(FileIn.i)
        Else
          SetGadgetText(#Gadget_Blobby_Messages, "Could not open the designated file to read from")
        EndIf
      Else
        SqlQueryString.s = "UPDATE Blobby SET "
        SqlQueryString.s + "BlobComment = '"      + BlobComment.s         + "' "
        SqlQueryString.s + "WHERE BlobRecord = '" + Program\CurrentRecord + "'"
        If DatabaseUpdate(Program\DatabaseHandle, SqlQueryString.s)
          SetGadgetText(#Gadget_Blobby_Messages, "The changed comment was saved back to the database.")
        Else
          SetGadgetText(#Gadget_Blobby_Messages, "The database comment update failed: " + DatabaseError())
        EndIf
      EndIf
  EndSelect
  CloseMyWindow(#Window_BlobbyData)
EndProcedure
; Save a picture back to disk from the database
Procedure SavePictureToDisk()
  ; Get the currently selected line
  Program\CurrentLine = GetGadgetState(#Gadget_Blobby_BlobList)
  ; Only proceed if there is a current line
  If  Program\CurrentLine <> -1
    ; Get the record number from the selected line
    Program\CurrentRecord = GetGadgetItemText(#Gadget_Blobby_BlobList, Program\CurrentLine, 1)
    ; Only proceed if there was a valid record number
    If Program\CurrentRecord
      SqlQueryString.s = "SELECT * FROM Blobby WHERE BlobRecord = '" + Program\CurrentRecord + "'"
      If DatabaseQuery(Program\DatabaseHandle, SqlQueryString.s)
        While NextDatabaseRow(Program\DatabaseHandle)
          BlobImageSize.i   = DatabaseColumnSize(Program\DatabaseHandle, 0)
          If BlobImageSize.i
            *BlobbyBuffer = AllocateMemory(BlobImageSize)
            If *BlobbyBuffer
              If GetDatabaseBlob(Program\DatabaseHandle, 0, *BlobbyBuffer, BlobImageSize.i)
                BlobColumnSize.i  = DatabaseColumnSize(Program\DatabaseHandle, 0) 
                BlobName.s        = KillQuote(GetDatabaseString(Program\DatabaseHandle, 1))
                BlobPath.s        = KillQuote(GetDatabaseString(Program\DatabaseHandle, 2))
                BlobSize.s        =           GetDatabaseString(Program\DatabaseHandle, 3) 
                BlobDate.s        =           GetDatabaseString(Program\DatabaseHandle, 4) 
                BlobComment.s     = KillQuote(GetDatabaseString(Program\DatabaseHandle, 5))
                BlobRecord.s      =           GetDatabaseString(Program\DatabaseHandle, 6) 
                BlobFileId.i      = CreateFile(#PB_Any, Program\CurrentDir  + BlobName.s)
                If BlobFileId.i
                  WriteData(BlobFileId.i, *BlobbyBuffer, BlobColumnSize.i)
                  SetGadgetText(#Gadget_Blobby_Messages, "We saved: " + BlobName.s  + " to disk")
                  CloseFile(BlobFileId.i)
                Else
                  SetGadgetText(#Gadget_Blobby_Messages, "We didn't get the image back from the blob buffer")
                EndIf
              Else
                SetGadgetText(#Gadget_Blobby_Messages, "Could not retrieve the blob data from the database")
              EndIf
              FreeMemory(*BlobbyBuffer)
            Else
              SetGadgetText(#Gadget_Blobby_Messages, "Could not allocate a buffer for the blob data")
            EndIf
          Else
            SetGadgetText(#Gadget_Blobby_Messages, "There is no image data stored in that record")
          EndIf
        Wend
        FinishDatabaseQuery(Program\DatabaseHandle)
      Else
        SetGadgetText(#Gadget_Blobby_Messages, "The database query failed: " + DatabaseError())
      EndIf
    Else
      SetGadgetText(#Gadget_Blobby_Messages, "No record number found on the selected line.")
    EndIf
  Else
    SetGadgetText(#Gadget_Blobby_Messages, "No active line selected")
  EndIf
EndProcedure
; Main program event handler
If Window_Blobby()
  Program\QuitFlag = 0
  OpenSystemDatabase()
  ListPicturesMainForm()
  SetGadgetText(#Gadget_Blobby_Messages, "Hello, Blobby is ready to work!")
  Repeat
    EventID  = WaitWindowEvent()
    MenuID   = EventMenu()
    GadgetID = EventGadget()
    WindowID = EventWindow()
    Select EventID
      Case #PB_Event_CloseWindow
        Select WindowID
          Case #Window_Blobby                       : Program\QuitFlag = 1
          Case #Window_BlobbyData                   : CloseMyWindow(#Window_BlobbyData)
        EndSelect
      Case #PB_Event_Gadget
        Select GadgetID
          Case #Gadget_Blobby_BlobList
            Select EventType()
              Case #PB_EventType_LeftDoubleClick    : EditPictureWindow()
              Case #PB_EventType_RightDoubleClick   : AddPictureWindow()
              Case #PB_EventType_Change             : DisplayPictureMainForm()
            EndSelect
            ; Main form controls
          Case #Gadget_Blobby_Add                   : AddPictureWindow()
          Case #Gadget_Blobby_Delete                : DeletePictureFromDatabase()
          Case #Gadget_Blobby_Edit                  : EditPictureWindow()
          Case #Gadget_Blobby_Save                  : SavePictureToDisk()
          Case #Gadget_Blobby_Exit                  : Program\QuitFlag = 1
          ; Data form controls
          Case #Gadget_BlobbyData_Save              : SavePictureToDatabase()
          Case #Gadget_BlobbyData_Camera            : LoadPictureFromDisk()
          Case #Gadget_BlobbyData_Exit              : CloseMyWindow(#Window_BlobbyData)
        EndSelect
    EndSelect
  Until Program\QuitFlag
  CloseWindow(#Window_Blobby)
EndIf
End