---------------------------------------------------------
The Liberty Basic Newsletter - Issue #70 - APR 2000
       2000, Cliff Bros and Alyce Watson
             All Rights Reserved
---------------------------------------------------------
In this issue:
	Install Maker -- a review by Gary Capps
	More on user-defined functions
		new functions and explanations
		functions in functions, and as parameters
		adding more functions to our editor

	Creating bmpbuttons at runtime -
		many ways!

	Adding a bmpbutton to the open source editor

In future issues:
    Writing output 'code'
    Changing the runtime icon
    Launching the default web browser and email client
    Fun with the caret
---------------------------------------------------------
This newsletter is packed with good stuff.  Some of it
came about because of questions or comments from members.

This forum belongs to all of us, and suggestions, comments
and submissions of articles are welcomed and desired.
Please participate!
---------------------------------------------------------
Gary Capps has written a very thorough review of a neat,
freeware installation program called Install Maker.
Thanks, Gary!!

Get Install Maker here:  http://www.clickteam.com
---------------------------------------------------------
INSTALL MAKER, A REVIEW BY GARY CAPPS

    Install Maker is a complete installer program that offers 
the programmer all of the features that he needs to move large 
programs, with lots of attached files, back and forth between 
friends and co-workers.  But, it also offers features that will 
appeal to the professional who is trying to distribute his 
program across the net. All of this out of a free program. That's 
right, free. But we all know what free means. It means you do something 
for me and I'll give you something free. But for the tradeoff I think 
that it's pretty close to being free. At the end of the install, right 
before the program ends, there is a window that gives the user the 
option to visit the website of the 'ClickTeam", the authors of the 
program.

