---------------------------------------------------------
The Liberty Basic Newsletter - Issue #99 - August 2002
   (c) 2002, http://groups.yahoo.com/group/lbnews/
             All Rights Reserved
---------------------------------------------------------
"Don't be afraid to take a big step when one is indicated.
You can't cross a chasm in two small steps."

	- David Lloyd George
---------------------------------------------------------
In this issue:

SyntaxError: Some words from the editor
Tipcorner:   Window Placement Techniques
Spotlight:   Listview Report by Brent Thorn
Article:     Enhancing Liberty Basic [Array Handling]
Article:     The Road to Release
Demo:        Formatted ListBox
Submissions: Submit articles for publication
---------------------------------------------------------
---------------------------------------------------------
SYNTAXERROR : Some words from the editor

Liberty Basic is always getting better.  If you have not 
heard yet, there is a new 3.02 beta in testing right now.
It not only fixes some bugs, but offers new commands and
functionality. It is exciting to see the language grow, 
and I know that Carl is thinking about the next major
release and what will be included in it.  New stuff all 
the time - isn't it great?  Carl is committed to making 
this the best all purpose BASIC language for hobbyists 
and professional programmers alike.  We are very 
fortunate.

I own other languages, some of which I have paid a 
handsome sum of change for, only to find out that they
have decided not to support their product further. For
a minimum investment Carl offers programmers and would
be programmers the chance to express their creativity
in many ways with a fantastic ever evolving language.  
Our hats are off to you Carl.  Keep the great things 
coming. 

Let me also apologize for the size of this month's 
newsletter.  So much great stuff, and the article I 
wanted to write originally for this month's newsletter
never even made copy.  Oh - you will probably see it
in October.

Thank you - Brad Moore (August 2002 Editor)

---------------------------------------------------------
TIPCORNER - Window Placement Techniques

This month's tip corner is focusing on the positioning of
windows when they are opened.  Many people ask often: "How
can I open a window centered on the screen?", or "How can
I open a window full screen?".

Luckly Liberty Basic has several system variables that are
designed to help with these problems.  These are variables
are our allies in our quest.  Let's get to know them:

UpperLeftX and UpperLeftY both control where the top left
corner of the window will be placed when it is opened.

DisplayWidth and DisplayHeight return the actual display 
size in pixels, this is the total area of your computers
screen.

WindowWidth and WindowHeight you probably already use. 
Set these with the width and height of the window you
want to open.

With these six variables we can explicitly and precisely 
position a window on the computer display.  If we want to
open a 400x260 window that is ten pixels down from the top
of the display and ten pixels from the left of the display,
then we simply set up our variables to do just this:

WindowWidth = 400
WindowHeight = 260
    	UpperLeftX = 10
    	UpperLeftY = 10

	Open "Test" For Window As #main

But what if we want to center the window on the screen?
Well we know the width and height of our window.  We can
get the actual width and height of the display.  In the X
direction (which is width) we would find the UpperLeftX by
subtracting the window width from the display width and
then dividing the result by 2 (that get the center).  If 
we do this also for height (in the Y direction) we will 
know the X and Y coordinates to locate our window at.
In Liberty Basic code it looks like the following:

WindowWidth = 400
WindowHeight = 260
    	UpperLeftX = Int((DisplayWidth-WindowWidth)/2)
    	UpperLeftY = Int((DisplayHeight-WindowHeight)/2)

    	Open "Test" For Window As #main 

So that addresses centering a window, but what about
opening it full screen?  Well the obvious answer it to
set the Window Width = Display Width and the Window 
Height = Display Height.  This works, but it causes the 
window to obstruct the Windows Task Bar.  This can be 
overcome by reducing the Window Height by 20 or so and
then forcing the window into the upper left corner 
like this:

WindowWidth = DisplayWidth
WindowHeight = DisplayHeight - 20
    	UpperLeftX = 0
    	UpperLeftY = 0

    	Open "Test" For Window As #main 

Which is not a bad work around, but there is a much
better method that is much more flexible.  It 
involves calling a Windows API function.  The 
function is ShowWindow.  There are several states
that you can change to.  Below is a list and the 
numeric value of each:

    SW_HIDE = 0
    SW_NORMAL = 1
    SW_SHOWMINIMIZED = 2
    SW_MAXIMIZE = 3
    SW_SHOWNOACTIVATE = 4
    SW_SHOW = 5
    SW_MINIMIZE = 6
    SW_SHOWMINNOACTIVE = 7
    SW_SHOWNA = 8
    SW_RESTORE = 9 

This is a system value, which is passed as the stateflag
in the function.  You also must pass the handle of the 
window you want to change.  Here is the function from the
32bit library:

CallDLL #user32, "ShowWindow", _
                  hWnd As long, _
                  stateflag As long, _
                  result As boolean

