---------------------------------------------------------
The Liberty Basic Newsletter - Issue #62 - JAN 2000
       2000, Cliff Bros and Alyce Watson
             All Rights Reserved

Brosco and Alyce have written a Book for Liberty BASIC,
which is available in electronic form on a CDROM.
For details:  http://alyce.50megs.com/sss/cd.htm
---------------------------------------------------------
In this issue:

    Version checking

    Reading a file header

    Loading and displaying bitmaps in the
    open source editor - part five

    Copy and move any file

In future issues:
    A new way to use the filedialog
    Playing wavs
    Using ascii characters in commands
    the FILES command
    writing and using an ini file
    writing output 'code'
---------------------------------------------------------
As always, comments, questions, requests and corrections
are most welcome.  We learn through discussion, and by
helping one another!
---------------------------------------------------------
VERSION CHECKING

Since Carl Gundel has made the alpha for Liberty BASIC
version 2.0 available to us, we need to take care about
version checking in our programs.  Really, we should have
been doing this all along, because new versions of LB
provided functions not available in previous versions, so
often programs would not run in older versions of LB.

The open source editor will not work properly in LB v2.0.
The way that the fonts are sized has changed, probably
permanently, so font commands will not be backwards
compatible.  

According to Carl:  "The font sizing is based on point size. 
LB v1.x uses pixels. I'm not sure what to do with that, but 
I'll do my best to make it compatible. Backwards compatibility 
is important to me, but I may need to make some compromises."


In our editor, this means that the font size
we have chosen that makes small button captions in v1.42,
makes much larger characters and the captions no longer
fit on the buttons when used in v2.0.  The new font sizing 
is actually better in keeping with the way fonts are sized 
in other Windows applications, such as word processors.

In versions up to v1.42, we needed to list a graphicbox
command first before we listed other controls in our window
setup.  If a graphicbox was listed after a button, but in
the same location, the graphicbox would obscure the button.
This is not true in v2.0, AND listing the graphicbox first
makes the button unusable!  For v2.0 programs we MAY need
to make a change, and list the graphicbox after the other
controls.  (Remember, everything is subject to change in
version 2.0 -- it is still in its alpha stage!)

Version$ contains the version of Liberty BASIC being run.
print Version$   would produce:  1.42
If we want to insure that Version$ is ONLY 1.42:
if Version$="1.42" then {BASIC code}
Version$ is a string variable.  To evaluate it, you may need
to use the val() function, as in this example:
if val(Version$) < 2 then {BASIC code}

We'll try to remember to do a version check in our programs
from now on.  I suspect we will devote a newsletter to
updating programs to be v2.0 compliant, when v2.0 goes into
beta release.  Here's the version check.  If the version
of LB is greater than 1.42, the program execution is
aborted:

'** this program will not work in LBv2.0!
    if val(Version$)>1.42 then
        notice "This program will not work in LBv2.0!"
        end
    end if

---------------------------------------------------------
READING A FILE HEADER

The first section of a bitmap file contains information
about the bitmap.  Try opening a bitmap file in Notepad.
The first two caracters will be "BM".  This will be followed
by a bunch of characters; some of them will be recognizable,
some will be odd-looking, and many will be unprintable.
These characters are evaluated according to their ascii
values.  Try this little routine, being sure the path
and filename are valid on your system.  It reads the ascii
values of the first 29 characters of a bmp file and prints
them in the main window.

