# A Simple Win32 Database, part two.

..adding a 'delete' button, and evading a 'bug'.

The following example is just a continuation of the last program. It adds a delete feature to the database, so that individual entries which have been selected in the listbox can be removed.

Please note that this example has modified the read-from-file procedure. This eliminates the need to create an empty file, if no file exists, by using a different test to determine if there is a file to read from, or not.

It has also been changed, with another test, so that it is no longer necessary to create a 'dummy' blank entry in the listbox.

..oops ! Our last program example uncovered a very strange 'bug'. If one entered an item of exactly 12 characters, saved it to the list, and then hi-lighted this item exactly twice, the program would crash. In order to get around this 'bug' we decided to insert a dummy space character if the line was 12 characters long, as a temporary cure. Though this bug may have been resolved by now, we've left it's 'cure' in this example ..;-) The 'fix' for this is in yellow.

--lists4.exw

    -- lists4.exw: by Ad Rienks
    -- Added a delete routine, contributed by Ferlin Scarborough
    -- with lot's of 'trickery', so put yer thinkin caps on!

    include win32lib.ew

    constant
      Win     = create(Window, "List +++", 0, Default, Default, 160, 220, 0),
      List1   = create(List, "", Win, 10, 10, 130, 58, LBS_NOINTEGRALHEIGHT),
      Label1  = create(LText, "", Win, 10, 75, 130, 20, 0),
      Sle1    = create(EditText, "", Win, 10, 96, 130, 20, 0),
      btnAdd  = create(PushButton, "Add", Win, 10, 126, 60, 25, 0),
      btnSave = create(PushButton, "Save", Win, 80, 126, 60, 25, 0),
      btnDel  = create(PushButton, "Delete", Win, 10, 156, 60, 25, 0)
      --  ^^ and the still newer Delete button.

    atom Handle -- declare 2 variables we use for file handling.
    object line

    procedure onLoad_Win( integer self, integer event, sequence params )
    -- from a file, this time, with a difference !
    -- Note that we use a completely different way to check for a file's
    -- existence here. Since (Handle) becomes -1 if the file does not
    -- exist, it will skip the 'read' entirely if this is true.
    integer count
      Handle = open("mydata2.txt", "r")     -- open the file,
      if Handle != -1 then                  -- and if successfully opened,
        while 1 do                          -- get each line,
            line = gets(Handle)
            if atom(line) then              -- until end of file.
                exit
            end if
            line = line[1..length(line)-1]  -- remove line-feed we add below,
            if length(line)=12 then
               line = line & ' '
            end if
            addItem( List1, line )          -- and "park" it in the listbox.
        end while
        close( Handle )
      end if
      -- check if the list is not empty
      -- Note that, because of a new check here, we no longer need
      -- a 'dummy' line in the list.
      count = getCount(List1)
      if count >= 0 then
          setIndex(List1, count)                  -- point to the last item.
          setText(Label1, getItem(List1, count))  -- show it also.
      end if
      setFocus(Sle1)
    end procedure   -- onLoad_Win

    procedure onChange_List1( integer self, integer event, sequence params )
    -- list item will be shown in the static line.
    integer what
        what = getIndex(List1)
        if what >= 0 then
            setText(Label1, getItem(List1, what))
        end if
    end procedure   -- onChange_List1

    procedure onClick_btnAdd( integer self, integer event, sequence params )
      integer junk
      sequence list_entry   list_entry = getText(Sle1)
      list_entry = getText(Sle1)
      if length( list_entry ) = 0 then
          junk = message_box("Type some text into the box first!",
                             "Empty entry!", MB_TASKMODAL + MB_ICONWARNING)
      else
          if length(entry) = 12 then        -- this is how we catch the bug,
              list_entry = list_entry & ' ' -- until a better solution is found.
          end if
          addItem(List1, list_entry)
          setText(Label1, list_entry)
          setText(Sle1, {})
          setIndex(List1, getCount(List1) )
      end if
      setFocus( Sle1 )
    end procedure   -- onClick_BtnAdd

    procedure onClick_btnSave( integer self, integer event, sequence params )
    -- to save our simple database to a file.
    integer listlen
      listlen = getCount(List1)           -- first, how many lines are in it ?
      if listlen > 0 then                 -- no use to save an empty database.
          Handle = open("mydata2.txt", "w")
          for i = 1 to listlen do
              line = getItem(List1,i)
              if length(line) = 13 then
                if equal(line[ 13 ], ' ') then  -- if 13th char. is a space,
                  line = line[1..12]            -- it's our 'fix', so remove it,
                end if
              end if
              line = line & '\n'      -- add a line-feed so we can read it, and,
              puts(Handle, line)      -- write the line to the file.
          end for
          close(Handle)
      end if
    end procedure   -- onClick_btnSave

    global procedure onClick_btnDel( integer self, integer event, sequence params )
    sequence lineitem
    integer itemnum, junk, counter

      if getCount(List1) > 0 then  -- skip routine if list is empty

    -- first, lets find out what we're 'trying' to delete
        itemnum = getIndex(List1)

        lineitem = getItem(List1, itemnum)

    -- start up a confirmation message box.
        junk = message_box("Are you sure you want to DELETE\n" & lineitem,
                           "Confirm Delete of Item!",
                           MB_ICONQUESTION + MB_YESNO + MB_TASKMODAL)
        if junk = IDYES then
    --  ^^ this line tests the results of the message_box function.
            junk = deleteItem(List1, itemnum)
            counter = getCount(List1)
            if counter > 0 then
              setIndex(List1, counter)
                setText(Label1, getItem(List1, counter))
            else
                setText(Label1, {} )
            end if
        end if
      end if
      setFocus(Sle1)
    end procedure   -- end of onClick_btnDel

    onOpen[Win] = routine_id("onLoad_Win")
    onChange[List1] = routine_id("onChange_List1")
    onClick[btnAdd] = routine_id("onClick_btnAdd")
    onClick[btnSave] = routine_id("onClick_btnSave")
    -- ^^ our latest event handler.

    WinMain(Win,Normal)

    -- end --