Finally here is an example program (it will run in LB3)
demonstrating the API function maximizing a window that
Liberty Basic created.

    WindowWidth = 400
    WindowHeight = 260
    UpperLeftX = Int((DisplayWidth-WindowWidth)/2)
    UpperLeftY = Int((DisplayHeight-WindowHeight)/2)

    Open "Test" For Window As #main

    Print #main, "trapclose [quit]"
    Print #main, "font ms_sans_serif 10"
    hWnd = hWnd(#main)

    stateflag = 3 'SW_MAXIMIZE
    CallDLL #user32, "ShowWindow", _
                     hWnd As long, _
                     stateflag As long, _
                     result As boolean

    Wait

    [quit]
    Close #main
    End

Use the other values mentioned above in the dll call
to get different results, such as a minimized window or
a hidden window.  Have fun with it. 

By Brad Moore, July 2002

---------------------------------------------------------
---------------------------------------------------------
SPOTLIGHT - ListView Report

The spotlight features a quick demo that Brent Thorn
posted to the Liberty Basic list in early July.  It
demonstrates the creation of a listview control with 
multiple columns.  The code is pretty advanced, but it can
be easily utilized in a drag and drop fashion and it
relies on several well encapsulated functions. 

Here is the code.  There may be a line wrap or two, so
I have also included a clean version in the attached 
Project zip file.

Have fun, and thanks Brent.
(Brent can be reached at: lbgui@aol.com)

'ListView Report Example
'by Brent D. Thorn, 7/2002

NoMainWin

Call InitCommonControls

Open "ListView Report Example" For Window As #1
#1 "TrapClose [Quit]"

'set style = LVS_REPORT + LVS_SINGLESEL + 
'            LVS_SHOWSELALWAYS+ LVS_NOSORTHEADER
dwStyle = _WS_CHILD + _WS_VISIBLE + 1 + 4 + 8 + 32768
hLV = CreateListView(_WS_EX_CLIENTEDGE, _
                      dwStyle, 0, 0, 175, 150, hWnd(#1), 0)

Call InsertColumn hLV, 0, "English", 75
Call InsertColumn hLV, 1, "Espaol", 75

Restore [Data]
Read E$, S$: I = 0
While E$ <> "EOD"
    Call InsertItem hLV, I, E$
    Call SetItem hLV, I, 1, S$
    Read E$, S$: I = I + 1
Wend

Wait

[Quit]
Close #1
End

[Data]
Data "One", "Uno", "Two", "Dos", "Three", "Tres" 
Date "Four", "Cuatro", "Five", "Cinco"
Data "Six", "Seis", "Seven", "Siete", "Eight", "Ocho"
Data "Nine", "Nueve", "Ten", "Diez"
Data "EOD", "EOD"

Sub InitCommonControls
    struct LVCOLUMN, _
        mask As ulong, _
        fmt As long, _
        cx As long, _
        pszText As ptr, _
        cchTextMax As long, _
        iSubItem As long, _
        iImage As long, _
        iOrder As long
    struct LVITEM, _
        mask As ulong, _
        iItem As long, _
        iSubItem As long, _
        state As ulong, _
        stateMask As ulong, _
        pszText As ptr, _
        cchTextMax As long, _
        iImage As long, _
        lParam As long, _
        iIndent As long
End Sub

'The next line wraps and will need to be put back together
Function CreateListView(dwExStyle, dwStyle, x, y, nWidth, nHeight,hWndParent, nID)

    CallDLL #user32, "GetWindowLongA", _
        hWndParent As long, _
        _GWL_HINSTANCE As long, _
        hInst As long

    CallDLL #user32, "CreateWindowExA", _
        dwExStyle As long, _
        "SysListView32" As ptr, _
        0 As long, _
        dwStyle As long, _
        x As long, _
        y As long, _
        nWidth As long, _
        nHeight As long, _
        hWndParent As long, _
        nID As long, _
        hInst As long, _
        0 As long, _
        CreateListView As long
End Function

Sub InsertColumn hLV, iCol, Text$, cx
    LVCOLUMN.mask.struct = 2 + 4 'LVCF_WIDTH + LVCF_TEXT
    LVCOLUMN.cx.struct = cx
    LVCOLUMN.pszText.struct = Text$
    CallDLL #user32, "SendMessageA", _
        hLV As long, _
        4123 As long, _ 'LVM_INSERTCOLUMN
        iCol As long, _
        LVCOLUMN As struct, _
        r As long
End Sub

Sub InsertItem hLV, iItem, Text$
    LVITEM.mask.struct = 1 'LVIF_TEXT
    LVITEM.iItem.struct = iItem
    LVITEM.iSubItem.struct = 0
    LVITEM.pszText.struct = Text$
    CallDLL #user32, "SendMessageA", _
        hLV As long, _
        4103 As long, _ 'LVM_INSERTITEM
        0 As long, _
        LVITEM As struct, _
        r As long
End Sub

Sub SetItem hLV, iItem, iSubItem, Text$
    LVITEM.mask.struct = 1 'LVIF_TEXT
    LVITEM.iItem.struct = iItem
    LVITEM.iSubItem.struct = iSubItem
    LVITEM.pszText.struct = Text$
    CallDLL #user32, "SendMessageA", _
        hLV As long, _
        4102 As long, _ 'LVM_SETITEM
        0 As long, _
        LVITEM As struct, _
        r As long
End Sub


---------------------------------------------------------
---------------------------------------------------------
ARTICLE - Enhancing Liberty Basic [Array Handling]
         (AKA: The memory Article)
         by Dennis McKinney (dlm81854@iquest.net)


Liberty Basic is very easy to learn and use. Unfortunately 
this fact tends to make some users feel that it's too simple, 
and that a lot of things simply "can't be done" because the 
language doesn't support this or that. This isn't necessarily 
the case. For example, arrays of more that two dimensions, 
mixed type arrays with both string and numerical elements in 
them, and arrays of structures are not supported. Several 
times in the past couple of years I have had need for these 
kind of arrays, so finally I decided to try to find a solution. 
As it turns out, the solution is pretty simple.

Liberty Basic has a very powerful capability built right in, 
the ability to utilize the Windows API. This ability proved 
to be the answer and allows all three of these array types 
to be created with very little effort. It only takes four API 
calls and a little unusual usage of an array and a structure. 
Think about what a variable, array, or structure really is. 
Each one is a certain number of bytes in memory that are 
reserved, or allocated, to contain values. These bytes of 
memory are the same regardless of which language the 
programmer is using to fill in the values. The Windows API 
provides the means to allocate memory and Liberty Basic has 
the means to utilize this memory.

Three of the four APIs we will be calling are wrapped in 
Liberty Basic functions. These will be called:
'Function GlobalAlloc( dwBytes )
'Function GlobalLock( hMem )
'Function GlobalFree( hMem )
Each one will be explained as it appears in this example.


Let's begin with an array of structures. As it turns out, 
all three types of arrays can be created as an array of 
structs. For this example we'll use an array 100 structures, 
and each structure will have 3 elements, 2 strings and one 
number. This array will not actually contain structures. 
The structures are going to be stored in memory. The purpose 
of the array is to store the address of each structure that 
we store in memory.

    'dimension the array for 100 addresses
    elements = 99
    dim structArray(elements)

    'define one structure
    struct test,_
    a as char[20],_
    b as long,_
    c as char[20]

Notice that the string elements are typed as char[x], where 
x is the length of the string. Using a$ as ptr will not work.

When you are going to allocate memory the first thing you 
need to determine is how much memory you need. This is done 
by multiplying the number of elements in the array by the 
size of the structure.

    'the size of the structure is     
    sizeofTest = len(test.struct)

    'the amount of memory needed is
    memBlockSize = (elements + 1)*sizeofTest

The memory API's are accessed thought kernel32.dll so,

    open "kernel32.dll" for dll as #kernel

Now we can allocate the memory needed

    hSArray = GlobalAlloc( memBlockSize )

This function allocates the memory and returns a handle to the memory object. After this call hSArray will contain the handle for 
the memory. Code for checking for a valid handle (not null) 
isn't included here. The function takes one argument, the 
amount of memory being requested.

Ok, so we've created some memory, but where is it? This next 
function will return a pointer to the first byte of the 
memory block, which is the address of the start of the memory 
block. The function takes the handle of the memory object as 
its argument.

    ptrSarray = GlobalLock( hSArray )

Let's review what has been done so far. 
1. Dimension an array for pointers to memory addresses.
2. Defined the struct for our data.
3. Allocated the needed memory.
4. Determined where the memory is located within the heap.

The memory and structure array are ready to be used, so for 
example purposes we'll fill all of the structures with data 
and store them in memory. As each struct is filled it will be 
placed in the memory one after the other. To place a struct 
into memory we'll call the RtlMoveMemory API. This API needs 
to know three things:
1. Dest, where to put the data into memory.
2. Src, address of the memory block to copy, in this case the 
   address of the test structure.
3. dwLen, the size, in bytes, of the block to copy, in this 
   case the size of the test structure.
The destination is determined as an offset from the first byte 
of the allocated memory. If each structure we were copying were 
10 bytes long, the first struct would be stored starting at 
ptrSarray, which is the first byte of the memory, the second 
struct would be stored starting at 11 bytes into the memory 
block, etc. Liberty Basic takes care of the second requirement 
(Src) by passing a pointer to our stuct. We have already 
determined the size of our structure (sizeofTest).

'for example purposes, fill the whole array of structures
    for i = 0 to 99
        'put some data into the struct
        test.a.struct = "Carol - " + str$( i)
        test.b.struct = i
        test.c.struct = "Andy - " + str$( i)
        'calculate the destination as an offset from the 
        'first byte
        dest = ptrSarray+(i*sizeofTest)
        'put the structure into memory
        CallDll #kernel,"RtlMoveMemory", dest as long, _
                   test as ptr, sizeofTest as long, ret as void
        'store the address so the data can be found when needed
        structArray(i) = dest
    next i

Now that we've put 100 structures into memory, how do we get 
the data back from memory? Again, Liberty Basic provides the 
means. Simply point our structure to the address of the memory 
we want. Remember that the addresses were stored in the 
structArray() so we just do this:  test.struct = structArray(i), 
where i is the element we want. 

'for example, read all of the structures
    for i = 0 to 99
        test.struct = structArray(i)
        A$ = test.a.struct
        B = test.b.struct
        C$ = test.c.struct
        print  A$ + "   " + str$(B) + "   " + C$
    next i

'To retrieve the third element from the 50th structure:
    test.struct = structArray(49)
    C$ = test.c.struct
    print C$

'To change the value of the third element of the 50th 
'structure:
    test.struct = structArray(49) 'get the structure
    test.c.struct = "Changed" 'change one or more values
    'save it
    dest = ptrSarray+(49*sizeofTest)
    CallDll #kernel,"RtlMoveMemory", dest as long, _
             test as ptr, sizeofTest as long, ret as void

    'just for example
    test.struct = structArray(49)
    C$ = test.c.struct
    print C$

The final and very important thing to do when your program 
ends is to free every memory that we allocated from the 
heap. This erases all the structs that we stored and frees 
the memory for use.

[quit]
    ret = GlobalFree(hSArray) 'call this for every memory block allocated. Use the appropriate memory handle.
    close #kernel
    end

'**** Functions ****
Function GlobalAlloc( dwBytes )
'returns the handle of the newly allocated memory object.
'the return value is NULL if fail.
    CallDll #kernel, "GlobalAlloc",  _GMEM_MOVEABLE as long,_
                      dwBytes as ulong, GlobalAlloc as long
End Function

Function GlobalLock( hMem )
'returns a pointer to the first byte of the memory block.
'the return value is NULL if fail.
    CallDll #kernel, "GlobalLock", hMem as long, _
                      GlobalLock as long
End Function

Function GlobalFree( hMem )
    CallDll #kernel, "GlobalFree", hMem as long, _
                      GlobalFree as long
End Function


The complete example is contained in structure_arrays.zip 
[Ed. Which is attached to the newsletter as a part of the 
newsletter zip file.  Unzip the newsletter zip file to 
access Dennis's demonstration program.]

In closing I'll leave you with this.
Gee, I wonder. Could this method be used with API calls 
that require arrays of structures?

---------------------------------------------------------
---------------------------------------------------------
ARTICLE - The Road to Release
          By Brad Moore (bjaz.moore@verizon.net)

There is no doubt that LB is powerful, even powerful 
enough to produce commercial quality applications.  If 
you need proof, just mosey on out to http://members.tripod.com/~eaglesoar/eagle.html and 
take a look at birthday keeper published by EagleSoar 
Software.  It has earned numerous awards and is a top 
rated shareware download.  Not bad for an application 
developed in Liberty Basic.

Truth is, any one can do it.  Given enough practice and 
patience, production and release of a polished, 
professional software product is well within the scope of 
a Liberty Basic programmer.  But there are several steps 
between "finishing" that great game and having a commercially 
acceptable software product.  The aim of this article is 
to take the average Liberty Basic developer from the "I 
think I am done" stage to the ready for distribution stage. 
So hop aboard for a really swell ride on the "Road to Release"
The Journey Begins

Our first stop is at the application itself.  There are 
several really important requirements that you are going 
to want to meet before you continue on the journey.

1) Is it ready?  It seems like a no brainer, but has it 
   been well tested?  Are there any dead ends in your menus 
   or buttons, functionality you meant to develop but forgot.  
   Does it do what it is suppose to do?  I this area I try 
   to do a few things to insure completeness.
   a) Bench-check the code.  Look through it top to bottom 
      and make sure you have not left out something you 
      meant to code.  Check for logic errors and insure you 
      open and close all your files correctly.  Are there 
      any hard coded paths to files? Is there an END 
      statement?  It is required.
   b) Test the code.  Take your program through every single 
      execution path that is possible.  Try to break it by 
      doing the unexpected.  This phase is often called alpha 
      testing in larger projects.
   c) Now have some other people try it out to see if they 
      can break it.  