open "c:\lb142w\bmp\copy.bmp" for input as #b
b$=input$(#b, 29)
close #b 

for i=1 to len(b$)
print asc(mid$(b$,i,1))
next i

You will get a list that begins like this:
66
77
242
1
0
0
0
0
0
0
118
0
0
0
40
0
0
0
25
0
0
0
25
0
0
0
1
0
4

Look at the first two characters.  66 is the ascii value 
for "B" and 77 is the one for "M".  We can read part of
this file (the first 29 bytes will be enough) to retrieve
information about this bitmap.  

The width of the bitmap is contained in characters 19 & 20.
Together, they form a "word" value that can be interpreted
by Windows programs.  We can make sense of this by
evaluating the ascii values for characters 19 & 20 in the 
proper way.

Character 20 will be the "hibyte" when used by api calls.
A "word" value consists of a "hibyte" and a "lowbyte".  
The hibyte portion of a "word" is evaluated by multiplying 
it by 256.  Character 19 will be the "lobyte" part of the 
"word" value.  The two values are added together to get a
value for the bitmap width.  Pseudo code:

asc(char 19) + {asc(char 20) * 256} = width

In our sample, above, the values are
char 19 = 25
char 20 = 0
25 + (0*256) = 25
Our little bitmap has a width of 25 pixels.  

If instead, 
char 19 = 10 
char 20 = 2
then the bitmap width would be:
10 + (2*256) = 522

Height is evaluated in exactly the same way, but
using characters 23 and 24, in pseudo code:
asc(char23) + {asc(char 24) * 256} = height
In our sample above:
char 23 = 25
char 24 = 0
bitmap height = 25 + (0*256) = 25

In our editor, we will add a bitmap previwer tool.
When the user chooses a bitmap file, we'll read the
header to determine information about the bitmap.
Here is the routine we'll use to get the bitmap
dimensions.  We'll open the file and read in the
first 29 bytes.  To get a value for character 19,
we'll use the mid$() function, reading the header
string, pic$ at char 19, for a length of 1:
asc(mid$(pic$,19,1))
We'll get a value for characters, 20, 23 and 24 
in the same way.  Here is is:

[openbmp]
    filedialog "Open Bitmap","*.bmp", picFile$

    open picFile$ for input as #pic
    pic$=input$(#pic,29)
    close #pic

    bmpwidth = asc(mid$(pic$,19,1)) + (asc(mid$(pic$,20,1)) * 256)
    bmpheight = asc(mid$(pic$,23,1)) + (asc(mid$(pic$,24,1)) * 256)
---------------------------------------------------------
LOADING AND DISPLAYING BITMAPS IN THE OPEN SOURCE EDITOR

We have an even more important bit of information
to check, though.  Since LB v1.42 and below can load
bitmaps that have 256 or fewer colors, we will have a
program crash if we try to load 16,000,000 color bitmaps.
We can find out how many colors are in the bitmap format
by checking the color depth from the file header, as we
did for the width and height.  The color depth will be
contained in character 29, so we can get its value like this:

    picDepth=asc(right$(pic$,1))

In our earlier sample, this value is 4.  To determine the
number of colors in a bitmap from its depth, we take 2 to
a power of picDepth:

    picColors=2^picDepth

In our sample
picColors = 2 ^ 4 = 16

Our bitmap is in 16-color format.  Liberty BASIC 1.42 can
load bitmaps that have 256 or fewer colors.  The color
depth that corresponds to 256 colors is 8.  In our bitmap
previewer, if the depth is greater than 8, that means that
there are more than 256 colors in the bitmap, and Liberty
BASIC cannot load it.  If that is the case, we'll give the
user the chance to load it and view it in PaintBrush.  To
run PaintBrush with a bitmap loaded:
  run "pbrush "+picFile$ 

The complete code for the bitmap previewer is at the end of
this newsletter.  The complete code for the updated Open Source
Editor is attached to the newsletter, since it is getting to
be quite lengthy.  Here is a routine that traps bitmaps that
are formatted to more than 256 colors:

    if picColors > 256 then
        message$="The chosen bitmap has more than 256 colors, "+_
            "so Liberty BASIC cannot load it.  Would you like to "+_
            "view it with Paintbrush?"
        confirm message$;answer$
        if answer$="yes" then
            run "pbrush "+picFile$ 
            picFile$=""
        end if
    end if

If we find that the bitmap can be loaded in Liberty BASIC, 
then we load and display it.  We will want to set a flag
if we have loaded a bitmap, so we can call UNLOADBMP and
free the memory consumed by the previous bitmap.  Let's call
the flag bmploaded, and set it to 1 when we have loaded a bmp:

    bmploaded=1

If a bmp was loaded previously, let's unload it, then load the
bmp that was chosen (picFile$) as "tempPicture".

    if bmploaded=1 then unloadbmp "tempPicture"
    loadbmp "tempPicture", picFile$

Since we know the bitmap width and height, we can center it
within our graphicbox.  Our graphicbox has been created with
a width of 410 and a height of 200:

    graphicbox #bit.box1, 20,100,410,200

To get the x location to draw the bmp centered, let's check 
the difference between the width of the graphicbox and the width
of the bitmap.  Since we want equal parts of the graphicbox to
show on both sides of our bmp to center it, we'll divide the
difference by 2.  We'll do the same thing with the height values
for the bmp and the graphicbox to determine the proper y location
for the upper left corner of the bmp.  Then we'll DRAWBMP at that
X, Y location.  Remember that variables must be placed outside
of quote marks in commands:

    xbmp=int((410-bmpwidth)/2)
    ybmp=int((200-bmpheight)/2)

    print #bit.box1, "cls"
    print #bit.box1, "drawbmp tempPicture ";xbmp;" ";ybmp
    print #bit.box1, "flush"
    goto [loop]

Notice that we first cleared the screen with CLS.  This accomplished
two things.  It freed the memory from any previous FLUSH commands,
and it removed any previous bitmaps from the graphicbox display, so
that only the current bitmap will show.  Then, we issue a FLUSH
command, so that the bitmap will be redrawn if the window is obscured
and restored.

We'll set up our bitmap previewer code by listing some new variables
to use in the bmp routines, and by adding a Bitmap Preview item to
the Tools Menu.

'picFile$           filename for opened bitmap
'pic$               string to hold bitmap file header
'savebmpfile$       name to save bmp AS
'picDepth           color depth of bitmap
'picCols            number of colors in bitmap
'bmpwidth           width of bitmap
'bmpheight          height of bitmap
'xbmp               x location to draw bitmap in graphibox
'ybmp               y location to draw bitmap in graphibox
'bmploaded          flag to set when a bitmap is loaded
'message$           string variable to hold notice and confirm messages

menu #1, "&Tools",_
    "&Branch Labels",[branchlabels],_
    "Bit&map Preview",[bmp]


When the routine is called, we'll open a window that contains a 
graphicbox to display the bitmap, some buttons for user interaction,
and a textbox to display the bitmap name, and another textbox to
display the bitmap dimensions.

[bmp] '** BITMAP PREVIEWER
    WindowWidth=451:WindowHeight=350

    button #bit.open, "Open",   [openbmp],  UL,350,5,80,26
    button #bit.save, "Save As",[savebmp],  UL,350,35,80,26
    button #bit.exit, "Exit",   [closebit], UL,350,65,80,26

    textbox #bit.t1, 20,35,300,26
    textbox #bit.t2, 20,65,300,26

    statictext #bit.s, "Bitmap File:",20,5,150,20
    open "Bitmap Preview" for dialog_modal as #bit
    print #bit, "trapclose [closebit]"

We are using the ctl3d.dll (see Newsletter #61) to
make our dialog windows look better, so let's go the
extra distance and issue font commands to our controls
to make them look a bit better.

    print #bit.t1,   "!font Times_New_Roman 0 16"
    print #bit.t2,   "!font Times_New_Roman 0 16"
    print #bit.save, "!font Times_New_Roman 0 16"
    print #bit.open, "!font Times_New_Roman 0 16"
    print #bit.exit, "!font Times_New_Roman 0 16"
    print #bit.s,    "!font Times_New_Roman 0 16"

When we close our bitmap previewer, we should free the memory
used, by issuing CLS and by unloading the current bitmap.  We
also need to set the flag that indicates a bitmap is loaded
back to 0, since the user may want to call on the bitmap
previewer again.

[closebit]
    print #bit.box1, "cls"
    close #bit
    if bmploaded=1 then unloadbmp "tempPicture"
    bmploaded=0
    goto [loop]
---------------------------------------------------------
COPY AND MOVE ANY FILE

Did you notice that we included a button called SAVE AS?

    button #bit.save, "Save As",[savebmp],  UL,350,35,80,26

We can copy any type of file, by opening it for INPUT, reading 
it into a string variable, then closing the file.  We then 
open a file with a different name for OUTPUT and write the 
string to that file.  Voila, we have copied and moved a file!

    open picFile$ for input as #pf
        pic$=input$(#pf, lof(#pf))
    close #pf

    filedialog "Save As..","*.bmp",savebmpfile$ 
    if savebmpfile$="" then [loop]

In the routine above, we've opened picFile$ for INPUT, and
read the contents into the string variable pic$.  Then we've
closed the file.  Next, we get a name from the user to save the
file AS.  If the user doesn't specify a name, we abort the
procedure and goto [loop]

If there is a valid name for the saved file, we open it for
OUTPUT, write the string pic$ to the file, then close it.

    open savebmpfile$ for output as #bpf
        print #bpf, pic$
    close #bpf

---------------------------------------------------------
BITMAP PREVIEW CODE:

'** NEW **
[bmp] '** BITMAP PREVIEWER
    WindowWidth=451:WindowHeight=350

    button #bit.open, "Open",   [openbmp],  UL,350,5,80,26
    button #bit.save, "Save As",[savebmp],  UL,350,35,80,26
    button #bit.exit, "Exit",   [closebit], UL,350,65,80,26

    textbox #bit.t1, 20,35,300,26
    textbox #bit.t2, 20,65,300,26

    graphicbox #bit.box1, 20,100,410,200
    statictext #bit.s, "Bitmap File:",20,5,150,20
    open "Bitmap Preview" for dialog_modal as #bit
    print #bit, "trapclose [closebit]"

    print #bit.t1,   "!font Times_New_Roman 0 16"
    print #bit.t2,   "!font Times_New_Roman 0 16"
    print #bit.save, "!font Times_New_Roman 0 16"
    print #bit.open, "!font Times_New_Roman 0 16"
    print #bit.exit, "!font Times_New_Roman 0 16"
    print #bit.s,    "!font Times_New_Roman 0 16"


    goto [loop]

[closebit]
    print #bit.box1, "cls"
    close #bit
    if bmploaded=1 then unloadbmp "tempPicture"
    bmploaded=0
    goto [loop]

[savebmp]
    if picFile$="" then
        notice "It is not possible to SAVE.  No Bitmap has been chosen."
        goto [loop]
    end if

    open picFile$ for input as #pf
        pic$=input$(#pf, lof(#pf))
    close #pf

    filedialog "Save As..","*.bmp",savebmpfile$ 
    if savebmpfile$="" then [loop]

    cursor hourglass
    open savebmpfile$ for output as #bpf
        print #bpf, pic$
    close #bpf
    cursor normal
    notice "Bitmap saved as "+savebmpfile$ 
    goto [loop]


[openbmp]
    filedialog "Open Bitmap","*.bmp", picFile$
    if picFile$ = "" then [loop]

    print #bit.t1, picFile$

    open picFile$ for input as #pic
    pic$=input$(#pic,29)
    close #pic
    picDepth=asc(right$(pic$,1))
    picColors=2^picDepth

    bmpwidth = asc(mid$(pic$,19,1)) + (asc(mid$(pic$,20,1)) * 256)
    bmpheight = asc(mid$(pic$,23,1)) + (asc(mid$(pic$,24,1)) * 256)

    if picColors > 256 then
        print #bit.t2, ""
        print #bit.t1, ""
        print #bit.box1, "cls"
        message$="The chosen bitmap has more than 256 colors, "+_
            "so Liberty BASIC cannot load it.  Would you like to "+_
            "view it with Paintbrush?"
        confirm message$;answer$
        if answer$="yes" then
            run "pbrush "+picFile$ 
            picFile$=""
            goto [closebit]
            else
            picFile$=""
            goto [loop]
          end if
    end if

    if bmploaded=1 then unloadbmp "tempPicture"
    loadbmp "tempPicture", picFile$
    bmploaded=1

    print #bit.t2, "Width:  ";bmpwidth;"    Height:  ";bmpheight;""

    xbmp=int((410-bmpwidth)/2)
    ybmp=int((200-bmpheight)/2)

    print #bit.box1, "cls"
    print #bit.box1, "drawbmp tempPicture ";xbmp;" ";ybmp
    print #bit.box1, "flush"
    goto [loop]
---------------------------------------------------------
Newsletter compiled and edited by: Brosco and Alyce.
Comments, requests or corrections: Hit 'REPLY' now!
mailto:brosco@orac.net.au or mailto:awatson@wctc.net
---------------------------------------------------------