A 'note' about the _ NOW FIXED _ bug.

    ...the cause of the bug.

    -- an extract fron the Euphoria mailing list.

    From:    Robert Craig
    Subject: Re: Win32Lib bug

    I've found the cause of the 12-character string bug in win32lib.ew.

    In getItem() we have:

        iLength = sendMessage( id, msg, item, 0 )

        -- extra line I added:
        iLength = iLength+1  -- temporary fix,  - David?

    -- NEW! 0.14c
        if iLength = 0 then
            warnErr( "Get item text length is zero." )
            return ""
        end if

        -- allocate a buffer
        buffer = allocate( iLength )   -- not quite enough space!

      -- get the message, based on control type
      if      window_class[ id ] = LISTBOX then msg = LB_GETTEXT
      elsif   window_class[ id ] = COMBO   then msg = CB_GETLBTEXT
      end if

      -- move the text to a buffer
      ok = sendMessage( id, msg, item, buffer )

    I found that the buffer allocated above was one character too small.
    It did not allow for the 0 that C always adds to strings (but Euphoria
    doesn't require). As a result, Windows would write the extra 0, sometimes
    corrupting the next block of storage. If the string was an exact multiple
    of 4 in length (such as 12) then Euphoria would allocate exactly the
    number of bytes requested, but if it wasn't, Euphoria would round up
    to the next multiple of 4 bytes, thereby protecting against the extra 0.

    Maybe the fix should just be:
         buffer = allocate(iLength+1)
    instead. I don't know. David should consider it.
    There are other calls to SendMessage that should also be checked.

    Regards,
         Rob Craig
         Rapid Deployment Software
         http://members.aol.com/FilesEu/

    ------------------------------

    Date:    Fri, 20 Nov 1998 12:38:41 -0800
    From:    "Cuny, David"
    Subject: Re: Win32Lib bug

    Robert Craig wrote:

    > I've found the cause of the 12-character string
    > bug in win32lib.ew.
    ...
    > iLength = iLength+1

    When you mentioned that you changed allocate() and it got rid of the bug, I
    had a suspicion that under-allocated strings were the culprit - especially
    after I went through the structures and didn't see any more that seemed
    under-allocated.

    I'll look at the invocations of allot, allocate, fetch and store, and make
    sure they are all accounting for the extra byte.

    Thanks again!

    -- David Cuny

..end of lesson.

CONTENTS