2) Next there are several functional things a good 
   application should have to meet the minimum requirements 
   of release ready.
   a) An about dialog box.  This little form that tells 
      about the application, who wrote it and when has 
      become a standard in windows programming.  Don't 
      consider going to market without it.
   b) A help file.  The next level up from an about box this 
      is an important component of any good software product.  
      If your program is of any size or complexity at all 
      consider creating a helpfile.  An excellent help 
      authoring tool (called EclipseHelp) is available from 
      Green Eclipse software at 
      http://www.greeneclipsesoftware.com/eh.html . 

Our Vehicle

For the purpose of this article we will be expanding the 
little applet that appeared in Newsletter 97 called the 
Liberty Basic Newsletter Reader.  I have updated it with a 
couple new twists since its publishing in May.  There is 
code to remove the menu item EDIT that the texteditor 
automatically puts in the menu when a texteditor is place 
on a window type form.   I have also changed the window 
to be of the type without a frame so that it can not be 
resized. 

In order to follow this article along I recommend getting 
the companion file that accompanies the newsletter which 
contains the LBNR.ZIP file.  Inside the zip file you will 
find the original project as released back in late May, 
and the new updated file called LBNR2.bas.  Also, there 
is an additional zip file called LBNR-Release, which 
contains other items we will be using during this tutorial 
article.  Unzip these in a convenient working location and 
we will get moving along.

