---------------------------------------------------------
The Liberty Basic Newsletter - Issue #102 - NOV 2002
    2002, http://groups.yahoo.com/group/lbnews/
             All Rights Reserved
Individual authors retain copyrights to their works.
---------------------------------------------------------
"Ever tried. Ever failed. No matter. Try Again. Fail again. 
Fail better."  - Samuel Beckett
---------------------------------------------------------
In this issue:
	Notes from the Editor
	Liberty BASIC News
	Safe Registry and Ini File Alternative
	Tipcorner - Deleting and Renaming Disk Files
	Segments and Flushing - a Graphics Tutorial
	Flat Toolbar with Toolips for the Open Source Editor
	Translating 32-bit Visual BASIC API Calls
	Event-Driven Programming Concepts - Part 1
		by Brad Moore
	Spotlight on the Community!
		ODBC in Liberty BASIC by Dennis McKinney
		Hex Viewer by Walt Grams
		Listing Files Recursively by Mark Parkinson
		Registering Hot Keys by Ken Lewis
		Preventing more than 1 instance of your program 
			by Ken Lewis
		Multi-Coloured Text Input Boxes using a DLL
			by Raymond Fisher
		Images on Buttons and Statictext 
			by Mike Bradbury
		Two Demos by David Conner
---------------------------------------------------------
NOTES FROM THE EDITOR

First, thanks to Dennis McKinney who has kindly consented to allow us to include his ODBC sample with this newsletter.  Dennis is one of those very special people who is always expanding our frontiers!

Thanks also to Walt Grams for his gracious permission to include his hex viewer in this newsletter!  It looks very similar to other such programs that I've seen, proving yet again that quite a lot can be accomplished in relatively few lines of code by a clever Liberty BASIC programmer.

Many thanks to Mark Parkinson for his concise and very useful code to list disk files of specified types recursively.  Great stuff!

Thanks also to Ken Lewis for his great demo program and explanation of registering hot keys.  Ken has broken some new ground on this one!  He has also provided instruction on preventing more than once instance of a program.  Great stuff!  Thanks, Ken!

Special thanks to Raymond Fisher, who contributed an article in a previous newsletter that showed us how to make colored text input boxes using the Windows API in Liberty BASIC.  He has now simplified this for us by providing a custom textbox DLL.  He includes detailed explanation and some sample programs.  Thanks, Ray!

Thank you, Mike Bradbury, for sharing another easy and innovative method.  The images on buttons and statictext will be a great asset to our programming toolbox!

Thanks David Conner, for sharing your two demo programs with us.

Thanks to my co-publisher, Brad for his great article on event-driven programming!

The quote above from playwrite Samuel Beckett was chosen for this issue because it so vividly describes the process I went through in creating the real Windows toolbar with tooltips.  There are several different ways to handle the toolbar creation, and the one in use is my own variation.  That was not a problem.  The problem came when I tried to add tooltips.  Some button images are universally undersood... cut,copy,paste, for instance.  Others aren't so clear, but images are the standard for toolbar buttons, so tooltips are invaluable.  Since we've known how to do tooltips for a long time, and toolbars are also not new, it should have been easy to marry the two, right?  Wrong!  I tried this, did some research, tried that, did some more research, tried yet another way, and so on and on.  I was getting REALLY good at failing!  I was despairing that I'd never work it out by myself when I finally hit on the answer.  There is NOTHING like the feeling you get when you hit the "run" button and view your program that is working as you envisioned, finally!