The option to use or not use a wizard is what greets you when the 
program is loaded and running. The wizard is so easy to use that I 
choose to use it up until I get ready to actually build the installer 
file. The wizard starts by asking simply to select the directory that 
contains your files. You are also given the option to include any 
subdirectories that may be in your main directory. A handy browse button 
is available at all places when one is asked for files. Next you are asked 
to name your product and the language for your chosen install. The name 
that you give at this stage will be the name that appears as your install 
icon, not necessarily the name of your program.  At this point a preview 
button is introduced, it allows you to see what the install is looking like 
at this stage. This feature is used throughout the rest of the program to 
allow you to see what you are actually building. You are always given the 
opportunity to go back and redo if you don't like what you see. Then comes 
the selection of .exe files to be represented by an icon that the program 
creates, and then adds to your start menu. You are also given the 
opportunity to have a shortcut to your desktop if you so choose. The 
.exe file usually refers to the run.exe that you have renamed to match 
your .tkn file (you did make that .tkn file didn't you?). Again, if you 
don't want to name your icons the same as your program, now is where 
you can make that decision. The next option in the process is the information 
window. This is where you input any license information, special installing 
instructions, or any pertinent information that you want your user to view. 
You don't have to worry about amount of information, because it is presented 
in a window with scroll bar to access loads of information. 

At this time it will be convenient to have a few graphics ready to be used 
in the install. Specifically if you have some sort of logo or icon that you 
have created for your program, then you need to have that BMP resized to be 
128x280. This BMP will be used in the install program itself and is a good 
place to do a little promotion for yourself, your product, your web site, and 
your programming language... You will also need a bitmap to be used as your 
background wallpaper if you choose the full-window style of install. I created 
a BMP with a gradient fill in the colors that I thought were more in the 
style of my program, rather than using the included blue color that they use 
as the default background. The only thing to remember is that this BMP is tiled 
if it doesn't fit in the window, and it needs to be 16 or 256 colors, no 
millions of colors here. If you have a readme type document that you want 
to be read before exiting the installer, add it here. For ease of use try 
not to use an application specific file but rather use a .txt or .doc type 
where everyone will have access through the system notepad. These are 
actually all of the special items that need to be prepared specifically 
for the install program; everything else should be in your program 
directory or sub-directories.

Now you are given the option of having a full screen format with a 
background, or a small format with no background. The full screen is 
the format most people are familiar with as it presents the dialog 
windows inside of a full screen background. You are asked to input 
any text that you want to appear in the right top corner of your full 
screen format. I use this to input a welcome line or a title line for 
the program I am installing. The bitmap window is where you enter the 
bitmaps that you created earlier that you want for your background and 
your logo. Again use the browse buttons to locate these files. For 
convenience I stored everything in my program directory, because later 
you can choose not to include these files in your install, but still 
have them handy to use in the program. Now is when the preview button 
comes in handy. If you don't like what you see stop the install and 
correct anything that you don't like. In real life you would do a few 
trial installs and have all of the BMPs worked out before you do the 
real thing. It took a few tries until I got my background and logo bitmaps 
just right, as fonts and bitmaps can look different under different applications.

The installation directory is where you stipulate what directory you want 
your program to install into. You can install into an existing directory or 
create a new directory for your install. I always use 'c:\program name', 
as c drive is the safest, because most everyone will have a c drive but 
not always d, e, f... You can also have the program load information from 
the registry base or from an .ini file. In the registry base option you 
are asked for the Root Key, Key and the Subkey. The .ini file option asks 
for the file name, section and entry. Again, I take the easy way and use, 
'c:\', but still the option to use the registry or .ini file is still 
available for use. On the user side, they are presented with the option 
of changing to any directory and are presented with how much disk space 
the install will need and how much disk space is available. If the user 
decides not to install, now is where they can bail out if they so choose.  	

The uninstallation window simply asks you whether you want an Uninstaller 
provided for the user. I always use this, because like the end user, I 
believe they ought to be able to remove my program with no hassles from 
my side.  The end page is where you put information for the "end page" 
this being the page where the user is given the option to view a readme 
type document that you have prepared beforehand. They are also given 
the option to launch your program when they exit the installer. The real 
end page though, is the advertising window that you agreed to, which gives 
the option to go to their web site. As I stated earlier, this is a small 
price to pay. If the user doesn't quit at this time the program is 
installed on his machine. You can choose to let the wizard build your 
install program at this time, but I always revert to the main program 
where additional options are available for the programmer.

If you check the "do not build" option on the end wizard page you will be 
put into the main application where you can fine tweak your install. You 
are given options for the following: Files, wizard texts, wizard options, 
uninstaller, and build. Files allow you to make changes and add options 
that the wizard doesn't offer. These are important features that I always 
use to specify the system file to place dll's, to place special fonts that 
I use in the font directory, not to include certain files (like that .bas 
and .bak program that I forgot to remove from my program directory). You can 
even specify files to not be removed by the uninstaller. 

After you fine tweak all the settings that you feel will be necessary, you 
can save your .iit program for future use and then you're ready to build your 
install program. The program produces a fine and compact package that can be 
used in a professional setting as well as being used to pass around your 
programs to friends and colleagues.


Gary Capps:
mailto:mourndove@wwgap.net
---------------------------------------------------------
USER DEFINED FUNCTIONS

We've discussed functions in previous newsletters.  Please
check newsletter #67 for an article by Carl Gundel on programmer-
defined functions in LB2, and a beginner's tutorial on functions
in general, and programmer-defined functions.

The ability to define our own functions in Liberty BASIC 2
will make the language more flexible, and it will make it
very easy to share code with one another.  There has been
some discussion recently regarding possible new string
manipulation functions that Carl might implement.  We can
write our own versions of string functions that are not
part of the native Liberty BASIC syntax.  We'll include
some string functions here, and also some math functions.
Some of the following functions should be easy to understand
even for beginners, while others are meant to be used by
more experienced programmers.

My hope is that this newsletter will spur others to 
experiment with their own functions and share them with
everyone.  There is a page on the Community Web that will
serve as a function library for LB2, so please post your
own functions here and on the CoWeb!

http://www.libertybasic.com/CoWeb.182
---------------------------------------------------------

********************IMPORTANT ***************************

To use these functions, we only need to know how to call
them.  Just as with Liberty BASIC functions, we do not need 
to understand HOW they work internally.  We just need to
know the proper way to use the functions.  We can then make
use of functions that have been shared by others, even if
we do not quite undertand the coding methods used.

********************IMPORTANT ***************************

---------------------------------------------------------
FUNCTION LIBRARY

Attached to this newsletter, you will find a file containing
the functions discussed here.  The discussion here is not
meant to be a tutorial on function writing, but rather an
overview of the way these functions were created.
---------------------------------------------------------
InsertString$

Several people requested a function that would insert one
string into another.  We can write our own InsertString$
function.  The parameters we want to pass will be:

(original string, insertion string, position to start the
insertion)

We'll try to use syntax that makes sense, yet is compact:

	newString$=InsertString$(string$,insert$,start)

Remember that as of this writing, a function must always
return a value, so the value must be captured in a 
variable, as above, or used in a command, as in the
next example.

As an example to call our new function, let's give Carl
Gundel his middle initial:

print InsertString$("Carl Gundel","E. ",6)

produces:  Carl E. Gundel

Here is the function, which takes in the original string,
the string to insert, and the starting position, then uses
Liberty BASIC's own left$() and mid$() functions.  

function InsertString$(string$,insert$,start)
    InsertString$=left$(string$,start-1)+insert$+mid$(string$,start)
    end function


Remember that when using the mid$() function, the third 
parameter is optional:

	mid$(string$,start[,length])

You must include the source string, and the starting position.
The mid$() function will return a string that starts at the
position indicated within the source string, and is the length
indicated.  If no length is indicated, then the string from the 
start position to the end is returned.
---------------------------------------------------------
ReplaceString$

Some folks have also requested a string function that would
replace characters within a string with other characters.
This one will have the following syntax:

	newString$=ReplaceString$(string$,replace$,start)

The first string will be the original string, the second string
will be the string that should replace part of the original
string, and 'start' will be the starting position within the
string to replace the characters.  Here is an example that will
replace the word, "like" with the word, "love".

print ReplaceString$("I like LB.","love",3)

produces:  I love LB.

We'll use LB string functions within our functions, just as we
did in the previous string function example.  This time, instead
of placing the new string into the original string, and then
adding the remainder of the original string to the end, we'll
place the insertion string in at the start position, and then
move as many characters as the length of the insertion string
to the right, and take the remainder of the original string from
that point to add to our new string.  We'll use a local variable
called "length" to represent the length of the insertion string.

function ReplaceString$(string$,replace$,start)
    length=len(replace$)
    ReplaceString$=left$(string$,start-1)+replace$+mid$(string$,start+length)
    end function

---------------------------------------------------------
ReverseString$

Some other programming languages have a function that will
reverse the order of characters in a string.  Here is the
syntax for such a function:

	reversed$=ReverseString$(string$)

Here is an example using the function:

print ReverseString$("Liberty BASIC")

produces:  CISAB ytrebiL

For this function, we will again rely on the LB mid$() function.
We can place our routine into a loop that decrements one on
each pass, starting with the last character in the string and
putting it into the first spot in a new string, then moving to 
the next-to-last character, and so on until the entire string 
has been reversed.

function ReverseString$(string$)
    for i = len(string$) to 1 step -1
        ReverseString$=ReverseString$+mid$(string$,i,1)
    next i
    end function

---------------------------------------------------------
String$

Some other languages also have a function that returns the
given number of characters specified.  Liberty BASIC has a
function called space$() that returns the number of blank
spaces specified.  We can write a function that returns a
string of ANY character specified, or even any group of characters.

Syntax:

	repeatString$=String$(char$, total)

As we might call it:

print String$("Tom ",4)
print String$("*",15)

produces:

Tom Tom Tom Tom
***************

This one is easy to write.  We just start a string
in a for/next loop, adding the string contained in
char$ as many times as is specified by "total".

function String$(char$, total)
    for i=1 to total
    String$=String$+char$
    next i
    end function

---------------------------------------------------------
BinDec (Binary to Decimal)

Liberty BASIC now has functions to convert hexadecimal
numbers to decimal numbers and decimal numbers to hexadecimal
numbers.  It is sometimes also useful to have a binary to
decimal conversion.  Binary numbers consist of all 0's and 
1's.  This is a binary number:  1001 0010 1000 1011, or it
can look like this, if you choose:  1001001010001011


Syntax for the function:

	decimalnumber=BinDec(binarynumber)

Usage:

print BinDec(100)

produces:	4

The easiest way we have found to do this conversion is to
convert the binary number to a string, as Thomas has done in
his version of binary to decimal conversion.  In a loop, he
parses the string version of the binary number, getting its
value with the VAL() function and adding it to the decimal
number that will be returned.

function BinDec(bin)
    binary$=str$(bin)
    for i = 0 to len(binary$)-1
        g$=left$(right$(binary$,i+1),1)
        BinDec=BinDec+((2^i)*val(g$))
    next i
end function

---------------------------------------------------------
DecBin (Decimal to Binary)

It is sometimes useful to be able to convert a decimal
number to its binary representation.  This is not as 
easily accomplished as the conversion in the other direction.

Syntax:

	binarynumber=DecBin(decimalnumber)

Usage:

print DecBin(8)

produces:	1000

The following method could be improved, so please post
your improvements of the function!  Thomas' routine evaluates
the position for the binary numeral in question, then uses
an algorithm to compare powers of the number to determine
if the binary digit will be a "0" or a "1".  He appends
each digit to the beginning of a string, and when the
computation is complete, he returns the value of the
string with the VAL() function.

function DecBin(dec)
    dec=int(dec)
    for i=1 to 64
        x=int((2^i)+.5)
        y=int((2^(i-1))+.5)
        nm=int(dec/y)*y
        if int(nm/x)=nm/x then s$="0"+s$ else s$="1"+s$
    next i
    DecBin=val(s$)
    end function

---------------------------------------------------------
Modulus or MOD

Most other languages have a MOD function.  This returns the
remainder when one number is divided by another.  Syntax:

	modnumber=Modulus(number,divisor)

Usage:

print Modulus(11,4)

produces:  3

Why does it produce 3 for an answer?  If you divide 11 by 4, the
quotient is 2, with a remainder of 3.  (4 * 2) + 3 = 11

Here it is.  Pretty easy, eh?

function Modulus(number, divisor)
    Modulus=(number)-(int(number/divisor)*divisor)
    end function

---------------------------------------------------------
MakeRGB

For API calls, we may need a long integer color value to
represent the red/green/blue color intensity of a color.
Liberty BASIC uses named colors, such as white, red, darkcyan,
etc.  It can also use a graphics color command to achieve
an RGB custom color.  API calls require that the red/green/blue
values be passed as a single long integer.  We do this by
multiplying the blue value by 256*256, the green value by 256,
and then adding the two results to the red value.  Here is is
as a function:

Syntax:

	longcolorvalue=MakeRGB(red,green,blue)

Usage:

print MakeRGB(100,200,30)

produces:	2017380

Have a look at the function below.  It contains error trapping
to prevent the possibility of values below 0 or above 255
from being used in the function.  This is up to the individual
programmer, but error trapping should be done either within
the function, or as part of the code before the function is called.

function MakeRGB(red,green,blue)
    if red<0 then red=0
    if red>255 then red=255
    if green<0 then green=0
    if green>255 then green=255
    if blue<0 then blue=0
    if blue>255 then blue=255
    MakeRGB=(blue*256*256)+(green*256)+red
    end function

---------------------------------------------------------
GetRed

We might also have a need to retrieve the individual RGB
values from a long integer color value.  For instance,
we can get the color of any pixel in a window with the
GDI function to GetPixel.  It returns a long integer
color value.  To use this with LB's color command, we need
to separate the colors into red, green and blue.  Remember
LB uses RGB like this:

with variables:
red=100 : green=231 : blue=52
print #graphics, "color ";red;" ";green;" ";blue

or with hard-coded values:
print #graphics, "color 100 231 52"

To 'get' the red componant of an RGB long integer color value,
we use the formula to create a long value from separate RGB
values, backwards!  We will send in the long integer color value,
and ask our function to return just the red componant.
Here is the function:

print GetRed(9471354)
function GetRed(color)
    blue=int(color/(256*256))
    green=int((color-blue*256*256)/256)
    GetRed=color-blue*256*256-green*256
    end function
---------------------------------------------------------
GetGreen

Of course, we'll want the green value also, so here it is:

print GetGreen(9471354)
function GetGreen(color)
    blue=int(color/(256*256))
    GetGreen=int((color-blue*256*256)/256)
    end function
---------------------------------------------------------
GetBlue

And now for the blue.  Notice that we must do the most
manipulating to retrieve the red value, and the least
to retrieve the blue value. 

print GetBlue(9471354)
function GetBlue(color)
    GetBlue=int(color/(256*256))
    end function
---------------------------------------------------------
HiWord and LoWord

A double-word, or dword value consists of two word values,
a hiword and a loword value.  Sometimes an api function
will return a dword, and we need to separate the hiword
value from the loword value for use.  If we wanted to make
a dword value from two word values, we would multiply the
value that is to be the high word by (256*256), which is 
also 256^2 (256 squared).  Then we would add the hiword to
the loword:

dwordvalue = hiword*(256*256) + loword

To separate the hiword and loword from a dword value, we do
the process in reverse:


print HiWord(99471354)
function HiWord(dword)
    HiWord=int(dword/(256*256))
    end function

print LoWord(99471354)
function LoWord(dword)
    hiword=int(dword/(256*256))
    LoWord=dword-(hiword*256*256)
    end function

---------------------------------------------------------
HiByte and LoByte

Just as a dword is made up of two word values, a word value
consists of two byte values.  To make a word value from two
byte values, we multiply the value that is to be the high byte
by 256, then add it to the lowbyte value:

wordvalue = hibyte*256 + lobyte

To separate the HiByte and LoByte from a word value, we reverse
the process:

print HiByte(4*256+5)
function HiByte(word)
    HiByte=int(word/256)
    end function

print LoByte(4*256+5)
function LoByte(word)
    hibyte=int(word/256)
    LoByte=word-(hibyte*256)
    end function

---------------------------------------------------------
HOW ABOUT??

Perhaps somebody would like to write a MakeWord or
MakeDword function and share it?  How about some other
useful math functions???
---------------------------------------------------------
NEW FUNCTIONS FOR OPEN SOURCE EDITOR

We can convert some more of our routines in the open source
editor to functions.  Here are two really useful ones.  The
first one will take a full path and filename, such as would
be returned by a filedialog, and it will return the filename
with no drive/directory information.  

    function SeparateFile$(f$)
        fileindex=len(f$)
        filelength=len(f$)
          while mid$(f$, fileindex,1)<>"\"
            fileindex=fileindex-1
          wend
        SeparateFile$=right$(f$,filelength-fileindex)
        end function


And here is the equivalent function that will return just the
drive and directory information from a filename:

    function SeparatePath$(f$)
        fileindex=len(f$)
        filelength=len(f$)
          while mid$(f$, fileindex,1)<>"\"
            fileindex=fileindex-1
          wend
        SeparatePath$=left$(f$,fileindex)
        end function

The previous two functions make it really easy to test for a 
file's existence with the FILES statement.

    function FileExist(fPath$,fFile$)
        files fPath$,fFile$,info$(
        FileExist=val(info$(0,0))
        end function

Here is the way we call these functions in our file/open routine
in the open source editor.  We place the filename into the
variable, file$.  We then get the filename alone and place
it into the variable, shortFile$, and the path information
alone and place that into the variable, filePath$.  

    filedialog "Open file..",filePath$+"*.bas",file$ 

    shortFile$=SeparateFile$(file$)
    filePath$=SeparatePath$(file$)

With that information, we can easily use our FileExist function
to see if the requested file exists, and if it doesn't (that
means the return from the FileExist function is less than 1)
then we do not attempt to open the file:

    if FileExist(filePath$,shortFile$)<1 then
        notice "Error"+chr$(13)+"File does not exist."
        goto [loop]
    end if

---------------------------------------------------------
USING FUNCTIONS WITH FUNCTIONS

Earlier, we made our own InsertString$ function that used
LB functions left$() and mid$() within it.  We can also use 
functions as parameters to send to other functions.  We called 
the InsertString$ function like this:

print InsertString$("Carl Gundel","E. ",6)

We could also have called it like this:

print InsertString$(upper$("Carl Gundel"),"E.",6)

Notice that the upper$() function is actually used as a parameter
in our InsertString$ function?  The result of the call would be:

CARL E. GUNDEL

In addition to using Liberty BASIC functions as parameters in our
functions, we can use OUR OWN functions as parameters in functions.
The FileExist function could have been called without setting the
filename and pathname into variables first, by using the
SeparatePath$() and SeparateFile$() functions as parameters passed
to our FileExist() function.  Here it is:

    if FileExist(SeparatePath$(file$),SeparateFile$(file$))<1 then
        notice "Error"+chr$(13)+"File does not exist."
        goto [loop]
    end if


Just as we can use Liberty BASIC functions within our own
functions, we can call other functions of our own within
our functions.
---------------------------------------------------------
REQUEST

Do you think we can also use OUR OWN functions as parameters
in Liberty BASIC functions?  I am often asked how I know all
the things about Liberty BASIC that I know.  Here is the anwer:
I ask questions, then try it out myself.  This function information
is an example of that.  I assume that you may, indeed, use your
own functions within Liberty BASIC functions, but I have 
purposely not tried it out, in hopes that some others will become
inquisitive and perhaps think of some new questions to ask that
I haven't thought of.

Can we use LOWER$(SeparateFile$(file$))

Try it and see, and report back to us!

How about an example of two programmer defined functions, one 
of which calls the other?  Or perhaps two functions that call
one another?  How about an example of recursion in programmer-
defined functions?  Inquiring minds want to know, and there are
far better brains out there than mine!
---------------------------------------------------------
BMPBUTTONS AT RUNTIME -- LB VERSION 2 ONLY!

We recently had a question from a member who wanted to know
if new text could be placed upon a bmbutton at runtime.  It
seemed that for his needs, simply having a set of bmpbutton
bitmaps loaded and waiting would not suit his purposes.
Remember that we can change the bitmap that appears on a
button during program execution with the command:

    loadbmp "newbmp", "New.bmp"
    print #1.bmpbtn, "bitmap newbmp"

To place text onto a bmpbutton at runtime, will require that we
create a new bitmap at runtime, save it with BMPSAVE, load it
with LOADBMP and then issue a bitmap command to the bmpbutton.

There are at least two ways to create a bmpbutton at runtime.
The first way is to load a blank button template, then draw it
into a graphicbox, and print the desired text upon it.  The other
way is to draw the new button completely with graphics commands
in a graphicbox.  With either drawing method, we then save it as a
file, load it, and issue a bitmap command to the bmpbutton in
question.  Here are the steps:

1.  Draw the desired button image in a graphicbox.
2.  GetBmp, x, y, width, height of the button image desired.
3.  BMPSAVE the bitmap named in the GetBmp command to a file.
4.  LOADBMP the bitmap file that we newly created.
5.  Issue a "bitmap  " command to the bmpbutton.

Here it is, in code, using a blank button template called "empty.bmp"
#1.g is the handle of a graphicbox.  #1.b is the handle of the bmpbuton.
The entire code for this program is contained in the attached file,
"bmpbuts.bas".

'draw the new bitmap, using a template bitmap and text commands:
    loadbmp "empty","empty.bmp"
    print #1.g, "down;color cyan;place 22 22"
    print #1.g, "drawbmp empty 0 0"
    print #1.g, "font arial 16"
    print #1.g, "|Close"
    print #1.g, "getbmp closebmp 0 0 100 30;flush"
    BMPSAVE "closebmp","close.bmp"
    loadbmp "closebmp","close.bmp"

'place the new image on the bitmap button:
    print #1.b, "bitmap closebmp"

The file "bmpbuts2.bas" is very similar, but it asks for user input
to determine the text to place upon the button template to make
a new image for the bmpbutton.  That new text is contained in the
variable, text$.  The code to draw this image follows:

'draw the new bitmap, with user input for bmpbutton text:
    loadbmp "empty","empty.bmp"
    print #1.g, "down;color cyan;place 12 22"
    print #1.g, "drawbmp empty 0 0"
    print #1.g, "font arial 16"
    print #1.g, "|";text$
    print #1.g, "getbmp closebmp 0 0 100 30;flush"
    BMPSAVE "closebmp","close.bmp"
    loadbmp "closebmp","close.bmp"

'place the new image on the bitmap button:
    print #1.b, "bitmap closebmp"

"Bmpbuts3.bas" is similar.  It uses a graphics window, whose
handle is #g, to draw the bitmap entirely with graphics commands,
save it, etc. BEFORE the program window opens.  In this way,
the newly created bitmap is the original image to appear on
the bmpbutton.

'draw the new bitmap entirely with graphics commands:
    print #g, "down;fill darkred"
    print #g, "size 2;backcolor darkred"
    print #g, "color black;line 99 1 99 28"
    print #g, "line 3 29 99 29"
    print #g, "color red"
    print #g, "line 1 1 99 1"
    print #g, "line 1 1 1 29"
    print #g, "place 18 22"
    print #g, "font arial 16"
    print #g, "|Button"
    print #g, "getbmp newbmp 0 0 100 30;flush"
    BMPSAVE "newbmp","new.bmp"

'make a bmpbutton with this new bitmap, and open a window:
    bmpbutton #1.b, "new.bmp",[open],UL,10,100
    open "Bmpbuttons at Runtime" for window as #1

There are two other demos included here.  "Bmpbuts4.bas" uses
sprite routines to draw an image for the bmpbuttons and
"Bmpbuts5.bas" shows a way to include only one bitmap file with
a program, and to make separate bitmap files for bmpbuttons at
runtime.  This one works pretty much as the previous examples,
so I won't include more explanation here.  It takes a large
bitmap that includes some of the bmputtons from the LB2 editor,
and makes separate bmpbuttons in a window.

"Bmpbuts4.bas" is the demo that uses sprite routines.  Why
would one go to all of that trouble?  Have a look at the 
attached bitmap, toolbar.bmp.  I have set my system color
scheme to one called "maple".  You can see that the toolbar
itself is the maple color, while the buttons are gray.  This
shows up in plenty of applications, but there are also many
programs that color the button background to the system colors,
then draw the image on IT.  Unfortunately, you cannot just
draw an image on top of another without obscuring all that is
underneath, so if your image has an irregular shape, it will
obscure a rectangular area on the screen below it.  To place an
image on the screen so that the screen background shows all
around the image, use masks and sprites.  A sprite is
simply the desired image, placed against a black background.
The mask is an identically shaped image, which is black in every
pixel of the desired image, and white everywhere else.

For a sample image sprite with mask, look at the attached
runbtn.bmp.  It is a copy of the run button from the LB2 editor,
but as a sprite and mask.  Have a look at wowmaple.bmp.  It is
the result on my system of drawing the mask and sprite to make
the bmpbutton.

This method does not use LB's Drawbmp command, but uses the GDI
function, BitBlt.  The mask is placed upon the screen using the
raster operation SRCAND, which combines pixels on the screen with
the mask bitmap by using the Boolean AND operator.  The sprite is
then put onto the screen using the dwROP SRCPAINT, which combines
pixels on the screen with the sprite bitmap using the Boolean OR 
operator. 

We can capture the results of these operations into a really nice-
looking bitmap for our bmpbutton, using GetBmp and BmpSave, as
we did in earlier examples.

IMPORTANT NOTE ABOUT COLORS

LB2 now colors windows with the DEFAULT system colors of the
user unless you override this with a BackgroundColor$ command.
Graphicboxes are now filled with this default color, but 
graphics windows are not.  Use a graphicbox for this sprite/mask
bmpbutton technique, and you will automatically have the correct
background color for your button.  Please look at "Bmpbuts4.bas",
which illustrates this method.

---------------------------------------------------------
ADDING A BMPBUTTON AT RUNTIME TO OUR EDITOR

We can make a bitmap button at runtime by opening a special
window to create and save our button.  Since we are avoiding
extra files in this editor, we'll draw the button with
graphics commands, and after we've saved it to a file,
we'll close the drawing window and open the program window.

[makebmpbutton]
    WindowWidth=btnwide+40:WindowHeight=btnhigh+60
'** NEW ** note that a graphicbox will automatically be
'          filled with default or programmer-set
'          BackgroundColor$! (not true of graphics window)
    graphicbox #b.g, 0,0,200,100
    open "BmpButton" for window_nf as #b
    print #b, "trapclose [closeb]"

    print #b.g, "font arial 0 12 bold"
    print #b.g, "down;color red ; place 5 10"
    print #b.g, "|Quit"
    print #b.g, "color 230 230 230"
    print #b.g, "line 0 0 29 0;line 0 0 0 14"
    print #b.g, "color darkgray"
    print #b.g, "line 28 1 28 14;line 1 13 28 13"
    print #b.g, "color 45 45 45"
    print #b.g, "line 29 1 29 15;line 1 14 29 14"
    print #b.g, "getbmp quitbutton 0 0 30 15"
    BMPSAVE "quitbutton","qbutton.bmp"
[closeb]
    WindowWidth=600:WindowHeight=480
    close #b:return

Remember that the graphicbox will be filled automatically with
the BackgroundColor$, which is the system default color, or
the BackgroundColor$ chosen by the programmer.  We'll let
that be the background color for the button, making the
foreground color red and writing the text "Quit" on the
button.  We'll also draw some borders to give a 3D-look.
The look of your bmpbutton is up to you, so experiment to
get a look that pleases you.

After the window is closed and the new bitmap is saved to
file, we had better check that the file was created successfully
before we try to add our RED "Quit" button to our editor.  We
can use our new FileExist() function!  The bitmap file will
be in the DefaultDir$ and will be called "qbutton.bmp"


    if FileExist(DefaultDir$,"\qbutton.bmp")<1 then
        notice "Error"+chr$(13)+"Unable to create bmpbutton!"
        end
    end if

The program will abort if the bitmap file is not found.  If
everything is okay, we can use the new bitmap for a bmpbutton"

    bmpbutton #1.quit, "qbutton.bmp",[quit],UL,504,btnY


We should also remember to add a tooltip for new button:
    struct toolinfo17, cbSize as word, uFlags as word,_
        hwnd as word, uId as word, x as word, y as word,_
        w as word, h as word, _
        hInst as word, lpstrText$ as ptr

    toolinfo17.cbSize.struct = 22
    toolinfo17.uFlags.struct = TTF.IDISHWND or TTF.SUBCLASS
    toolinfo17.hwnd.struct = h
    toolinfo17.uId.struct = hwnd(#1.quit)
    toolinfo17.hInst.struct = hInstance
    toolinfo17.lpstrText$.struct = "Close Program."
    calldll #user, "SendMessage", hwndTT as word, TTM.ADDTOOL as word, _
    0 as word, toolinfo17 as struct, re as long

As always, the full code for the open source editor is 
attached to this newsletter.  Please consider sending in
your own version of the editor, or any functions that you
have written and are willing to share.
---------------------------------------------------------
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
---------------------------------------------------------
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
---------------------------------------------------------