In addition to the changes I mentioned above, I have also 
added a simple ABOUT window.  It is a dialog box that reuses 
the main application graphic and displays some basic 
information about the program.  The following code snippet 
does most of the work (There are line wraps that must be 
fixed - sorry):

[About]
'Window setup
    
    WindowWidth = 317 : WindowHeight = 387
    Graphicbox  #about.gbx, 10, 10, 115, 340
    Groupbox    #about.gp1, "", 140, 80, 155, 10
    Groupbox    #about.gp2, "", 140, 280, 155, 10
    Statictext  #about.stx1, "Liberty Basic Newsletter Reader", 145, 15, 145, 65
    Statictext  #about.stx2, "about", 145, 100, 145, 175
    Button      #about.ok, "Close",[about.close],UL, 190, 315, 100, 30

    Open "About LBNR" For Dialog As #about

    #about "trapclose [about.close]"
    #about "font ms_sans_serif 10"
    #about.stx1 "!font arial 12 bold"
    #about.stx2 "!font arial 10"

    'Create a info string for the body of the about text
    info$ = "Quick demo showing operation of loading a file directly into"  + Chr$(13) + _
    "a textedit window.  It is quick and simple.  Read the Tipcorner"  + Chr$(13) + _
    "of LB Newsletter 97 (June 2002) for more information. "  + Chr$(13) + _
    "By Brad Moore : June 2002 " + Chr$(13)  + Chr$(13) + _
    "This code requires LB 3.x or greater to run"

    'put the info string into the static text field
    #about.stx2 info$

    'set a flag so we know the about window is open
    aboutWin = 1

    'add the graphic last
    #about.gbx "drawbmp LBNR 0 0"
    #about.gbx "flush"


    'wait for the user to close the window
    Wait

[about.close]
    Close #about
    'reset a flag so we know the about window is now closed
    aboutWin = 0
    Return

A point or two to bear in mind is that this is a snippet from 
a larger program and will not run as is on its own.  Also, 
the graphic that is displayed in the graphic box is loaded 
by the main application and just reused in the code above.

Managing Multiple Windows

One of the things to watch for, when a program has the 
potential of having multiple windows open at any one time, 
is how the windows are handled when the program ends.  If 
the main window is closed and the END statement is executed, 
and other windows are left open, it will leave the program 
in an error state.  For that reason I add a status flag to 
my code to track the status of all other windows that could 
be opened during program execution.  This is good practice 
if you are seeking a professional appearing application.  
Here is my flag being initialize in the main program body:

'Set a flag for the about window - initialize to zero
aboutWin = 0

And later in the main program body the flag is checked prior 
to closing the main window to insure that the ABOUT window 
is not open.

[quit]
    'Don't leave the about window open
    If aboutWin <> 0 Then
       Close #about
    End If
    
    'Now close the main window
    Close #main
    End

Finally, the flag is managed in the snippet that displays 
the ABOUT window.  Scanning that code above you will notice 
that it is set to the value '1' as soon as the window is 
opened, and is again reset to a zero when that window is 
closed.

Getting Back On-track

In addition to the ABOUT dialog window that was added to the 
LBNR, I have also written a quick and dirty help file which 
has been integrated into the code.  Opening a help file is 
very easy in Liberty Basic, just specify the help engine name 
and the help file name as the argument to the RUN command.  
Here is the little bit of code used to open our help file 
called LBNR.hlp:

[help]
    Run "winhlp32 lbnr.hlp"
    Wait

I created the help file with the help file editor mentioned 
earlier in the article.  You could just as easily have 
created it with a word processor or one of many other help 
file editor tools.  In fact for a fairly simple program you 
could have even made your own window and then loaded a 
simple help text file.  In a later article (due later in 
the fall of 2002) I will discuss help file creation and 
tools to make the job easier.

As mentioned earlier, the finished code is LBNR2.bas and its 
supporting files are in the ZIP file called LBNR2.ZIP.  
These are the files that we will use in the next sections.

Our Second Stop