Thanks for listening, er, I mean "reading."  If you find anything in this issue that is interesting or useful, please comment.  If you have a question or want to point out an error or omission, please comment.  If you have a suggestion for a future article, please comment.  Tom Nally will be publishing the December newsletter and Brad Moore will be doing the January issue, so send submissions to one of these fine people.  (Some of you ARE planning to write articles in the next couple of months, aren't you?)

Best wishes to all in our online Liberty BASIC family,
Alyce Watson

---------------------------------------------------------
LIBERTY BASIC NEWS
Carl Gundel is planning a bugfix release of LB3 -- Liberty BASIC 3.03.  It won't contain new features, just fixes for bugs.

Carl has also begun work on the next significant version update, to be called Liberty BASIC 4.  Here are some of his improvements and plans, as posted on his official forum:

*****************
from October 22:
- Working on DO/LOOP support
- Dim more than 1 array with a single DIM statement
- The debugger supports run to cursor.  Breakpoints are coming also.
- TAB() is now supported in PRINT statements (LPRINT will support it also).
- Proper enforcement of nested structures is now supported at compile time.  For example it will prevent loops from overlapping, and other similar problems which would previously only result in hard to understand errors when the program was run.
- Added the ability to specify how many spaces are used when tabbing in the Liberty BASIC editor.

******************
from October 26:
Okay, now that I have a handles variable mechanism, I also added a command called MAPHANDLE (I'm open to suggestions as to a better name if there is one).  It will rename all the handles that start with a sequence of characters to something else.

Valid forms:

  maphandle #handle, stringexpression
  maphandle stringexpression, #handle
  maphandle #handle, #handle
  maphandle stringexpression, stringexpression


******************
from October 29:
Today I started work on support for subs for event handlers.  I made good progress and managed to make the core part of the mechanism work!  This is an exciting feature!  Break out the bubbly!  ;-)

---------------------------------------------------------
---------------------------------------------------------
SAFE REGISTRY AND INI FILE ALTERNATIVE - intermediate level
	by Alyce Watson, 2002

Did you ever wonder how a program can remember the list of your most recently opened files, or your font preference, or that you are a registered user?  There are several ways this can be done.  One way is to write a simple text file, called an "ini" file.  

INI FILES
"Ini" is short for "initialization".  Liberty BASIC has an ini file that contains your font preference, your list of recent files, your list of externals, and other preferences and records.  It is called lbasic302.ini and you will find it in the same folder that contains your copy of Liberty BASIC.  When Liberty BASIC starts up, it reads the information in this file, and uses it to set your prefered font, list your recent files in the File menu, and so on.  You can open this file in a texteditor and read it.

The gui designer that comes with Liberty BASIC, Freeform, has an ini file.  The open source Liberty BASIC editor that was developed in a series of tutorials in this newsletter, also has an ini file, and ini files were the subject of an earlier newsletter article.  See the attached version of the open source editor for an ini file example.  The user's font preference and three most recently opened files are written to the ini file.

WRITING TO THE REGISTRY
Windows has a repository of information about the way it works.  This is called The Registry.  Windows looks in the registry to see which shortcuts should be displayed on your desktop, to record your list of recently opened files, to record your choice of desktop color scheme, display resolution, and all kinds of other information.  Applications can write to the registry.  This is a very bad idea for novice programmers!  If the registry becomes corrupt, Windows may not function properly, or some applications may not function properly.  In the worst case, the computer may not be usable at all!  Considering the risks involved, it makes little sense to store information in the registry when something like an ini file will work just fine.


USING THE API TO CREATE AN INI FILE
The two functions, WritePrivateProfileStringA and GetPrivateProfileStringA provide an easy and precise way to write to and read from initialization files.  These "ini" files are simple text files.  Use this method as an alternative to writing information to the Windows registry.  Tampering with the registry can have dire results, even causing Windows to become unusable.  Writing to a private ini file can only have an impact on the single program referenced.

You might want to use ini files to record a user's list of recently opened files, or his preferences for use of the program, like syntax color "on" or "off", or even whether he is a registered user of your program.


WRITING AN INI FILE BY API
WritePrivateProfileStringA

First, we'll examine writing to a private ini file.  Here is the syntax of the function, which is a part of kernel32.dll:

calldll #kernel32, "WritePrivateProfileStringA", _
Section$ as ptr, _    'section name
Entry$ as ptr, _      'entry name
String$ as ptr, _     'actual entry
FileName$ as ptr, _   'name of ini file
result as long

NOTE:  The syntax above is for LB3.  Syntax for this function for LB2 usage is included at the end of this article.

Following, find the descriptions for each argument of the function:

********
Section$ 
Points to a null-terminated string that specifies the section name to which a string will be copied. Liberty BASIC automatically adds the necessary single null terminator, which is chr$(0), so we don't need to add it.  If the section does not exist, it will be created.  If you were to read the file in a texteditor, this entry would look like this -- the section name, contained in square brackets:

[section]

If the section were called "user", it would look like this:
[user]


******
Entry$ 
Points to a null-terminated string containing the entry associated with the value string. If the entry does not exist it will be created. If this is NULL (""), the entire section is deleted.  If you were to read the file in a texteditor, this entry would look like this -- the Entry$ followed by an = sign, followed by the String$ 

Entry=String

If the Entry was called "name" and its value was "Carl Gundel", it would look like this:
name=Carl Gundel

*******
String$
Points to the null-terminated string value to be written to the file. If this is NULL, (""), the Entry$ entry is deleted from the file.  

Entry=String

If the Entry was called "name", and the String$ was "Carl Gundel",it would look like this:
name=Carl Gundel

*********
FileName$ 
Points to a null-terminated string that names the INI file.  If this name is a fully-qualified path and file name, it will be used.  If there is no path, Windows searches for the file in the Windows directory.  If it doesn't exist, it will be created.

******
result
Returns zero if it fails, or nonzero if it is successful.


DEMO
Here is a demo.  After running this small routine, look in the Windows folder for the file "testme.ini".  Open it in Notepad to see the result.  Notice that your program does not need to "open" or "close" the file as you would normally do when writing to a file in Liberty BASIC.

Section$="User"
Entry$="Name"
String$="Carl Gundel"
FileName$="testme.ini"

CallDLL #kernel32, "WritePrivateProfileStringA", _
Section$ As ptr, _
Entry$ As ptr, _
String$ As ptr, _
FileName$ As ptr, _
result As long

This function can be written on a single line.  Imagine that, writing information to an ini file with a single line of code!  The following line may line-wrap in your text editor, but you may type or copy/paste it into the LB editor to run it, making sure to eliminate any line wraps.  The single line constitutes an entire program.

calldll #kernel32, "WritePrivateProfileStringA","User" as ptr,"Name" as ptr,"Carl Gundel" as ptr,"testme.ini" as ptr, result as long

I ran the single-line program, then looked in my Windows folder for the file, "testme.ini" and this is what it contained:

[User]
Name=Carl Gundel

We can use this method to record many kinds of information.  


READING AN INI FILE BY API
GetPrivateProfileStringA
This function reads a specific ini file.  Many arguments are the same as the ones in WritePrivateProfileStringA.  There are some additional arguments, which will be explained later.  

Section$, and FileName$ are explained earlier in the decription of WritePrivateProfileStringA.  


******
Entry$ 
Points to the null-terminated string that is associated with the value string.  If this parameter is empty, the return string will contain a list of all values in the specified section, separated by null characters, and terminated with double null characters.


********
Default$
Points to a null-terminated string that specifies the default value for the given entry if the entry cannot be found in the initialization file. This parameter must never be NULL or empty.  You might want to specify a value that makes it clear that this section of the ini file was blank.

Default$ = "no name given"


*************
ReturnString$
Points to a null-terminated string that will contain the key value stored in the ini file.  If the key value cannot be found, this buffer will contain the contents of the Default$ value.  There is an important difference in the use of this string argument in GetPrivateProfileStringA.  We will be reading data that is placed into this variables by the API function.  This means that we must pass this argument "By Reference".  Passing it this way means that we are not just passing the value of the string, we are passing its memory address so that the function can alter the data contained at that address.  To let Liberty BASIC know that we want to pass the argument "By Reference", we include a null-termination on the string, like this:

ReturnString$ = space$(100) + chr$(0)

Above, we are creating a buffer, or location in memory that is large enough to contain the data that will be placed there by the function.


**********
SizeString
This parameter specifies the length of the buffer, ReturnString$


******
result
The return value specifies the number of bytes copied to the specified buffer, not including the terminating null character.  This means that it specifies the length of the text that the function placed into the buffer.  We can use this information to read the value in the ini file without extraneous characters that might be tacked onto the end.


Here is a working example, that reads the values written to "testme.ini" that were produced in the routine above:

Section$="User"
Entry$="Name"
FileName$="testme.ini"
Default$ = "no name" + Chr$(0)
SizeString=100
ReturnString$=Space$(SizeString)+Chr$(0)

CallDLL #kernel32, "GetPrivateProfileStringA", _
Section$ As ptr, _
Entry$ As ptr, _
Default$ As ptr, _
ReturnString$ As ptr, _
SizeString As long, _
FileName$ As ptr, _
result As long


Print "The key is "
Print Left$(ReturnString$,result)
End

If you were now to use Notepad to open "testme.ini" found in the Windows directory, it would look like this:
[User]
Name=Carl Gundel


DEMO TO RECORD USER'S PASSWORD
Here is a small demo that checks the ini file to see if the user has registered.  You would, of course, put your own code to handle the situation.  The demo simply gives a notice telling if the user is registered when the program starts.  There is a button that the user can click to enter his password.  Run the program for the first time, and you will get a "Not registered." notice.  Click the button and type the proper password.  Close the program.  Run the program again, and you should receive notice that you are a "Registered user."
 
This demo is similar to the earlier demos, but it uses a different section and entry name:

Section$="Register"
Entry$="Password"


nomainwin
button #1, "Register",[reg],UL,10,10,120,26
statictext #1, "Password = 'Official'",10,50,200,30
open "Register My App" for window_nf as #1
print #1, "trapclose [quit]"

gosub [readReg] 'see if user has registered

if key$<>"Official" then
notice "Not registered."
else
notice "Registered user."
end if

wait


[quit]
close #1:end

[reg]
prompt "Enter password";pw$
if pw$="" then
notice "Not a valid password."
end if

Section$="Register"
Entry$="Password"
String$=pw$
FileName$="testme.ini"

CallDLL #kernel32, "WritePrivateProfileStringA", _
Section$ As ptr, _
Entry$ As ptr, _
String$ As ptr, _
FileName$ As ptr, _
result As long
wait


[readReg]
Section$="Register"
Entry$="Password"
FileName$="testme.ini"
Default$ = "no password" + Chr$(0)
SizeString=100
ReturnString$=Space$(SizeString)+Chr$(0)

CallDLL #kernel32, "GetPrivateProfileStringA", _
Section$ As ptr, _
Entry$ As ptr, _
Default$ As ptr, _
ReturnString$ As ptr, _
SizeString As long, _
FileName$ As ptr, _
result As long

key$=Left$(ReturnString$,result)
return

*******************************
LIBERTY BASIC 2, 16-BIT SYNTAX:
Here are the functions in LB2 syntax:

WRITING:
'open "kernel" for dll as #kernel
CallDLL #kernel32, "WritePrivateProfileString", _
Section$ As ptr, _
Entry$ As ptr, _
String$ As ptr, _
FileName$ As ptr, _
result As word
close #kernel  

READING:
'open "kernel" for dll as #kernel
calldll #kernel, "GetPrivateProfileString", _
Section$ as ptr, _
Entry$ as ptr, _
Default$ as ptr, _
ReturnString$ as ptr, _
SizeString as word, _
FileName$ as ptr, _
result as word
close #kernel

---------------------------------------------------------
TIPCORNER - DELETING AND RENAMING DISK FILES - novice level
	by Alyce Watson, 2002

DELETING FILES
Liberty BASIC allows us to delete files with the KILL command.  Note that they will not be placed into the recycle bin.  They will be permanently deleted.

syntax:

KILL filename$

In the above example, filename$ must be the name of an existing file on disk.  If you try to KILL a file that doesn't exist, the program will halt with an error.  If the filename$ doesn't contain a path, it is assumed to be in the same folder as the Liberty BASIC program.  This folder is contained in the variable DefaultDir$.  The following little demo creates a text file, then kills it.  The file is created in the DefaultDir$ because no path information is included in the filename$ parameter.

open "hello.txt" for output as #f
print #f, "hi"
close #f

kill "hello.txt"

Here, we let the user choose a file to delete:

filedialog "File to Delete","*.*",filename$
if filename$="" then 
    print "Cancelled!"
else 
    kill filename$
    print filename$;" deleted!"
end if


RENAMING FILES (MOVING FILES)
We can rename a file with the NAME statement. Files can be moved to other folders using the NAME statement.  To rename the file, "readme.txt" to "message.txt":

NAME "readme.txt" as "message.txt"

To move the file, "c:\info\readme.txt" to c:\myfiles\readme.txt":

NAME "c:\info\readme.txt" as "c:\myfiles\readme.txt"

Just as with the KILL command, an attempt to NAME a file that doesn't exist will cause the program to halt with an error.  Also, as with the KILL command, if no path is given for the filename$, it is assumed to be in the same directory as the program, which is the DefaultDir$

Here is a demo that allows a user to rename a file.

filedialog "File to rename","*.txt",oldfile$
if oldfile$="" then end

filedialog "New name","*.txt",newfile$
if newfile$="" then end

name oldfile$ as newfile$

---------------------------------------------------------
SEGMENTS AND FLUSHING - novice level
	- a graphics tutorial
	- copyright 2002, Alyce Watson

From the eBook, Mastering Liberty BASIC 3:

Liberty BASIC graphics consume memory.  Each graphics command is written to memory to something called a metafile.  If many graphics are drawn, memory resources may become low.  This can make a program and a system perform sluggishly.  It can even cause a program to crash.  You can avoid this problem with proper graphics memory management.  There are three commands that release memory consumed by graphics:  CLS, DISCARD, and DELSEGMENT


CLS
The CLS (CLearScreen) command clears the graphics area and releases all memory consumed by graphics.  This will work fine if there is no need to remember previous drawing routines, and if the flickering associated with clearing the screen is acceptable in a program.


FLUSH
The FLUSH command may be a bit confusing.  In other areas of life, the word flush may mean to remove things, or to clean things out.  We flush the old antifreeze from our radiators so that we can fill them with new antifreeze, just as one example.  In Liberty BASIC, FLUSH has a unique meaning.  It means "make graphics persist".  Graphics drawn on a graphics window or graphicbox will disappear if the window is obscured or covered-over.  If we issue a FLUSH command, the graphics will persist so that they will still be visible when the window is again made visible.

Each time the FLUSH command is issued, a "drawing segment" is created.  All drawing operations that are accomplished in between FLUSHes (or from the beginning to the first FLUSH command) are part of a drawing segment.  Each of these segments has an ID number.  A FLUSH command closes the current drawing segment and increments the ID by 1.


SEGMENT
You can retrieve the segment number (segment ID) of the current drawing segment with the SEGMENT command.  It looks like this:

syntax:  print #handle, "segment variableName"

as it might look in a program:

print #1, "segment segID"
notice "Drawing segment is ";segID

After this command is issued, the segment ID number will be contained in the receiver variable.  This is the ID for the segment that is currently open.  The first segment begins with the first graphic command and ends with the first FLUSH command.  All segments after that are contained between FLUSH commands.  To get the ID of a segment, use the SEGMENT command right before the FLUSH command.  Segment IDs are used to DISCARD or REDRAW individual segments.  The receiver variable can be named as desired, and it is advantageous to use discriptive names for it.  For instance, if a circle has been drawn, you might call the segment "circleID".  


The segment ID for each segment doesn't change, regardless of any additions or deletions of other segments.


SPRITES
Segments and flushing do not apply to sprite graphics placed on the screen with DRAWSPRITES.  If sprite graphics are flushed by using GETBMP, DRAWBMP, FLUSH then this sequence does constitute a drawing segment.


DISCARD
This causes all items drawn since the last flush to be removed from memory immediately.  Discard does not force an immediate redraw, so the items that have been discarded will still be displayed until a REDRAW (see REDRAW below).

syntax:  print #handle, "discard"

as it might appear in a program:

print #1, "discard"


REDRAW
This command has two forms.  The first form will cause the window to redraw all flushed drawn segments.  Any deleted segments will not be redrawn (see DELSEGMENT below).  Any items drawn since the last flush will not be redrawn either, and will be lost.

syntax:  print #handle, "redraw"

as it might appear in a program:

print #1, "discard; redraw"

The second form of the command allows us to redraw a specific drawing segment.  

Syntax:  syntax:  print #handle, "redraw "; idNum

For instance, let's imagine that we've filled the screen with blue and drawn a pink circle and a green box in the first drawing segment, which we close with a FLUSH command.  We issue other drawing commands later in the program, but later still, we went to display that first segment again.  Knowing that this is the first segment, we can issue a REDRAW command like this:

print #1, "redraw 1"

Any segment may be redrawn in this fashion, as long as it hasn't been deleted with DELSEGMENT (see below), or by the CLS command.  We can retrieve the ID of a drawing segment and store it in a variable for use later with the SEGMENT command, which is explained above.  If we have placed a particular segment ID into a variable called "myID", then we redraw that segment like this:

print #1, "redraw ";myID


DELSEGMENT
This causes the drawn segment identified to be removed from the window's list of drawn items.  The memory that was consumed by the drawn segment is reclaimed by the operating system.  When the window is redrawn, the deleted segment will not be included in the redraw.  It will not be possible to redraw this segment with the REDRAW command.

Syntax:  print #handle, "delsegment n"

As it might appear in a program:

with hard-coded ID number:
print #1, "delsegment 2"

or with ID number contained in a variable:
print #1, "delsegment ";myID



DEMO
The following small program illustrates how to flush graphics, how to obtain segment IDs, how to discard grahics, and how to redraw individual segments.

nomainwin
button #1.redraw, "Redraw",[newDraw],UL,10,10, 120,24
open "Flush and Redraw Demo" for graphics_nf_nsb as #1
#1 "trapclose [quit]"

#1 "down; fill yellow; size 5"
#1 "color darkgreen;backcolor green"
#1 "place 10 70;boxfilled 200 200"
#1 "place 30 120;\First Segment"
#1 "segment boxID"
#1 "flush"  'end first segment

#1 "fill pink;color yellow"
#1 "backcolor darkcyan"
#1 "place 150 150; circlefilled 100"
#1 "place 100 140;\Second Segment"
#1 "segment circleID"
#1 "flush"  'end second segment

#1 "fill blue; color lightgray"
#1 "backcolor darkred;place 150 150"
#1 "ellipsefilled 250 200"
#1 "place 50 140;\Third Segment, not flushed"
#1 "discard"    'discard these commands from memory

wait

[newDraw]
if currentID=circleID then
    #1 "redraw ";boxID
    currentID=boxID
else
    #1 "redraw ";circleID
    currentID=circleID
end if
wait

[quit]
close #1:end
---------------------------------------------------------
FLAT TOOLBAR WITH TOOLTIPS FOR THE OPEN SOURCE EDITOR
	- upper intermediate level
	- editor code is open source, public domain
	- this article copyright 2002, Alyce Watson

Before we begin this tutorial, please know that creating a toolbar with tooltips is a fairly complex process.  It is possible to do it with great ease with a DLL made especially for the purpose by Dennis McKinney.  Get it at the Liberty Belle:   http://libertybelle.0catch.com


We can add a real Windows toolbar to our open source Liberty BASIC Editor!  This type of toolbar has flat buttons that appear to raise up when the mouse hovers over them.  We can also add tooltips to the toolbar buttons.  Toolbars and tooltips are part of comctrl32.dll.  Here is how we do it, step-by-step:


STEP ONE - create hidden buttons 
We need to create some genuine Liberty BASIC buttons so that we have access to the button event handlers, which are the branch labels.  We'll hide these buttons by giving them a position offscreen and a width and height of zero.  We will never see these buttons in the Open Source Editor, but they are essential to the operation of the toolbar.

    Button #1.hide0, "",  [new],        UL, -400, -400, 0, 0
    Button #1.hide1, "",  [open],       UL, -400, -400, 0, 0
    Button #1.hide2, "",  [save],       UL, -400, -400, 0, 0
    Button #1.hide3, "",  [saveas],     UL, -400, -400, 0, 0
    Button #1.hide4, "",  [print],      UL, -400, -400, 0, 0
    Button #1.hide5, "",  [run],        UL, -400, -400, 0, 0
    Button #1.hide6, "",  [debug],      UL, -400, -400, 0, 0
    Button #1.hide7, "",  [maketkn],    UL, -400, -400, 0, 0
    Button #1.hide8, "",  [paint],      UL, -400, -400, 0, 0
    Button #1.hide9, "",  [winfile],    UL, -400, -400, 0, 0
    Button #1.hide10, "", [notepad],    UL, -400, -400, 0, 0
    Button #1.hide11, "", [calculator], UL, -400, -400, 0, 0
    Button #1.hide12, "", [help],       UL, -400, -400, 0, 0
    Button #1.hide13, "", [quit],       UL, -400, -400, 0, 0



STEP TWO - bitmap for the toolbar
We need a bitmap for our toolbar and it must be created properly.  This is not difficult.  For best results, make this bitmap a 16-color bitmap.  After creating the bitmap in your favorite Paint program, be sure to "save as" and choose the 16-color format.  You may instead choose the 256-color format, but that is the maximum number of colors to use if the toolbar is to appear correctly.

Make the toolbar bitmap 16 pixels tall.  IMPORTANT:  fill the background with the traditional, default, Windows lightgray color!  This color will appear transparent on the toolbar, so that the button images appear directly on the toolbar, rather than in rectangular boxes.  This transparency is the reason we save the bitmap as a 16-color bitmap.

Each button image should be 16 pixels wide and 16 pixels high.  Do not leave any space between the button images!  If you have two buttons on the toolbar, your toolbar bitmap will be 16 pixels high and 32 pixels wide.  If you have four buttons, then it will be 64 pixels wide, and so on.

    'do not attempt to create toolbar if bmp is missing
    if FileExist(DefaultDir$,"opensrc.bmp")=0 then wait

We'll need a handle to our toolbar bitmap, which we will pass as an argument to the function that creates the toolbar.  We could load the bitmap with Liberty BASIC's LOADBMP command, but we will instead load it with an API call - LoadImageA.  This function asks for a width and height to give the bitmap, and it will be loaded at those dimensions.  If we use 0 for these arguments, the bitmap will be loaded at its own default dimensions.  The function returns a handle to the loaded bitmap.  We need to be careful when specifying the load flags.  We must use the _LR_LOADFROMFILE flag to tell the function to look for a file on disk.  It is also really important to use the _LR_LOADMAP3DCOLORS flag.  When this flag is used, the function searches the color table for the image and replaces the following shades of gray with the corresponding 3D color:
 
Color		Replaced with
Dk Gray, 	RGB(128,128,128)	COLOR_3DSHADOW
Gray, 		RGB(192,192,192)	COLOR_3DFACE
Lt Gray, 	RGB(223,223,223)	COLOR_3DLIGHT

This means that the bitmap is loaded to match the system default colors.  If a user has an alternate color scheme than the standard Windows desktop colors, and the _LR_LOADMAP3DCOLORS flag is not used, then the images for the buttons will probably appear within their lightgray background boxes.  If the flag is used, then the images for the buttons will match the colors in force on the system, giving a very professional look.  See issue #100 for more information on bitmap color tables.

The flag for  _LR_LOADTRANSPARENT is also used.  This flag causes the LoadImageA function to retrieve the color value of the first pixel in the image and replace the corresponding entry in the color table with the default window color (COLOR_WINDOW). All pixels in the image that use that entry become the default window color. This value applies only to images that have corresponding color tables. If the flag includes both the LR_LOADTRANSPARENT and LR_LOADMAP3DCOLORS values, LR_LOADTRANSPARENT takes precedence. However, the color table entry is replaced with COLOR_3DFACE rather than COLOR_WINDOW.

Here is the LoadImageA function:

    flags=_LR_LOADFROMFILE or _LR_LOADMAP3DCOLORS _
        or _LR_LOADTRANSPARENT

    CallDLL #user32, "LoadImageA",_
    0 As long,_			'instance handle, can be null
    bmp$ As ptr,_		'file name of bitmap
    _IMAGE_BITMAP As ulong,_	'type of image to load
    0 As long,_ 		'desired width, 0=default
    0 As long,_			'desired height, 0=default
    flags As long,_		'load flags
    hbmpTools As ulong		'handle to loaded bitmap

 

STEP THREE - define constants and structs
Liberty BASIC recognizes many Windows constants and will subsitute their values when they are typed with an underscore.  The Windows constant for SRCCOPY is recognized by Liberty BASIC when it is written like this:  _SRCCOPY .  There are some Windows constants that are not recognized by Liberty BASIC, and trying to access them will cause the compiler to halt with an "undefined Windows constant" error.  If this happens, then we must use the actual value of the constant.  Here are some values we must define in order to use a toolbar:

    TB.ADDBUTTONS    = 1044
    TB.SETTOOLTIPS   = 1060
    TBSTYLE.BUTN     = 0
    TBSTYLE.FLAT     = 2048
    TBSTYLE.TOOLTIPS = 256

We will also need a struct that will hold information about the toolbar buttons:

    struct TBBUTTON,_
    bmpID As long,_ 'index of bitmap
    cID As long,_   'command ID
    State As long,_ 'button state
    Style As long,_ 'button style
    dwData As long,_'not used
    Str As long     'not used



STEP FOUR - get IDs for the hidden buttons
Remember those hidden Liberty BASIC buttons?  We use them now.  We make a call to GetWindowLongA with the flag _GWL_ID to get the Windows ID for each of these buttons.  Here are the first two.  The GetWindowLongA function returns the ID of the button.  We'll store each ID in a separate variable for use later.

    hbutton=hwnd(#1.hide0)
    CallDLL #user32, "GetWindowLongA",_
    hbutton As long,_
    type As long,_
    hID0 As long

    hbutton=hwnd(#1.hide1)
    CallDLL #user32, "GetWindowLongA",_
    hbutton As long,_
    type As long,_
    hID1 As long



STEP FIVE - create the toolbar control 
The first thing we must do is fill the TBBUTTON struct.  The members are filled as indicated below.  The first toolbar button is initialized when the toolbar is created.  All other buttons are added later:

    TBBUTTON.bmpID.struct = 0              'index of first bitmap
    TBBUTTON.cID.struct = hID0             'ID of first button
    TBBUTTON.State.struct = 4              'enabled
    TBBUTTON.Style.struct = TBSTYLE.BUTN   'style
    TBBUTTON.dwData.struct = 0             'not used
    TBBUTTON.Str.struct = 0                'not used

Notice the TBBUTTON.cID.struct is given the ID we retrieved for our first hidden Liberty BASIC button.  Now, when a user clicks the first toolbar button, our Liberty BASIC branch label event handler for that button will be activated!

We'll set up the style for the toolbar.  It will be a child window of our LB window, and it will be visible.  We also want the FLAT style of buttons, rather than the raised style, and we'd like our toolbar to have a border.  Notice that we are using the TBS.TOOLTIPS style.  This allows us to add tooltips to our buttons, for a really professional appearance.  Here is the CreateToolbarEx function.  Each argument is explained.  The function returns the handle to the toolbar.

    style=_WS_CHILD Or _WS_VISIBLE Or TBSTYLE.WRAPABLE Or TBSTYLE.FLAT _
        Or _WS_BORDER or TBSTYLE.TOOLTIPS
    uStructSize = Len(TBBUTTON.struct)
    CallDLL #comctl32, "CreateToolbarEx",_
        hMain As long,_     'parent handle
        style As long,_     'window style flags
        0 As long,_         'ID
        14 As long,_        'number of Bitmaps
        0 As long,_         'hBMInst-not used
        hbmpTools As long,_ 'bitmap handle
        TBBUTTON As struct,_'toolbar button struct
        1 As long,_         'number of buttons to start
        16 As long,_        'width buttons
        16 As long,_        'height buttons
        16 As long,_        'width bitmaps for buttons
        16 As long,_        'height bitmaps for buttons
        uStructSize As long,_
        hTB As long         'handle to toolbar


STEP SIX - create the toolip control
If we want the toolbar buttons to display tooltips, then we must create the tooltip control before adding any buttons to the toolbar.  We explained tooltips pretty thoroughly when we first added them to the Open Source Editor in issue #60, so please refer to that issue for more details.  First, we set up some values that will be needed in creating the tooltip control:


    TTF.IDISHWND    = 1
    TTF.CENTERTIP   = 2
    TTF.SUBCLASS    = 16
    TTM.ADDTOOL     = 1028
    TTM.DELTOOL     = 1029
    TTS.ALWAYSTIP   = 1
    TTS.NOPREFIX    = 2
    style           = _WS_POPUP or TTS.NOPREFIX _
                      or TTS.ALWAYSTIP

Next, we retrieve the instance handle of the parent window:

    calldll #user32, "GetWindowLongA", _
        h as long, _GWL_HINSTANCE as long,_
        hInstance as long

And now we create the tooltip control.  It is not a visible control, like a button or textbox, but rather a control that manages the tooltips for us in the background.  It knows when the mouse is hovering over a button or other control, and it displays the tooltip for a few seconds, then hides it again.  The CreateWindowExA must create a window os "TOOLTIPS_CLASS32" and it returns the handle to the tooltip control.

    calldll #user32, "CreateWindowExA",_
        _WS_EX_TOPMOST as long,_
        "TOOLTIPS_CLASS32" as ptr,_
        "" as ptr, style as long,_
        _CW_USEDEFAULT as long,_CW_USEDEFAULT as long,_
        _CW_USEDEFAULT as long,_CW_USEDEFAULT as long,_
        h as long, 0 as long, hInstance as long,_
        0 as long,hwndTT as long



STEP SEVEN - add tooltips to the tooltip control
We need a struct to hold the tooltip information.  Here it is:

    struct toolinfo, cbSize as long, uFlags as long,_
        hwnd as long, uId as long, left as long, top as long,_
        right as long, bottom as long, _
        hInst as long, lpstrText$ as ptr

We need to fill members of the struct before we add each tooltip.  Some things remain the same for each tooltip, so we fill those struct members only once.  A change form the previous method of doing tooltips for the Open Source Editor - instead of using the window handle, we fill that struct member with the toolbar handle.  We also remove the flag that indicated that the ID for the tooltip is the handle of the control to which it is assigned.  Instead of a control ID, we'll use the pixel coordinates of each button to tell the tooltip where it belongs.  We need to fill the "top" and "bottom" coordinate members only once, since all buttons are in a horizontal line and share these values:

    'THESE STRUCT MEMBERS ONLY NEED TO BE FILLED ONCE:
    toolinfo.cbSize.struct = len(toolinfo.struct)
    toolinfo.uFlags.struct = TTF.SUBCLASS
    toolinfo.hwnd.struct = hTB
    toolinfo.top.struct=0     'top location on toolbar
    toolinfo.bottom.struct=22 'bottom location on toolbar

We need to fill some members of the struct differently for each tooltip.  Each will have a different caption, and a different left and right location on the toolbar.  Each left location is 23 pixels greater than the one preceding it, since that is the "finished" width of the buttons.  The right location is 22 pixels greater than the left location for each tooltip/toolbar button.  Once we've filled the struct as desired, we send a message to the toolbar with SendMessageA that adds the tooltip.

    'THESE STRUCT MEMBERS NEED TO BE FILLED ANEW
    'FOR EACH TOOTLIP ADDED:
    toolinfo.lpstrText$.struct = "New File"
    toolinfo.left.struct=0      'initial left location
    toolinfo.right.struct=22    'add 22 to left for right
    calldll #user32, "SendMessageA", hwndTT as long,_
    TTM.ADDTOOL as long, 0 as long,_
    toolinfo as struct, re as long

    toolinfo.lpstrText$.struct = "Open File"
    toolinfo.left.struct=23    'add 23 to previous left location
    toolinfo.right.struct=45   'add 22 to left for right
    calldll #user32, "SendMessageA", hwndTT as long,_
    TTM.ADDTOOL as long, 0 as long,_
    toolinfo as struct, re as long
 
(And so on for all tooltips desired.)



STEP EIGHT - add the tooltip control to the toolbar
Remember, we must add the tooltip control before adding buttons to toolbar.  After the tooltip control is created, we added by sending the toolbar a message.

    CallDLL #user32, "SendMessageA",_
    hTB As long,_		'toolbar handle
    TB.SETTOOLTIPS As long,_	'message to set tooltips
    hwndTT As long,_		'handle of our tooltip control
    0 As long,_			'not used
    result As long



STEP NINE - add buttons to the toolbar
We're almost done!  Just as we had to fill the tooltip struct before adding each tooltip to the control, so must we also fill the toolbar button struct before adding each toolbar button.  It requires the image index for each button.  This is a zero-based index.  Our first bitmap image was at index 0, and that was set when we created the toolbar.  The next image is at index 1, and so on.  We also need to fill the struct member that contains the ID of the hidden Liberty BASIC button that is associated with each toolbar button.

    'fill toolbar button struct with new info to add buttons:
    TBBUTTON.bmpID.struct = 1  'bitmap image index
    TBBUTTON.cID.struct = hID1 'ID of button

    CallDLL #user32, "SendMessageA",_
    hTB As long,_		'toolbar handle
    TB.ADDBUTTONS As long,_	'message to add button 
    1 As long,_			'number of buttons to add
    TBBUTTON As struct,_	'toolbar button struct
    r As long

    TBBUTTON.bmpID.struct = 2  'bitmap image index
    TBBUTTON.cID.struct = hID2 'ID of button

    CallDLL #user32, "SendMessageA",_
    hTB As long,_		'toolbar handle
    TB.ADDBUTTONS As long,_	'message to add button 
    1 As long,_			'number of buttons to add
    TBBUTTON As struct,_	'toolbar button struct
    r As long

(And so on for all of the desired buttons.)

Please see the Open Source Editor version 32_04 that is included in the zip archive attached to this newsletter.

WRAPABLE TOOLBARS
The toolbar we created for our editor will not wrap when the window is made narrower than the toolbar.  A wrapable toolbar will add rows and move some buttons down so that all buttons are visible when the window width becomes too narrow to display all buttons in a single row.  To make a wrapable toolbar, add the following style bit to the style flag used to create the toolbar:

    TBSTYLE.WRAPABLE = 512

For the bar to wrap, we have to sent it a _TB_AUTOSIZE message in our resizehandler

[resize]
    TB.AUTOSIZE      = 1057

    CallDLL #user32, "SendMessageA",_
    hTB As word,_		'toolbar handle
    TB.AUTOSIZE As word,_	'message to resize itself 
    0 As long,_			'0
    0 As long,_			'0
    r As long

We haven't made our Open Source Editor toolbar wrapable because it would require a lot more code in the resizehandler, and the method described in this article is already fairly complex!  The x, y coordinates for many of the buttons would change when the toolbar wrapped, and we'd need to determine new coordinates and make changes to our tooltips so that they would still display with their associated buttons.  This isn't as important for a toolbar with a relatively small number of buttons, but it should be taken into account when creating a toolbar with many buttons.

EASIER TOOLBARS
LB Workshop contains a Toolbar Maker that allows you to select from many standard button images, or from images of your own.  After selecting an image, you may also specify a branch label for the button and the tooltip text.  The Toolbar Maker automatically saves your toolbar bitmap in 256-color format and creates the code for the toolbar with tooltips in a sample window, complete with stubs for all button branches.

http://iquizme.0catch.com/lb/lbw3/lbw3.html

Dennis McKinney has created an extremely versatile toolbar DLL.  Here is the way it is described at the Liberty Belle:

-The most versatile toolbar dll available. 
-Rewritten to take advantage of LB3 callbacks. 
-No extra hidden buttons needed. Cleaner, more efficient LB code. 
-Create almost any type of toolbar you need, horizontal, vertical, or wrapable. 
-Can be used with floating toolwindows. 
-Group different styles of buttons. 
-Built-in tooltips. Use one string to create the tips for the whole toolbar. 

Grab your copy here:

http://libertybelle.0catch.com/lb3code.htm
---------------------------------------------------------
TRANSLATING 32-BIT VISUAL BASIC API CALLS
	- intermediate-advanced level
	- by Alyce Watson, copyright 2002

Brad Moore wrote a detailed article on making API calls for the last newsletter.  It isn't possible to include every bit of information on this complex subject in a single article.  The subject would fill a good-sized book, and in fact there are quite a lot of books available.  For those who cannot spend the small fortune required to purchase many of these books, there are plenty of free, online references available as well.  The best starting place is the Microsoft Developer Network, or MSDN.  It is notoriously difficult to navigate, but it is free and it is packed with information in the form of references, articles, and sample code.  Find it here:  http://msdn.microsoft.com/  For an API reference, click the link for the Platform SDK.  SDK is another of those ubiquitous three letter acronyms (TLA's!) that stands for Software Developer's Kit.

Besides being very large and difficult to navigate, the MSDN documents API calls in C syntax.  It is often easier for Liberty BASIC programmers to understand Visual BASIC syntax.  There are many Visual BASIC websites with information of all kinds, including API information.  My favorite is "The All API Net."

This article was requested by a reader.  It is intended for readers who have some experience making API calls, and for those who have read and understood Brad's fine article in issue #101.  It is by no means a comprehensive reference or tutorial on translating Visual BASIC API calls.  It is meant to be a starting point.  Please remember that argument types may need tweaking.  What may be passed "As Long" in Visual BASIC may need to be passed "As Ulong" in Liberty BASIC.  If you attempt to call an API function and you get a "key is missing" error from Liberty BASIC, this either means that you have omitted an argument, or that one of your arguments is of the wrong type.  In this case, check the MSDN and other sources and do some experiementation.  Check Brad's article in issue #101 for a list of types to use with API functions and structs.

Perhaps some kind reader will volunteer an article for a future newsletter on translating API calls from C to Liberty BASIC?  It would be most welcome.

And now, we'll move on to the translation from Visual BASIC.  (I know, you wondered if we'd ever get here!)  For an up-to-date copy of the Windows 32-bit API list in Visual BASIC syntax, visit the All API Net, here:  http://www.allapi.net

Download and install the API Viewer.  You may also want to get the API Guide as a helpful reference, but it is the API Viewer that contains a comprehensive list of functions, constants and structures.

Open the win32 file in the viewer and choose a function.  For explanation purposes, we'll use the MessageBox function.  Here it is in Visual BASIC syntax:

Declare Function MessageBox Lib "user32" Alias "MessageBoxA" (ByVal hwnd As Long, ByVal lpText As String, ByVal lpCaption As String, ByVal wType As Long) As Long

In code, it will appear as a single line, just as Liberty BASIC API calls must be a single line.  Let's take it apart and translate it into a form that Liberty BASIC can understand.

We can ignore this part:
	Declare Function 

This part tells us which DLL contains the function:
	Lib "user32"	
For us, we change this to 
	calldll #user32,

This is the name that Visual BASIC gives the function:
	MessageBox  

Liberty BASIC cannot assume that this is the function name.  We must check and see if the function has an "alias" name, which is the real name of the Windows function.  In this case, there IS an "alias" name, and this is the one we use:
	Alias "MessageBoxA" 

We now have, for LB:
	calldll #user32, "MessageBoxA",

Now comes the list of arguments, which follows the opening bracket.  Hungarian notation is usually used to describe the arguments.  See the latter part of this article for more on Hungarian Notation.  The "ByVal" modifier means that the argument is passed by value, not as a pointer (by reference).  The "hwnd" means handle, and "As Long" is translated "As Long".
	(ByVal hwnd As Long, 

For LB:
	hwnd as long,

A "String" in Visual BASIC is passed "as ptr" in Liberty BASIC.  The "lp" in "lpText" stands for "long pointer".	
	ByVal lpText As String, 

For LB:
	lpText$ as ptr,

Although it isn't used in this particular API call, if a string is passed "ByRef", it is passed as a memory location that can be altered by the API function.  Liberty BASIC automatically adds a null terminator to strings that we pass as pointers.  If we want to tell Liberty BASIC to pass a string "ByRef", we add our own null terminator.  This is simply a signal to Liberty BASIC.  We do not need to pass a string this way for our message box call, but as an example, to pass a string "ByRef":
	lpText$=space$(100)+chr$(0)
	and then in the call
	lpText$ as ptr,

The next argument is similar to the previous one:
	ByVal lpCaption As String, 

In LB:
	lpCaption$ as ptr,

The "wType" argument is passed in VB "As Long".  The "w" can signify "word" and it may be possible to pass this value "As Word" in Liberty BASIC.  If a function translation gives an error, try changing to a different type for some of the arguments.  A "long" is a signed value.  A "ulong" is an unsigned value.  Both types are 4-bytes, but the type "long" reserves one bit for the sign, either positive or negative.  The type "ulong" being unsigned, doesn't reserve a bit for the sign, so it can contain a larger number than a "long", but it can only contain a positive number.  Type flags are generally unsigned, so passing as "word" or "ulong" is advised.
	ByVal wType As Long

In LB:
	wType As Long
	or
	wType As uLong
	or
	wType As word

And last, we have the value given after the closing bracket:
	) As Long
This is the return from the function.  If there is no value after the closing bracket, the API call doesn't return a value.  Liberty BASIC always requires a return-type argument, so if there is no value returned, use "result as void".  For this one, though, use:
	
	result as long

The entire function
calldll #user32, "MessageBoxA",	hwnd as long,lpText$ as ptr,lpCaption$ as ptr,wType As uLong, result as Long

Note that the api call must be all on one line in the code!  Here is a working program that uses the line continuation character to list the arguments on separate lines.  Please remember that this article is not meant to explain the use of any individual API function, but to give some guidelines for translating from VB to LB syntax.  Issue #51 contains a message box tutorial by Larry Dunham.  The old tutorial is for 16-bit Liberty BASIC 1.4x or 2.0x, but with the information in this article, I'm sure that you can update the methods for 32-bit LB3 easily!

lpText$="An error has occured!"
lpCaption$="Oh No!"
wType=_MB_ABORTRETRYIGNORE or _MB_ICONHAND

calldll #user32, "MessageBoxA",_
hwnd as long,_
lpText$ as ptr,_
lpCaption$ as ptr,_
wType As uLong,_
result as Long

select case result
    case _IDABORT
    print "Abort button was selected."

    case _IDCANCEL
    print "Cancel button was selected."

    case _IDIGNORE
    print "Ignore button was selected."

    case _IDNO
    print "The No button was selected."

    case _IDOK
    print "OK button was selected."

    case _IDRETRY
    print "Retry button was selected."

    case _IDYES
    print "Yes button was selected."
end select

***********************************
TYPES/STRUCTS

Although we didn't need a struct in the MessageBoxA call, we do need them for some API functions.  In Visual BASIC, you will find them called "Type" rather than "Struct".  Here is a typical Type in VB syntax as retrieved from the All API Net Viewer:

Private Type POINTAPI
	x As Long
	y As Long

End Type

In Liberty BASIC, we must either list a struct and its members on a single line, or use the underscore line continuation character.  The Type above would look like this as a Struct:

struct POINTAPI, x as long, y as long

In LB, we start with the keyword "struct", then include the name we are giving to the struct, followed by a comma, then a comma separated list of struct members, along with their types.  We can spread the struct declaration over multiple lines by using the line continuation character:

struct POINTAPI,_
    x as long,_
    y as long

When referring to Type members in Visual BASIC, use this syntax:
POINTAPI.x = 47

In Liberty BASIC syntax:

POINTAPI.x.struct = 47

The method above assigns a value to a struct member.  To retrieve a value that has been placed there by an API function, do this:

print POINTAPI.x.struct       'print the value
or
x1 = POINTAPI.x.struct        'assign value to a variable
or 
print 2 * POINTAPI.x.struct   'use in a calculation

To pass a struct into an api call:

	POINTAPI as struct,

Earlier we discussed the difference between passing a string by reference and by value.  "By value" passes the contents of the string to the function.  "By reference" passes the actual memory address of the string so that the function can modify the value.  When we need to pass a numeric value by reference, rather than by value, we place it in a struct.  The function can then modify the number contained in the struct.  For an example of this at work, look at the piano.bas program that ships with Liberty BASIC.

***********************************
CONSTANTS

Liberty BASIC recognizes many Windows constants and will supply the proper value when they are preceded by an underscore.  For instance, the constant _MB_OK is recognized by Liberty BASIC and can be used as such.  If LB gives you an "undefined Windows constant" error, then use the API Viewer to look up the value of the constant.  It will be given as a hexadecimal number.  The API Viewer output will look like this:

Private Const MB_ABORTRETRYIGNORE = &H2&
Private Const MB_OK = &H&
Private Const MB_YESNOCANCEL = &H3&
Private Const MB_YESNO = &H4&
Private Const MB_USERICON = &H80&
Private Const MB_USEGLYPHCHARS = &H4
Private Const MB_TYPEMASK = &HF&
Private Const MB_TOPMOST = &H40000&

To determine a value, you can set up a constant of your own using the hexdec() function:

MB.ABORTRETRYIGNORE = HEXDEC("&H2&")

Be sure to put the hex string within quote marks.  You may determine the decimal value like this:

print HEXDEC("&H2&")

OOOPs.  LB has a slight bug in the hexdec function.  The "&" character following the "2" is perceived to be a number (since it isn't, LB incorrectly supplies a zero in this spot), and printing the above will give you a value that is incorrect by a power of 1, assuming that the "2" is in the "16's" column, it will give you "32" for an answer.  Be sure to remove trailing characters, like this:

print HEXDEC("&H2")

The value for this one is 2.  You may then substitute the decimal value

MB.ABORTRETRYIGNORE = 2

To discover if LB recognizes a constant, try printing it:

print _MB_ABORTRETRYIGNORE

The above will print 
2

***********************************
VISIT THESE WEBSITES FOR MORE ON HUNGARIAN NOTATION
http://www.gregleg.com/oldHome/hungarian.html
http://web.umr.edu/~cpp/common/hungarian.html

---------------------------------------------------------
Event Driven Programming Concepts - Part 1
by Brad Moore, copyright 2002 - all rights reserved

A real world event -

A gray morning began to develop outside my window.  The light pitter patter of rain droplets beat upon my window as it was driven by a brisk fall wind (hey, this is Oregon!).  Tucked snuggly in bed, covers tight about my neck, I shivered slightly at the thought of the wet morning that waited for me.  For now, though, a pleasant dream drifted through my mind as I slept.  Then it happened.  From somewhere out in the ether, just inches from my head a horrifying buzz permeated my morning slumber.  There is was - the alarm clock ringing in the morning.  It was time to reluctantly terminate my restful sleep and begin a new task - preparing for the morning.

This is the first of several articles about event driven programming.  What you just read is a real world account of an event.  In the little story I was asleep and then suddenly I was awakened by an alarm clock.  Once awakened I began to perform another task.  The alarm clock ringing was the event trigger.  It was what signaled to me that I needed to stop what I was currently doing (sleeping) and begin doing something else.

Programming: Event Based versus Lineal -

For many people the concept of event driven programming is quite foreign.  This is particularly true of those who are coming from lineal languages such as Quick Basic or Fortran.  In these programming languages we begin the program on line one and the program progresses through each of the lines from the top to the bottom.  Maybe some user intervention causes a branching of the code, but the program is always moving from top to bottom in a lineal fashion.

Events differ from basic user intervention in one significant way.  They can not be predicted.  I will explain this a bit latter.  For now let us look at a basic lineal program.  This one is written in LB3 using the mainwin as the interface.

   'Example of Basic lineal program
  [menu]
   cls
   print
   print " *******  Temperature Conversion  *******"
   print
   print "   1) Convert Fahrenheit to centigrade"
   print "   2) Convert centigrade to Fahrenheit"
   print "   3) Quit"
   Print
   Print "   Enter Selection -> ";
   input a$
   a = val(a$)
   if a = 1 goto [F2C]
   if a = 2 goto [C2F]
   if a = 3 then
     print
     print "Thanks for using the tool"
     end
   end if
   goto [menu]

  [F2C]
   print
   print "Fahrenheit to centigrade conversion"
   print
   print "Enter Fahrenheit value -> ";
   input a$
   a = val(a$)
   if a$ <> "0" and a = 0 then goto [menu]
   print
   print a$;" degrees Fahrenheit is equal to ";
   print using("###.#",str$((a - 32) * 5/9));
   print " degrees centigrade."
   print
   print "Press enter to continue...";
   input a$
   goto [menu]

  [C2F]
   print
   print "centigrade to Fahrenheit conversion"
   print
   print "Enter centigrade value -> ";
   input a$
   a = val(a$)
   if a$ <> "0" and a = 0 then goto [menu]
   print
   print a$;" degrees centigrade is equal to ";
   print using("###.#",str$(a * 9/5 + 32));
   print " degrees Fahrenheit."
   print
   print "Press enter to continue...";
   input a$
   goto [menu]

This program features a menu for the user to select a function and it branches based on the user input.  One of the trademarks of this form of program is that the system (Liberty Basic in this case) is instructed to sit and wait for one  and only one expected unit of user input at each location where user intervention is required.  The code above has five input statements.  Each of them expects certain qualified data to be entered.  Data that does not fit that qualified list of entries causes an error condition.  This introduces inflexibility into the program.  There is no way for me to effectively exit the program if I did not chose that option from the main menu.  Once the program begins asking me for a temperature value I have no choice but to enter one.

Looking into Events -

Event driven programs are significantly different in this area.  Any event that is possible can be triggered at any time.  Lets look at events a little closer and see why.  A basic example is a window with a couple buttons.  Here is the code:

   'Setup
   NoMainWin
   WindowWidth = 166
   WindowHeight = 142
   UpperLeftX = Int((DisplayWidth-WindowWidth)/2)
   UpperLeftY = Int((DisplayHeight-WindowHeight)/2)
   'Add buttons
   Button #main.bt1, "Button  1",[bt1],UL, 25, 20, 105, 25
   Button #main.bt2, "Button  2",[bt2],UL, 25, 55, 105, 25
   'open the window
   Open "Window" For Window As #main
   'trap the close event (hey! this is an event!)
   Print #main, "trapclose [quit]"

  [loop]
     Wait

  [quit]
     Close #main
     End

  [bt1]
     'This does nothing really
     GoTo [loop]

  [bt2]
     'This does nothing really
     GoTo [loop]

Pretty simple code.  Open a window with two buttons.  What we want to see is that each of them  points to their own program branch where program events are handled.  These are the event handlers.  In the example above, when the program executes the code at these branches, the program to simply returns back to the main loop.  There is a third event handler in the code.  It is the branch labeled quit, which closes the program if the user clicks on the `X' in the upper right of the window.

Each of these events are primed in the code.  In the case of the buttons we have specified the event handler as a branch called [bt1].  This is required to actually do something when the event is triggered.  Any valid branch name can be used.  This is the "address" of where the program execution will be directed when the event is actually triggered.  Here is that line of code

  Button #main.bt1, "Button  1",[bt1],UL, 25, 20, 105, 25

Once the event is primed we cause the program to drop into a waiting state with the Liberty Basic command WAIT.  This command will suspend program execution and wait until an event is detected for which there is an event handler setup.  (By the way Liberty Basic will choke if you have control that specifies an event handler and the required label is not present when the program tries to execute it.)  There is a lot of windows specific stuff that happens behind the scenes in the process of detecting the event trigger (we will look a bit deeper at that in our next installment).  Until then, accept on faith that an event will be triggered when the user clicks the button labeled "Button 1" with their mouse.  The trigger is like the alarm clock in the introductory scene.

Liberty Basic, having detected the event then examines the internal structures it has assembled and determines that program execution should be handled by the code that follows the branch [bt1].   This is like the new actions that take place AFTER the alarm clock rings - specifically I get out of bed to face another wet, gray Oregon morning.  In the case of the button example the following code is executed:

    'This does nothing really
    GoTo [loop]

Obviously this code does nothing.  It simply returns the program back to the wait state that it came from.   As we will see in a minute, event driven programs do not have the shortfalls of the lineal forms of programming.  But why is a graphic oriented program by nature event driven and why is a non-graphic oriented program usually lineal?

Why is a GUI Usually Event Driven? -

It is a good question.  When one understands this concept then you have made the critical transition into the world of event driven programming.  The reason is simple.  I have alluded to it already.  To accomplish what event driven programming accomplished in the lineal programming paradigm you would have to write a user response evaluation routine to test each and every response the user could supplied to any given input statement.  This is necessary to insure that the user did not want to do something other than what the programmer expected the user to do at a given point in the code.

For instance if the user is expected to input a value for Centigrade, but instead types "Quit"  then you would have to know how to handle "Quit" for that input statement (not to mention the other input statements too).  What if the user typed "EXIT" instead?  The possibilities are numerous - so the programmer sticks to the required input, validates it and rejects all others.  This is typical in linear programming.

In the event driven world the programmer is not required to interpret the user input because the environment has defined what that input will be.  Buttons for actions, fields for data and traps for menus and window functions.  Each of these has definable parameters and each is handled behind the scenes with a single MACRO event handler which is embedded in the WAIT, INPUT and SCAN commands.  Events simply trigger pre-built event handlers.  We discussed them.  It all happens as a result of the environment.  It is defined by the Graphical User Interface specifications and the syntax for each command and control.  As we saw in the button command - there is a place to tell the environment where to go to do the thing that needs done.  It does not get any easier than that!

GUI Based Temperature Conversion Program -

To see these concepts in a little more action let's take a look at a GUI (graphical User Interface) version of our temperature conversion program.

   '** Temperature Conversion
   '   Should work in LB2 and LB3
   '   by Brad Moore
   '   released to the public domain Oct 2002

   'Window Setup
   NoMainWin
   WindowWidth = 276
   WindowHeight = 187
   'Center the window
   UpperLeftX = Int((DisplayWidth-WindowWidth)/2)
   UpperLeftY = Int((DisplayHeight-WindowHeight)/2)

   'Control Setup
   Statictext  #main.st1, "Fahrenheit", 25, 20, 75, 16
   Statictext  #main.st2, "Centigrade", 25, 45, 75, 20
   Button      #main.F2C, "Fahrenheit -> Centigrade", _
                          [F2C],UL, 20, 75, 230, 25
   Button      #main.C2F, "Centigrade -> Fahrenheit ", _
                          [C2F],UL, 20, 105, 230, 25
   Textbox     #main.tb1, 110, 15, 137, 24
   Textbox     #main.tb2, 110, 40, 137, 24

   'Open the window
   Open "Temperature Conversion" For Window As #main

   'Trap the close window EVENT (hey! this is an event trap!)
   Print #main, "trapclose [quit]"
   Print #main, "font ms_sans_serif 10"

   'Wait here for events
  [loop]
     Wait

   'Handle the close window event
  [quit]
     Close #main
     End

   'Handle the F to C event
  [F2C]
      'get contents of textbox1
      Print #main.tb1, "!contents? a$"
      'Get the numeric value and insure it is a number
      a = Val(a$)
      If a$ <> "0" and a = 0 Then
         Notice "A numeric value is required for Fahrenheit"
         GoTo [loop]
      End If
      'Display the conversion in the Other textbox
      Print #main.tb2, Using("####.#",Str$((a - 32) * 5/9))
      Wait

   'Handle the C to F event
  [C2F]
      'get contents of textbox2
      Print #main.tb2, "!contents? a$"
      'Get the numeric value and insure it is a number
      a = Val(a$)
      If a$ <> "0" and a = 0 Then
         Notice "A numeric value is required for Centigrade"
         GoTo [loop]
      End If
      'Display the conversion in the Other textbox
      Print #main.tb1, Using("####.#",Str$(a * 9/5 + 32))
      Wait

If you remember our lineal program example of the Temperature Conversion program - once you committed to converting Centigrade to Fahrenheit you were required to follow the process through.  There was no way of changing your mind and converting the other way around.   In the event drive environment we simply enter our data into a textbox and then press a button.  The event for the button is triggered and the answer is displayed.  We could have just as easily typed the value into the field and then pressed the "X" in the upper right corner of the window and triggered the [quit] event handler.

Events and Liberty Basic -

Liberty Basic allows events to be set up on many GUI based commands.  In particular these that follow allow (require) events to be specified by defining a branch label to handle the event:

  *  Button
  *  Checkbox
  *  Combobox
  *  Graphicbox
  *  Listbox
  *  Menu
  *  Popupmenu
  *  Radiobutton

Events are trapped and triggered from your code where you have placed one of the following event detection commands:

  *  Scan
  *  Input
  *  Wait

So far we have used the Wait command, in the code above.  As I described earlier this command simply waits for an event then routes the program executing to a predetermined branch label to handle the event.  The Input command could have been used as well in the same case.  I prefer to use the Wait command because it adds clarity to the code as it implies that the program is waiting for a user triggered event.

The Scan command is a bit more complex.  The Liberty Basic help file says that the Scan command is simply an input command that does not wait.  We will be covering this command in a future installment of this article series, pealing away all the mystery so that it is not so confusing.

We will also be covering event traps and timers in future installments of this article series, so please stay tuned.  For now, if there are questions arising from this discussion, please post them on the Liberty Basic discussion group, or email them direct to me.  Thanks.

(Also find attached to the newsletter a zip file called events-1.zip which contains this newsletter article that is formatted in pretty type, as well as both versions of the temperature calculator programs.)


---------------------------------------------------------
---------------------------------------------------------
SPOTLIGHT ON THE COMMUNITY

The spotlight section of the newsletter highlights a programmer, website, or other item of particular interest to Liberty BASIC programmers.  This month, the spotlight is on THE COMMUNITY!  The last several newsletters have contained many, many contributions from our members.  This is so exciting!  This kind of involvement provides such a wide variety of information that there is always something for everyone!

The publishing team offers a hearty THANKS to all contributors!  Everyone has something of value to contribute, so please keep sending in these great demos, articles and tutorials!

The remainder of this newsletter consists of the many fine community contributions we have received this month, so ENJOY!

---------------------------------------------------------
ODBC IN LIBERTY BASIC - advanced level
	by Dennis McKinney
	http://libertybelle.0catch.com

Editor's note:  ODBC stands for Open Database Connectivity.  Dennis McKinney has developed a method to access ODBC directly in Liberty BASIC, using API calls.  No add-on DLLs are required.  Following, find excerpts from Dennis's messages about ODBC from the Experienced User's forum [lbexp].  Attached to this newsletter, find a sample program file that illustrates the use of ODBC in Liberty BASIC.  Used by permission.

**********************
The definition of ODCB, according to Microsoft:

Open Database Connectivity (ODBC) is a widely accepted application programming interface (API) for database access. It is based on the Call-Level Interface (CLI) specifications from X/Open and ISO/IEC for database APIs and uses Structured Query Language (SQL) as its database access language. 
**********************

Excerpts from Dennis's messages on ODBC:

Liberty BASIC can: interface with Access/VB databases (*.mdb), create these databases, create and modify tables, create fields, assign keys and indexes, save records, retrieve recordsets, execute SQL statements. If this can be done with Access databases it can be done with any database that uses ODBC.

No [add-on] dll [is required] at this point. This is pure Liberty BASIC 3.1.  All that's required is MDAC 2.1 or greater to be installed on your computer.  These are the 32 bit drivers for ODBC. The Jet drivers are also needed. After a certain version they no longer ship with MDAC. I think it's around V2.6. Most Windows computers have these anyway.  They're installed by a number of common programs that people use.

The information I've used to get the ODBC code started comes from the ODBC 3.0 SDK. This is hard to come by in book form but the entire book plus more is on the MSDN site. The start page is 

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/odbc/htm/dasdko/dbcoverview.asp 

The book is listed on this page's menu as ODBC Programmers Reference and the ODBC API is listed there also. 

Info specific to Access can be found at

http://msdn.microsoft.com/library/en-us/odbc/htm/odbcjetmicrosoft_access_driver_/programming_considerations.asp 

A lot of good info can be found by searching the web too.

ODBC is just a batch of API calls mixed with SQL. It's purpose is to allow Windows apps to access data from a variety of databases. This includes mysql databases. My code focuses on Access and VB because that is what I have, and those drivers exist on the majority of Windows computers. The main difference between using ODBC with Access verses other databases is mostly in the SQL syntax used, so the code should be fairly easy to adapt when other databases like mysql are targeted.

There is an excellent online book to learn SQL at

http://members.tripod.com/er4ebus/sql/

At this time the code I have is just a set of functions to handle the basic tasks of ODBC. It's very basic and needs a lot of development. As soon as I can get a simple gui put together for it I'll put it in the files section here at lbexp. If you're like me, a working example goes a long way when trying to learn something new. Just as a test, I ran the code in a Win98 computer that has never has Access or VB installed and the code worked. The database was created, etc.

*******
Editor's note:  the sample Dennis speaks of is attached to this newsletter.
---------------------------------------------------------
HEX VIEWER - intermediate level
	by Walt Grams

HexView.bas by Walt Grams.  Used by permission.
mailto:wjgrams@yahoo.com

Date: Sun, 06 Oct 2002  
Subject: [libertybasic] HexView.bas 

Here is a little program I wrote to allow one to view the contents of any file on a byte by byte basis. Each byte in the file is displayed as both a two digit hexadecimal number and the corresponding ASCII character. Try it on several different types of files to see what the various formats look like internally. 

Just for fun try a Microsoft Word file. It is amazing what they keep in your files.

Anyone looking for a challenge can turn this into a full hexadecimal file editor. :-)

Walt


'Displays the contents of any file as the hexadecimal values
'of the individual data bytes in the file and also as the
'corresponding ASCII characters

dim s$(16)
dim h$(16)
NOMAINWIN
WindowWidth = 500
WindowHeight = 380
UpperLeftX = 200
UpperLeftY = 150
statictext #hexView.fname, "File Name: ", 5, 10, 350, 14
button #hexView.cf, "Choose File", [CFClicked], UL, 370, 7, 100, 25
button #hexView.nxt, "Next", [NextClicked], Ul, 370, 45, 0, 0
button #hexView.prv, "Previous", [PrevClicked], UL, 370, 75, 0, 0
statictext #hexView.alabel, "Hexadecimal address:", 370, 125, 150, 15
textbox #hexView.addr, 370, 145, 50, 25
button #hexView.go, "Go", [GoClicked], UL, 430, 145, 0, 0
textbox #hexView.field, 50, 45, 300, 270
statictext #hexView.hexlabel, "Hexadecimal View", 53, 32, 130, 14
statictext #hexView.textlabel, "Text View", 270, 32, 50, 14
statictext #hexView.addrlabel1, "000000:", 5, 50, 45, 14
statictext #hexView.addrlabel2, "000020:", 5, 114, 45, 14
statictext #hexView.addrlabel3, "000040:", 5, 178, 45, 14
statictext #hexView.addrlabel4, "000060:", 5, 242, 45, 14
open "Hex View" for window as #hexView
print #hexView, "trapclose [quit]"
print #hexView.field, "!font Courier_New 10";
wait

[quit]
close #hexView
end

[GoClicked]
print #hexView.addr, "!contents? hexAddr$";
startLoc = hexdec(hexAddr$)
if startLoc < 0 then startLoc = 0
if currentFile$="" then wait
goto [ShowData]

[PrevClicked]
startLoc = startLoc - 128
if startLoc < 0 then startLoc = 0
if currentFile$="" then wait
goto [ShowData]

[NextClicked]
startLoc = startLoc + 128
if currentFile$="" then wait
goto [ShowData]


[CFClicked]
filedialog "Open file to view", "*.*", fileName$

if fileName$="" then wait
startLoc = 0
print #hexView.nxt, "!locate 370, 45, 100, 25"
print #hexView.go, "!locate 430, 145, 40, 25"
currentFile$ = fileName$

[ShowData]
open currentFile$ for input as #f
print #hexView.fname, "File Name: " + currentFile$
if startLoc > 5000 then print #hexView.field, "One moment please ..."
if startLoc > 0 then
  for x = 1 to startLoc
    if eof(#f)=0 then x$ = input$(#f,1)
  next x
  print #hexView.prv, "!locate 370, 75, 100, 25"
else
  print #hexView.prv, "!locate 370, 75, 0, 0"
end if
hexaddr1$ = pad$(dechex$(startLoc))
hexaddr2$ = pad$(dechex$(startLoc + 32))
hexaddr3$ = pad$(dechex$(startLoc + 64))
hexaddr4$ = pad$(dechex$(startLoc + 96))
print #hexView.addrlabel1, hexaddr1$; ":"
print #hexView.addrlabel2, hexaddr2$; ":"
print #hexView.addrlabel3, hexaddr3$; ":"
print #hexView.addrlabel4, hexaddr4$; ":"
disp$ = ""
for index1 = 0 to 15
  s$(index1) = ""
  h$(index1) = ""
  for index2 = 0 to 7
    if eof(#f)=0 then
      val = asc(input$(#f,1))
      if val = 10 or val = 13 or val = 0 or val = 9 then
        s$(index1) = s$(index1) + " "
      else
        s$(index1) = s$(index1) + chr$(val)
      end if
      hexval$ = dechex$(val)
      if len(hexval$)<2 then hexval$ = "0" + hexval$
      h$(index1) = h$(index1) + hexval$ + " "
    else
      s$(index1) = s$(index1) + " "
      h$(index1) = h$(index1) + ".. "
    end if
    if index2=3 then h$(index1) = h$(index1) + " "
  next index2
  disp$ = disp$ + h$(index1) + "  " + s$(index1) + chr$(13) + chr$(10)
next index1
print #hexView.field, disp$
close #f
print #hexView, "refresh"
wait

function pad$(h$)
while len(h$) < 6
  h$ = "0" + h$
wend
pad$ = h$
end function

'end of program (watch for line wraps)

---------------------------------------------------------
LISTING FILES RECURSIVELY - intermediate level
	by Mark Parkinson pm@bodmin-comm-coll.cornwall.sch.uk 

' recurse files.bas for LB3
' Mark Parkinson
' 08/10/02

' This program will list all the files in the whole directory structure
' which match the given file spec. A process called recursion is used.

' Feel free to use and adapt the program eg to sum the filespace used or
' delete files or rename files or move them.
' It is also easy to adapt the program to delete empty subdirectories.


dim info$(10, 10)  'Ready for the files command.
'Note no backslash - added later on.
placetohunt$="c:"
thingtohuntfor$="*.bak"

print "now listing all files in ";placetohunt$;" which match ";thingtohuntfor$
print

call recurse placetohunt$, thingtohuntfor$
print
print "finished"

end

sub recurse pathspec$,mask$
    'Put in the backslash separator.
    pathspec$=pathspec$+"\"
    files pathspec$, mask$, info$(

    filecount=val(info$(0, 0))
    subdircount=val(info$(0, 1))

for i=1 to filecount
    filename$= pathspec$+info$(i, 0)
    filesize$= info$(i, 1)
    datestamp$=info$(i, 2)
    print filename$;"     ";filesize$
next i

'Arrays cannot be local to subs so the subdirs
'are all put in a string separated by *'s which
'can't occur in filenames.
list$=""
for i=1 to subdircount
    list$=list$+pathspec$+info$(f + i, 1)+"*"
next i

'The subdirs are now pulled out of the (local)
'string one at a time and the sub is called again.
'This process where a sub calls itself
'is called recursion.
while list$<>""
    p=instr(list$,"*")
    p$=left$(list$,p-1)
    call recurse p$,mask$ 
    list$=mid$(list$,p+1)
wend

end sub
---------------------------------------------------------
REGISTERING HOT KEYS - intermediate level
	by Ken Lewis kenlewissr@sbcglobal.net

Editor's note:  The introduction to Ken's article is copied here.  Please see the DOC version in the attached zip file for a beautifully formatted article, along with a well-organized demo.  Thanks, Ken!

***********************************
Setting up HotKeys for your program
Liberty BASIC v3.02

This article will discuss setting up hot keys to activate a function in a program that is running.  This article does not cover the type of hotkey used as a keyboard shortcut to start a program, or the type that snaps your window to the top.  Although this could be done with the type of hotkey we will discuss.

I decided to write a program that would take a screen shot of the desktop, the active window or the client area of the active window.  In order to do this without a program window showing, I had to discover how to use HotKeys.  I thought this information might be useful to other users, so I am sharing it here.

If you plan to use the Windows API, I suggest that you download the Win32api.txt file that can be found in the files area of the [libertybasic] group on yahoo.  You will also need the Platform SDK or a similar reference, for explanations of the variables used in the API calls.  I have spent many hours browsing through the Win32api.txt file, looking for functions that might be useful to me at sometime in the future.  This is how I knew the RegisterHotKey function existed, and this is where I will start.

The Win32api.txt file is a list of API calls in Visual BASIC format. 
---------------------------------------------------------
PREVENTING MORE THAN 1 INSTANCE OF YOUR PROGRAM  - advanced level
	by Ken Lewis kenlewissr@sbcglobal.net
      
	Liberty BASIC v3.02

From Hobbiest to Professional, most programmers eventually code a program that needs to prevent a second instance.  Maybe you set up function specific hotkeys, or you add an Icon to the system tray.  Whatever the reason, starting a second instance would, (at the very least), be problematic.  Although there are many ways to approach this problem, the easiest, (that I have found so far), is provided by the Windows operating system. (I know this may be hard to believe, but it is true).  In this article we will take advantage of a Windows object whose purpose is to coordinate MUTually EXclusive access to shared resources.  This object is known as a MUTEX object.  I will not go into details on using this with shared resources.  Instead we will cover an exploitable benefit of creating a MUTEX object.

When creating a MUTEX object, we must supply a string of characters, which I will refer to as the 'name' of the mutex.  If a mutex already exists with the same name, instead of a new one being created, the handle to the existing mutex is returned.  In addition, the Windows defined error code,  ERROR_ALREADY_EXISTS,  is set by the operating system.  If we create a mutex, and immedialely call GetLastError we can notify the user and end the program if it is already running.   

Attached to the newsletter should be a file named 1InstanceOnly.bas.  This is the example program to go with this article.  A few words of caution follow;

1) Each program that you create, must have a unique name for the mutex.  Otherwise you will end up with one program preventing a different program from running.
	
2) The attached code will not operate properly in the editor, my guess is that LB is taking ownership of the mutex.  If you run it in the editor, you will have no problem starting a first instance over and over.  If you try to start a second instance, the program will work as expected.  After attempting to run a second instance, and closing the first instance, you will no longer be able to start a first instance.  BUT if you tokenize the code it works exactly the way it is supposed to, every time.

3) If you are writing a network distributed program and you want to make sure that only one user at a time has access to the program, then the mutex name must begin with "Global\", (e.g. "Global\MyLibertyBASICProgram").  If you want to allow several users to run one instance of the program, at the same time, the mutex name must begin with "Local\", (e.g. "Local\MyLibertyBASICProgram").

4) Mutex 'name' comparison is case sensitive.  So if you have a program that edits information, and a different program that displays that information, (i.e.  15 users can view customer information, but only 2 users are allowed to edit it.) You can prevent the viewers from opening, while the editors are in use. BUT you have to make sure that you match the name and case of the mutex in all programs. 

If you find this code useful, be sure to thank our newsletter editors/publishers, for allowing me to share it.
---------------------------------------------------------
MULTI-COLOURED TEXT INPUT BOXES USING A DLL
	by Ray Fisher 

    rmfisher@talk21.com OR
    rmfisher@btinternet.com

In Newsletter No.100 I described how to create a custom control for text input that could have any colour of text and any colour of background.

In a programme using this method it was necessry to have a number of functions at the end of the main part of the code. This was a bit messy, so I have tried to encapsulate the methods in a DLL.  By doing this most of the code is hidden from sight, and a single call to the DLL function is all that is required for each instance of the control. This is demonstated in '_1stTestdllv01.bas'.

The calling syntax is:

open "rmfDLLv01.dll" for dll as #rmfdll
calldll #rmfdll, "reInputBox", _
  handle    as long, _ ' handle of parent window
  inkHex$   as ptr, _  ' ink colour
  paperHex$ as ptr, _  ' paper colour
  x         as long, _ ' x co-ord
  y         as long, _ ' y co-ord
  w         as long, _ ' width
  h         as long, _ ' height
  max       as long, _ ' max chars
  hTB       as long    ' returns a handle to the new control
close #rmfdll

This will create a single line input box.
The background can be set to any colour.
The font can be set to any colour.
This version uses HEX colour values for input.
The position and size can be set.
The maximum number of chars entered can be set.
The function returns a handle to the new control.

If multiple instances of the control are required, the code can be kept simple by enclosing the call to the DLL function in a LB function, and this can be called from the main part of the programme with a single line of code. See '_2ndTestdllv01.bas'. In this demo I have added extra code so that the controls can be tabbed by using the ESC key.

I have supplied the source code for the DLL for those who may be interested. At the moment the DLL only contains this single external function. Because the colour information is passed as a hexadecimal value, in Liberty BASIC this must be passed as a string. Inside the function the calls to SendMessage() requires that the colour be passed as a RGB value. Therefore it will be seen that there is an internal function 'hex2rgb()' that converts the HEX string into a RGB value.  Because this is all inside the DLL, it is hidden from view in usage.

I hope this is of interest, and any of the code can be copied or altered.

[Editor's note]
Place text into the text input boxes with the API function SetWindowTextA

CallDLL #user32, "SetWindowTextA",_
hTB As long,_    'handle of textbox
txt$ As ptr,_    'desired text
result As void   'no return

Retrieve text that the user has typed into the textbox with GetWindowTextA, like this:

CallDLL #user32, "GetWindowTextLengthA",_ 'determine size of buffer
hTB As long,_    'handle of textbox
tlength As long  'returns length of text

Txt$=Space$(tlength)+Chr$(0)
CallDLL #user32, "GetWindowTextA",_
hTV As long,_    'handle of textbox
Txt$ As ptr,_    'buffer to hold returned text
tlength As long,_'size of buffer
blength As long  'size of returned string

print "Text is ";left$(Txt$,blength)
[End editor's note]


---------------------------------------------------------
IMAGES ON BUTTONS AND STATICTEXT
	by Mike Bradbury (mike@karemi.fsnet.co.uk)


Images and Icons on buttons, Images in statictext controls and justifying text in static controls.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I would agree with Alyce's recommendation that anyone still using LB2 would find much benefit from an upgrade to LB3, unless of course you need to run your programs in Windows 3.x

The demos for this short submission are included with the newsletter in MBradbury.zip and only tested in ****LB3.02**** and Win98SE. 

The zip file also includes some small images and an icon, just unzip everying into a temporary folder and run the .lba files.

Program BMPonButton3.lba puts an image symbol2.bmp on to a standard LB button. The image can be placed to the left, centre or right of the button and is the same size as a small icon (16x16 pixel) so you can construct a collection of images and use them like icons or to add a profile to a button.

Program ICONonButton.lba allows you to choose an icon (.ico) file or to extract an icon from a .exe or .dll file. The icon can be placed to the left, centre or right of the button. An icon file cat.ico is included and is a large icon, 32x32 pixels. After trying this icon, select a dll or exe from another directory. Each click of button allows you to choose a new icon.

Program StaticControlJustify.lba allows text in a statictext control to be justified left, centre or right. Remember that a statictext control autowraps.

Program StaticControlImage.lba replaces text in a statictext control with a bmp image. The control will resize itself to fit the image. As the control will revert to its defined size if the window is resized, it is advisable to make the control the same size as the image. If you need to know the image size, load it into MS Paint where Image menu/Attributes will give you the size in pixels. The bmp eiffel.bmp is included but try your own bitmaps as well. Now you can have graphic images in a window type 'window'

All the controls in these demos are standard Liberty BASIC controls drawn with LB commands and thus respond to LB commands so the only calls to the Windows API are to change the control styles.

MBradbury.zip contents:

BMPonButton3.lba
ICONonButton.lba
StaticControlJustify.lba
StaticControlImage.lba
symbol1.bmp
symbol2.bmp
cat.ico
eiffel.bmp

I hope you find these demos useful.....I can't imagine what programming you are doing if not :>)

Mike Bradbury (mike@karemi.fsnet.co.uk)
---------------------------------------------------------
TWO DEMOS BY DAVID CONNER
	lbdavid26@yahoo.com

Editor's note:  These demos are not commented.  If you have questions, please email the author at the address above.  The first demo is a color previewer that allows the user to specify red, green and blue values.  It then shows the resulting color in a preview box.  Editor suggests that you trap errors if you implement this code in your own programs.  If user doesn't enter values, the program crashes.  Editor suggests that you fill the textboxes with a default value at the start of the program, and then check for valid values before attempting to fill the preview box.  For instance, after opening the window:

print #1.t1, "255"

Then, when the preview button is clicked:

if val(red$)<0 the red$="0"
if val(red$)>255 then red$="255"

The second demo adds a cascading popup menu to an existing menu.  For a full explanation of this method, please see newsletter #82, where such a menu was added to the Open Source Editor, with detailed instructions.  The explanation is for LB2, which is 16-bit.  Please look in the Open Source Editor, which is attached to this newsletter, for an update for LB3, 32-bit syntax.



' Custom Color Previewer
' ccp.bas
' -mailto: lbdavid26@yahoo.com

[init]
WindowWidth = 350 : WindowHeight = 200
UpperLeftX = int(DisplayWidth-WindowWidth)/2
UpperLeftY = int(DisplayHeight-WindowHeight)/2
nomainwin

[open]
graphicbox #1.g, 224,32,100,100
textbox #1.t1, 70,30,50,24
textbox #1.t2, 70,60,50,24
textbox #1.t3, 70,90,50,24
statictext #1.s1, "&Red (0-255)", 20,30,50,25
statictext #1.s2, "&Green (0-255)", 20,60,50,25
statictext #1.s3, "&Blue (0-255)", 20,90,50,25
button #1.b1, "Preview Color", [preview],LR,200,10
open "Custom Color Previewer" for window as #1
#1 "trapclose [quit]"

wait

[quit]
close #1: end

[preview]
#1.t1 "!contents? red$"
#1.t2 "!contents? green$"
#1.t3 "!contents? blue$"
#1.g "fill "; red$; " "; green$; " "; blue$
wait
'''''''''''''''''''''''''''''''''''''''''''''
'popupmenu2.bas

' A Cascading Popup Menu
' David Conner, Open Source

if val(Version$) < 3 then
notice "LB 3 or Higher"
end
end if

NoMainWin
WindowWidth = 300
WindowHeight = 150
UpperLeftX = int(DisplayWidth-WindowWidth)/2
UpperLeftY = int(DisplayHeight-WindowHeight)/2

open "user32" for dll as #user

menu #1, "&File",_
"&Exit", [quit],_
"Popup", [loop]

menu #1, "&Popup",_
"&Item 1", [1],_
"&Item 2", [2]

statictext #1.s, "Click on the File Menu to see", 10,30,250,24
statictext #1.s, "a Cascading Popup Menu", 10,50,250,24

open "Cascading Popup Example" for window_nf as #1

#1 "trapclose [quit]"
h = hwnd(#1)
calldll #user, "GetMenu", h as long, hMenu as long

calldll #user, "GetSubMenu",_
hMenu as long,_
0 as long,_
hFile as long

calldll #user, "GetSubMenu",_
hMenu as long,_
1 as long,_
hPopup as long

mFlags = _MF_BYPOSITION or _MF_POPUP or _MF_STRING

calldll #user, "ModifyMenuA",_
hFile as long,_
1 as long,_
mFlags as long,_
hPopup as long,_
"&Popup Menu" as ptr,_
result as long

calldll #user, "RemoveMenu",_
hMenu as long,_
1 as long,_
_MF_BYPOSITION as long,_
result as long

calldll #user, "DrawMenuBar",_
h as long,_
result as void

wait

[quit]
close #1:close #user:end

[loop]
wait

[1]
notice "You Selected Item 1"
wait

[2]
notice "You Selected Item 2"
wait

---------------------------------------------------------
---------------------------------------------------------
SUBMISSIONS

The Liberty BASIC Newsletter encourages all LB programmers
to submit articles for publication.  Everyone has something
valuable to say, from beginners to veteran LBers.  Consider
sharing a code routine, with explanation.  Perhaps you can
review a favorite LB website, or program, or coding tool?
Why not submit a list of questions that have been nagging
at you?  How about sharing your favorite algorithm?

---------------------------------------------------------
---------------------------------------------------------
    Comments, requests, submissions or corrections: 
Simply reply to this message, or contact a member of the team!

			The Team:
	Alyce Watson: alycewatson@charter.net
	Brad Moore: brad.moore@weyerhaeuser.com
	Tom Nally:  SteelWeaver52@aol.com
	Carl Gundel: carlg@libertybasic.com
	Bill Jennings: bbjen@bigfoot.com
---------------------------------------------------------
---------------------------------------------------------