Our second stop on our journey to release covers the steps 
needed to prepare and package the required files to run our 
application away from Liberty Basic.  There are several 
fundamental steps that we must follow to get an executable 
application that does not require the Liberty Basic 
environment to run.  The Liberty Basic Help file discusses 
much of this, as we will also.  After a brief over view of 
the procedures that lie ahead, we will begin the process 
using the LBNR2 as our subject.

Preparing our project for release and distribution requires 
us to:

1) Create a tokenized runtime file (often called a TKN 
   file) from our source code.
2) Gather all of our supporting files for distribution 
   (graphic files, text files, help files, etc)
3) Make copies of the runtime libraries and runtime 
   engine, renaming it if necessary. 
4) Optionally changing the runtime engine icon.
5) Finally testing the distribution package.

Let's go through each of these steps, elaborating on the 
process.  Additional information on each step can be 
obtained from the Liberty Basic help file.

Creating a TKN File

In order to run a Liberty Basic file outside of the 
development environment it must be first tokenized.  
Tokenizing a BAS file performs the initial compile steps, 
ordering the source code and writing it to an intermediate 
p-code that the Liberty Basic virtual machine can execute.  
A tokenized file is stored on your machine with the file 
extension TKN.   The file is often called a TKN file 
instead of a tokenized file for this reason.

Using our example file called LBNR2.bas, tokenize it 
following these simple steps:

1) Open the file in the Liberty Basic 3 editor.
2) Run the file to insure it does not have any syntax errors 
   that will prevent tokenizing.
3) Under the RUN menu of the editor, select the Make *.TKN 
   file menu option.
4) After the file has compiled into a tokenized format you 
   will be prompted to save the file.  Save it as LBNR2.TKN.

That is all there is to it.  You can run your tokenized file 
if you would like - by selecting Run *.TKN file from the Run 
menu of the Liberty Basic 3 Editor.  One final not about 
creating TKN files. You must be using a registered version 
of Liberty Basic in order to create tokenized files for 
distribution.  

File Collection I

The next critical step in preparing for release is to get 
all your additional files together.  In most cases a project 
will have a graphic file or two (maybe many more), a help 
file and maybe even a text file or two.  In our case with 
LBNR2 we have all three types of files to add to our 
distribution.   I find it is helpful to create a sub-folder 
in my project folder to begin gathering my final release 
files into.  I usually just call this folder RELEASE.  In 
the project we are working on the folder structure might 
look something like:

	Upper-level-folders...
		|
		+--- LBNR   (You project development files 
                  |      are in this folder)
			|
			+---Release   (Collect release files 
                                 only here - copies of 
                                 original files)

In the case of our project, copy (be sure to copy and not 
use the original files in your release folder) the following 
files into the release folder:

* LBNR-Demo.txt
* LBNR.hlp
* LBNR.gid
* LBNR.bmp
* LBNR2.tkn

The first four files are files I created for this exercise.  
They are part of the ZIP file mentioned elsewhere in this 
tutorial article.  If you unpacked the zip file correctly, 
they should all be in one working directory, and I hope you 
called that directory LBNR.  The fifth file is the TKN file 
you created in the previous steps.  I did not include the 
TKN file, you must create it yourself.

File Collection II

The next step in preparing for distribution is the bringing 
together of the runtime libraries and the runtime engine.  
These are files that are shipped with the Liberty Basic 
product.  The help file specifies the files that are 
required.  I have not included these in the accompanying 
ZIP file because you should already have them on your 
machine of you are using Liberty Basic 3.x, and also 
because they are fairly large.

You will find all the files you need in the root of the 
directory where you installed Liberty Basic.  It is 
important that you have both enabled the viewing of system 
files and the display of file extensions in the Explorer 
options of Windows.  If you do not do this, you will not 
see all the files you require to create your distribution.  
In Windows 98 these options can be accessed by selection 
FOLDER OPTIONS from the VIEW menu of the Windows Explorer.  
Then clicking the VIEW tab in the dialog box that is 
presented.  On my system I have enabled the "View All 
Files" option and have unchecked the "Hide file extensions 
for known types" option.

Presuming your computer is configured correctly, you should 
be able to locate the following files from the root of your 
Liberty Basic install directory:

* vbas31w.sll
* vgui31w.sll
* voflr31w.sll
* vthk31w.dll
* vtk1631w.dll
* vtk3231w.dll
* vvm31w.dll
* vvmt31w.dll

These are the runtime libraries.  As you can see there are 
eight of them.  Copy all eight to your RELEASE folder under 
your LBNR folder.

Likewise, copy the file RUN.EXE (the runtime engine) from 
the root of your Liberty Basic install directory to your 
RELEASE folder under your LBNR folder.  Your application is 
almost ready to be run free of the Liberty Basic development 
environment.  One step remains - renaming the runtime engine.

Several releases ago Liberty Basic was changed to simplify 
the way a user launches an application created in Liberty 
Basic.  Previously a second runtime was required which had 
the developer's registration key and a simple line to execute 
the real TKN file.  This intermediate file was always called 
Startup.TKN.  Today the requirement for this intermediate 
file is replaced with the simple step of renaming the runtime 
engine to the same name as the TKN that you want to execute 
when the runtime engine is invoked.  In the case of our 
application, which is called LBNR2.TKN, we would need to 
rename the RUN.EXE to LBNR2.EXE.  Don't change the EXE file 
extension.  This is critical.  

Rename your copy in your RELEASE folder now.  Do not rename 
the RUN.EXE in the Liberty Basic folded, it is the wrong 
one.  Once you are done, you can launch the LBNR2 
application by simply double clicking the LBNR2.EXE file.  
Give it a try.

Kick it up a Notch

I like Emeril (the chef on satellite TV) - if you want a 
little more kick, toss a little more spice in.  This step 
is purely optional, but if you are looking for a polished 
look and feel to you application I recommend it.  This is 
that little extra "BAM".    

Most programs have a unique icon, and yours should too.  
It is easy and fun to change the runtime icon of your 
application.  First you will need a 32x32 icon.  I created 
my own for the LBNR2 project, and you unpacked it with the 
rest of the files.  It is called LBNR2.ico.  In most cases, 
in the real world, you are not going to find the icon on a 
platter, but you are going to have to create you own.  

Creating icons is very easy, and there are many tools around 
to make it easier.  I created my icon using Alyce Watson's 
icon editor from her Liberty Basic 2 files area (the URL for 
her site is: http://iquizme.0catch.com/lb).  It is free and 
runs under LB2 (it will not run in LB3.x - sorry).  Other 
alternatives are the icon editor that is part of Liberty 
Basic 3.x, or the icon workshop that is part of Liberty 
Basic Workshop (also available on Alyce's web site).  Or 
search the web - there are many third party tools that are 
free.  Some tips to remember: the icon must be 16 colors 
and measure 32x32 pixels.

For the purpose of discussion, let us simply use the icon I 
have provided.  Changing the icon on the runtime engine is 
a very simple mater of following a few simple steps:

1) Open Liberty Basic 3.x
2) From the SETUP menu select ICON EDITOR (it is its own 
   little applet)
3) From the FILE menu of the Icon Editor select OPEN ICON
4) Select the icon file named LBNR2.ico using the file open 
   dialog window
5) You can edit the icon if you would like at this point - 
   don't forget to save the result.
6) Finally apply the icon to the runtime engine selecting 
   SAVE TO RUNTIME EXE from the file menu - you will need 
   to navigate to your RELEASE folder and save it to the 
   file LBNR2.EXE (remember we renamed the runtime engine 
   earlier)

In addition to a customized runtime you should also consider 
whether you wish to distribute an End User License Agreement 
(EULA) with you application.  Most serious software 
developers provide this document, which the user must agree 
to in order to begin using (or often as a result of using) 
the software product.  It is a smart decision to have an 
EULA because it protects the software developer and the end 
user alike.  

Stock EULA's are hard to come by.  Many people simply 
pattern their EULA after those found on other popular 
software offerings.  I located a couple sites that sell 
downloadable templates that can be used to develop an EULA.  
Check them out at (watch for line wraps):

* http://shop.store.yahoo.com/startupbiz/enduslicageu.html
*  http://www.lawvantage.com/description/technology_agreements
/software_license_agreements/summaries/SWLA1004S.shtml
 
The second link is a site called LawVantage, and they offer 
the following as a list of items that a comprehensive EULA 
would cover:

* Grant Of License; 
* Usage Limitations; 
   * Unauthorized Use; 
   * Copy; 
   * Redistribute or Resell; 
   * Network, internet, intranet; 
   * Reverse Engineering, Decompilation and Disassembly; 
   * Separation of Components; 
   * Export; 
   * Use by Others; 
* Intellectual Property Rights; 
* Disclaimer Or Warranties; 
* Limitation Of Liability; 
* Term And Termination; 
* Indemnification; 
* Assignment; 
* Survival; 
* Miscellaneous; 

As an alternative, the website for Shareware developers 
called UpLoad.it (http://upload.it/upload_000002.htm) 
offers the following template that can be copied and then 
modified to meet your own personal needs (the URL to see 
this online is: http://upload.it/upload_000039.htm) :

LICENSE 
-------------- 
ACME Company (ACME) hereby gives you a non-exclusive license 
to use the software YourSoftware (the Software). 
For evaluation, the license is granted, and is time-limited. 
For registered release you have to pay a license fee, by 
following instructions prompted by the program. 

You may: 
- use the Software on any single computer; 
- use the Software on a second computer so long as the 
  primary user of each copy is the same person and more than 
  one copy is not simultaneously; 
- copy the Software for archival purposes, provided any copy 
  contains all of the original Software's proprietary notices. 

You may not: 
- permit other individuals to use the Software except under 
  the terms listed above; 
- modify, translate, reverse engineer, decompile, disassemble 
  (except to the extent applicable laws specifically prohibit 
  such restriction), 
- create derivative works based on the Software; 
- copy the Software (except as specified above); 
- rent, lease, transfer or otherwise transfer rights to the 
  Software; 
- remove any proprietary notices or labels on the Software. 

TERMINATION. 
The license will terminate automatically if you fail to 
comply with the limitations described above. On termination, 
you must destroy all copies of the Software and 
Documentation. 

DISCLAIMER OF WARRANTY 
--------------------------------------------- 
The Software is provided on an AS IS basis, without warranty 
of any kind, including without limitation the warranties of merchantability, fitness for a particular purpose and 
non-infringement. 
The entire risk as to the quality and performance of the 
Software is borne by you. 
Should the Software prove defective, you and not ACME assume 
the entire cost of any service and repair. 

ACME IS NOT RESPONSIBLE FOR ANY INDIRECT, SPECIAL, 
INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER 
INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, 
WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND 
ALL OTHER COMMERCIAL DAMAGES OR LOSSES. 

Title, ownership rights and intellectual property rights 
in and to the Software shall remain in ACME. The Software 
is protected by international copyright treaties. 

----------------------END------------------

Obviously the developer who will be using this EULA will 
need to adapt it to their own needs.  This version is 
specific to shareware - if you are developing freeware you 
will want to alter the terms as they relate to software 
evaluation.  Another point to consider is that it is not 
uncommon to see the EULA as a part of the help file as 
well as plain test file.  

Don't forget that once you create this file, it should 
also be included in the RELEASE folder so that you will 
remember to distribute it along with your application.  
In the case of our project, copy the file EULA.txt from 
the working directory to the RELEASE directory.


Testing (Again)

Nothing is more demoralizing that thinking it is all done 
and ready and releasing your work (with out testing it well) 
and finding out you missed something.  Usually it is something 
simple, just adding to the humanization.  Test and test well.  
You are pretty much done with preparing for distribution, now 
insure it all works.  

After you have tested the program thoroughly, have a friend 
or family member try their hand at it.  Something you 
overlooked usually becomes apparent.  Fix the problems and 
try again.  Now is the time to get it right.   

Arrival at our Destination

The final step in preparing your application for release is 
packaging.  There are many packaging tools available, and 
some are even free.  My favorite free packaging tool is Inno 
Setup Compiler.  It can be downloaded from the Inno website 
at: http://www.jrsoftware.org/isinfo.php .  Another good 
installer, which I have used before is HJInstall from 
freebyte software.  The program is free and can be 
downloaded from the freebyte website at: http://www.freebyte.com/hjinstall/

For the purpose of this article, I am going to presume that 
you have gone to jrsoftware's website and downloaded Inno 
Setup Compiler and installed it on your system.  If you 
have been following along and doing the exercise as we go, 
you are ready to create an install script.  

Inno Setup Compiler has a very strong packaging wizard, 
which can easily create a comprehensive and professional 
looking install in only a matter of minutes.  Start the 
wizard by selecting the option to create a new script file 
using the Setup Wizard when you first launch Inno.  The 
wizard will walk you through a couple pages that collect 
basic information about your application.  Our application 
is called LBNR and it is version 2.  The defaults are 
generally fine on the Application Directory page.

The heart of the Setup wizard is the Applications Files 
page.  Navigate to the executable for our application and 
set it as the application main executable.  This is the 
renamed runtime engine that should now have a custom icon 
and be called LBNR2.EXE - it is in the RELEASE folder.  
Next, click the Add Files button and browse to the RELEASE 
folder again and select ALL the files in that folder except 
the executable LBNR2.EXE (it is included in the field above).  
You can select all the files at once by clicking the first 
file in the file dialog window, then clicking the last file 
and holding the shift key down at the same time.  To 
unselect the one file we do not want simply hold the control 
key down (it is labeled Ctrl) and click the LBNR2.EXE file.  
If you made a mistake, there are additional buttons to add 
and remove files.

The next page can be setup any way you perfer.  It is nice 
to leave the options enabled that allow the user to change 
the various settings to meet their own requirements.  In the 
following page called Application Documentation, you can link 
to text files that you wish to display to the user.  If your 
program included release notes, you can display them in the 
installer and the user could read them when installing.  
This is also a great place to link the EULA that was created 
earlier.  In the case of our project, click the browse 
button for the license file and browse to the RELEASE folder 
and link to the EULA.txt file.  

Click next and finish to complete the creation of your 
install script.  Inno will give you the option of compiling 
the script.  Unless you plan to make manual changes to the 
install script, go ahead and compile now.  You should now 
have a single executable installer that can be run on any 
Windows 95 or better system.  If you have a second system 
somewhere, or have a friend who can help, run the setup 
program on the other machine.  It should install a fully 
functional, stand-alone application.  Remember - test you 
installation.  If you have access to several different 
Operating Systems (Windows98, Windows ME, Windows XP, etc) 
it would be great to try you program out on all of them.  
I know this is rare, but getting friends together to test 
on their machines is a great alternative.

I have included my Inno Setup script in the project 
archive that is coming with this newsletter.  If you have 
installed Inno, you can look at it, although my directory 
structure will be different from yours, so you will probably 
not be able to use the script as is.  Finally, the whole 
executable install is in the files area of the newsletter 
group in Yahoo.  It was fairly large due to the size of the 
runtime files, and I did not want to send a file of that 
size along with the newsletter.  If you followed along, you 
should already have a fully functional installation.

Thanks.  Hope you enjoyed the ride.  We have come to the 
end of the Journey.  We have a professional product that is 
ready for market.  Now my challenge to you:  Finish your 
project and prepare it for market.  Have fun!

Brad Moore
Bjaz.moore@verizon.net

---------------------------------------------------------
---------------------------------------------------------
DEMO - Formatted Listboxes

Mike wrote on July 11th:    

Here is another submission for the next newsletter.  Maybe 
it would be of interest to those who say that LB is not 
suitable for business applications.

The code shows the methods I use to format listboxes and 
manipulate data from a listbox.  No doubt this will be 
replaced soon by the excellent work being done with 
Windows ListView API, but users who shy away from dll 
calls may like to use it.

[Ed. Well Mike, I don't think it will be replaced by the
API versions, as you mention, many users like doing these
things using native LB commands.  It is interesting to 
note that this newsletter contains two methods of doing
nearly the same thing.  See the Spotlight for an API 
based version of code to format the listbox.  Here is 
Mike's code he submitted as a demo.  There may be many
line wraps, for an executable version, check the attached
zip file for the basic program Formatted_Listbox.bas]


    'For LibertyBASIC tested with version 3.01, Win98, 800x600
    'Formatted listbox with column labels which become field
    'selection buttons for editing. To enable editing, click
    'the Edit button, select an item from the listbox and then
    'click the button for the field you wish to change. When
    'finished editing, click the Lock button and the listbox
    'resumes its' normal behaviour.
    'Obviously data would usually be saved to disc file rather
    'than data statements and editing would require changes to
    'be written back to the file.
    'mike@karemi.fsnet.co.uk

    '~~~~~~~~~~~~~~~~~~~~~~~ Window size ~~~~~~~~~~~~~~~~~~~~~~~~~
    ' following dimensions are pixels
    dw=DisplayWidth: dh=DisplayHeight
    WindowWidth = dw: WindowHeight = dh
    '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
disable=0:enable=1
BackgroundColor$="darkcyan"
NoMainWin
Dim buy$(20)
Dim array$(20)  'array for listbox
'arrays for individual columns in listbox
Dim serial$(20):Dim item$(20):Dim cost$(20)
'fill arrays. Building array$() from separate elements enables
'easy manipulation of the listbox contents.
For n=1 to 20 'ignore element zero, see LB help file re-listbox
ser$=Str$(n)
serial$(n)=formatLeadingZeros$(5, ser$)      'fix length of serial$(),padded with zeros
Read a$:item$(n)=a$ 'load demo data
item$(n)=formatTrailingSpaces$(14, a$)       'fix length of item$(), paddedwith spaces
Read cost 'load demo data
cost$(n)=formatCost$(cost)                   'format cost, aligning decimals
array$(n)=makeListboxEntry$(n)               'build array for listbox
Next n
xRef=200
Menu #m1, "&About program", "&Info", [info]
Button #m1.L0, "", [], UL, xRef,140,49,20
Button #m1.L1, "Item", [editItem], UL, xRef+49,140,121,20
Button #m1.L2, "Cost", [editCost], UL, xRef+170,140,86,20
Button #m1.L3, "Spare", [spare], UL, xRef+256,140,86,20
Button #m1.b0, "BUY", [shop], UL, xRef,440,70,20
Button #m1.b1, "Exit", [quit], UL, 0,0,0,0
Button #m1.b3, "Edit", [edit], UL, xRef,110,70,20
Listbox #m1.lb1, array$(,[show],xRef, 160, 341, 135
Texteditor #m1.tb1, xRef,300,255,130
Open "Listview" For window_popup As #m1 'no titlebar/close box
'array of handles for buttons to be used as labels
hCont(0)=hWnd(#m1.L0)
hCont(1)=hWnd(#m1.L1)
hCont(2)=hWnd(#m1.L2)
hCont(3)=hWnd(#m1.L3)
'font for button labels (use bold font for clarity)
#m1.L0, "!font arial 9 bold"
#m1.L1, "!font arial 9 bold"
#m1.L2, "!font arial 9 bold"
#m1.L3, "!font arial 9 bold"
'disable buttons used as labels
Call disableControl disable, 0, 3
'Button can still be written to even though disabled
#m1.L0, "Serial"
#m1.tb1, "!font technical 12"
'font for listbox (only use fixedwidth font or columns will not align)
#m1.lb1, "font courier_new 10"
#m1.lb1, "singleclickselect"
#m1.tb1, "Click items in the listbox to add";Chr$(13);"those items to your shopping list!";_
         Chr$(13);"Then click BUY";Chr$(13);"Apologies to female programmers,";Chr$(13);_
         "for the list contents!"
buy=1
Wait
'
[shop]
#m1.b1, "!locate 300 440 70 20"
#m1, "refresh"
#m1.tb1, "!cls"
#m1.tb1, "You have bought:"
For i=1 to buy
#m1.tb1, buy$(i)
Next
#m1.tb1, "Total cost: ";totalcost
If totalcost <>0 Then #m1.tb1, "Please mail me with your";Chr$(13);"credit card number!"
Wait
'
[show]
'show choice from listbox in texteditor
If editControl<>1 Then
If buy=1 Then #m1.tb1, "!cls"
#m1.lb1, "selectionindex? index"
#m1.tb1, "Selected from listbox:"
#m1.tb1, "Serial is ";serial$(index)
#m1.tb1, "Item is ";item$(index)
#m1.tb1, "Cost is ";cost$(index)
buy$(buy)=array$(index)
totalcost=totalcost+Val(Right$(cost$(index),5))
buy=buy+1
End If
Wait
'
[edit]
editControl=1-editControl
If editControl=1 Then
    Call disableControl enable, 1, 2
    #m1.b3, "Lock"
    #m1.tb1, "!cls"
    Else
    Call disableControl disable, 1, 2
    #m1.b3, "Edit"
    #m1.lb1, "selectindex 0"
End If
Wait

[editItem] 'edit item
#m1.lb1, "selectionindex? index"
If index<>0 Then
    p$=item$(index)
    Prompt "Enter new item..."; p$
    If p$<>"" Then
        '**** reformatting *********
        item$(index)=formatTrailingSpaces$(14, p$)
        'update listbox array
        array$(index)=makeListboxEntry$(index)
        #m1.lb1, "reload"
        #m1.lb1, "selectindex "; index
    End If
End If
Wait

[editCost] 'edit cost
#m1.lb1, "selectionindex? index"
If index<>0 Then
p$=cost$(index)
Prompt "Enter new cost..."; p$
If p$<>"" Then
    '**** reformatting *********
    char=Asc(Left$(p$,1))
    If char<48 or char>57 Then p$=Mid$(p$,2)
    temp$=formatCost$(Val(p$))
        If Len(temp$)<=7 Then
           cost$(index)=formatCost$(Val(p$))
           'update listbox array
           array$(index)=makeListboxEntry$(index)
            #m1.lb1, "reload"
            #m1.lb1, "selectindex "; index 'keep item selected
            Else
            Notice "Invalid entry"
        End If
    End If
End If
Wait

[spare]
'The number of columns in the list box can be expanded to suit.
Wait

[info]
Notice "About Program";Chr$(13);"Formatted Listbox with column labels";Chr$(13);_
       "Data for each column is from a separate array."
Wait
'
[quit]
Close #m1
End

Sub disableControl state, strt, fin
    For n=strt to fin
    hC=hCont(n)
    CallDLL #user32, "EnableWindow",_
    hC As long,_
    state As ushort,_
    result As void
    Next n
End Sub

Function formatLeadingZeros$(fieldWidth, ser$)
    formatLeadingZeros$=Right$("00000",fieldWidth-Len(ser$))+ser$
End Function

Function formatTrailingSpaces$(fieldWidth, ft$)
    temp$=ft$+Right$(Space$(fieldWidth),fieldWidth-Len(ft$))
    formatTrailingSpaces$=Left$(temp$,fieldWidth) 'truncate if too long
End Function

Function formatCost$(cost)
    formatCost$=""+Using("###.##",cost)
End Function

Function makeListboxEntry$(n)
    makeListboxEntry$=serial$(n)+Space$(1)+item$(n)+Space$(1)+cost$(n)
End Function

'data to fill arrays for demo
Data "Socks(2)",5.35,"Tie",7.29,"Shirt",12.50,"Tie pin",1.99,"Gloves",9.95
Data "Wool jumper",23.68,"Shoes(lace)",57.75,"Tie",8.34,"Slippers",11.67,"Socks(3)",5.49
Data "Silver Pen",5.35, "Hairbrush",7.29,"Razor",12.50,"Toothbrush",1.99
Data "Sun lotion",9.95,"Trainers",23.68,"Shoes(slip-on)",57.75,"Mouse",8.34,
Data "Gold pen",11.67,"Mousemat",3.49

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

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
---------------------------------------------------------
---------------------------------------------------------

