---------------------------------------------------------
The Liberty Basic Newsletter - Issue #59 - DEC 99
       1999, Cliff Bros and Alyce Watson
             All Rights Reserved
---------------------------------------------------------
In this issue:
    Thanks!
    Writing an editor for Liberty BASIC - Part Two

In future issues:
    Writing an editor for Liberty BASIC - tooltips
---------------------------------------------------------
THANKS!

Thanks to everyone who has participated in this group
over the past year.  Thanks for your patience with me as
I attempted to carry on in Brosco's absence.

A special thank you to the kind and clever people who
wrote articles for this newsletter:

    Stev Harney
    Robert Oakes
    Dean Hodgson
    Carl Courtney
    Ian Davies
    Doyle Whisenant
    Herman Oosthuysen
    David Mosley
    Larry Dunham

    and contributors:
        Brent Thorn
        Brad Moore

Best wishes to everyone for a peaceful and prosperous
new millenium.

    - Alyce Watson
---------------------------------------------------------
ADD A TOOLBAR - BUTTONS

In keeping with our desire to eliminate extra files from
this project, we'll use regular push buttons, and not
bitmap buttons.  There is another reason to use regular
push buttons, however.  If a user has a desktop color
scheme in force that uses non-standard colors, then using
bmpbuttons with gray backgrounds does not look good!
Regular buttons will automatically be drawn in the right
color by Windows.

What buttons do we need?  Let's make buttons for all of
the functions we can access with menus, except for the
edit functions, which are handled automatically by LB.
A button command is in this form:

    BUTTON #WINDOW.EXT, "Caption",[branch],UL,x,y[,w,h]

Each button should be designated with the window's handle,
then a dot, then a unique extension.  Just as with variable
names and branch labels, descriptive extensions for controls
make our job easier.  We know at a glance that #1.print is
the handle of the PRINT button.  That would not be so 
obvious if the label were #1.button5.

---------------------------------------------------------
BUTTON PLACEMENT

After the button caption, we designate the branch label
where program execution should proceed when the button is
clicked.  Then we must tell LB which corner of the window
is the starting point when setting button placement.  I
ALWAYS use UL, or upper left corner, because I find that to
be the least confusing.  After the corner, we designate
the x,y placement of the button within the client area.
After that, we may set a width and height for the button.
If we do not, LB will automatically size the button according
to its caption and the size of the font.  Since we are using
a number of buttons, and we want to assure that they do not
overlap one another, and that they have a uniform appearance,
we'll give them each the same width and height.  Since we
want to reserve as much of the client area of the window as
possible for the texteditor, we'll make these buttons small -
length 30, height 15.  Again, to save space, we'll place them
quite high upon the window, at y location of 2.

    button #1.new,     "New",   [new],       UL,3,2,30,15

Since we start the button at y=2 and it is 15 pixels high,
we'll change the location of our texteditor, so that it
does not cover any part of the buttons - at y=20.  We'll
also want to decrease the height of the texteditor to
accomodate the loss of the top 20 pixels:

[resize]'** LOCATE TEXT EDITOR
    '** LOCATE TEXTEDITOR 20 PIXELS FROM TOP OF CLIENT AREA
    print #1.t, "!locate 0 20 ";WindowWidth-1;" ";WindowHeight-21

We've started our first button at x=3 from the left side of
the window.  It is 30 pixels wide.  Clearly, we want to place
our next button at x>=33, so that it doesn't cover part of the
first button.  We could place it at x=33, so that the buttons
would sit next to one another, with no space in between.  
Instead, let's give it an extra pixel of breathing room
and place the second button 31 pixels to the right of the
first button, at x=34.  The following button will be placed
31 pixels further, at x=65, and so on.

If you have looked at commercial applications, you may
have noticed that buttons are often grouped in clusters
according to function.  We can do that easily, too.  Let's
place them in groups matching the functions in the menus.
We need only place a few extra pixels between the ending
button for one group of functions, and the beginning
button for the next group.  The first group we'll list will
be the File functions, followed by the Run functions.
Let's place the Run button at a 40 pixel x-increment
instead of the 31 pixel increment we have been using.

    button #1.print,   "Print", [print],     UL,127,2,30,15

    button #1.run,     "Run",   [run],       UL,167,2,30,15

We'll use the 31 pixel increment for all of the Run
functions, then a 40 pixel increment to divide the Run
functions from the Externals functions.  And so it goes.
---------------------------------------------------------
READABLE CODE

We've grouped our buttons on the window.  Let's also group
them in our code listing, for ease of reading.  Let's also
go that extra distance, and line up vertically.  The more
visual cues we give ourselves, the easier it is to find
what we need and to edit our code:

    button #1.new,     "New",   [new],       UL,3,2,30,15
    button #1.open,    "Open",  [open],      UL,34,2,30,15
    button #1.save,    "Save",  [save],      UL,65,2,30,15
    button #1.saveas,  "..As",  [saveas],    UL,96,2,30,15
    button #1.print,   "Print", [print],     UL,127,2,30,15

    button #1.run,     "Run",   [run],       UL,167,2,30,15
    button #1.debug,   "Debug", [debug],     UL,198,2,30,15
    button #1.token,   "TKN",   [maketkn],   UL,229,2,30,15
    button #1.runtkn,  "R tkn", [runtkn],    UL,260,2,30,15

    button #1.paint,   "Paint", [paint],     UL,300,2,30,15
    button #1.file,    "F Mgr", [winfile],   UL,331,2,30,15
    button #1.note,    "Note",  [notepad],   UL,362,2,30,15
    button #1.calc,    "Calc",  [calculator],UL,393,2,30,15

    button #1.help,    "Help",  [help],      UL,433,2,30,15
    button #1.tutor,   "Tutor", [tutorial],  UL,464,2,30,15
---------------------------------------------------------
FONTS

If we popped the above routine into our editor code, we
would immediately see a BIG error!  The captions are TOO
BIG for the buttons!  When we take over the task of sizing
the buttons, we must make sure that they display their
captions properly.  We have two choices here.  We can 
increase the size of all of the buttons, testing to be 
sure that even the longest caption displays properly.

Our other choice is to issue a FONT command, changing the
size of the font used to display the button caption.  Since
we want to keep our buttons small, we'll change the font
to a small one.

The syntax for a font command to a button is:
    
    PRINT #w.button, "!font facename w h"

Note the use of a "!"  Because we can print a new caption
on a button, we put the exclamation character first in
our command, to alert LB that this is a command, not a
new text string to place on the button.

    print #1.new,     "font arial 0 12"

The command above would result in a button that still
displays the default font, but now contains the words,
"font arial 0 12."  I know, because I've made that error!

Just a reminder about font commands.  If the name of the
font contains more than one word, the blanks must be filled
with an underscore character"

    print #w.button, "font courier_new 0 20"

Notice also that we have designated a width of 0.  We can
designate a width, of course, but if we set the value to
0, we allow Windows to calculate the proper font width
for the height we have chosen.
 
Let's change the fonts on all of our buttons:

    print #1.new,     "!font arial 0 12"
    print #1.open,    "!font arial 0 12"
    print #1.save,    "!font arial 0 12"
    ....
    print #1.tutor,   "!font arial 0 12"
---------------------------------------------------------
A BETTER-LOOKING TOOLBAR

If we test the program now, we'll see our buttons lined
up at the top of the client area, which is white.  That
really doesn't look too good, does it?  We can color that
area, by placing a graphicbox there.  In a plain window
placing the graphicbox command before the button commands
will place the buttons on top of the graphicbox.  If you
do it the other way around, with the graphicbox command
after the button commands, the buttons will be covered!

    graphicbox #1.g, -1,-1,800,21  'toolbar
    button #1.new,     "New",   [new],       UL,3,2,30,15
    ...
---------------------------------------------------------
LOCATE THE GRAPHICBOX

It really doesn't matter what initial x,y placement, and
width and height we give the graphicbox, because the
program will perform the resize function at startup, since
we have given a RESIZEHANDLER command.  We need to set
our size and placement within our [resize] routine.

If we locate the graphicbox at 0,0 and give it a width of
WindowWidth, then its border will show.  To eliminate the
border, we can set the x,y origin of the graphicbox to
be a negative number, so we've used -1,-1.  We've made it
the width of the window + 2 so that its right-hand border
also is hidden.  There is no way (that I know!) to eliminate
the bottom border.

'** LOCATE GRAPHICBOX - WindowWidth+2 WIDE, 21 PIXELS HIGH
    print #1.g, "locate -1 -1 ";WindowWidth+2;" 21"
---------------------------------------------------------
COLOR THE GRAPHICBOX

We don't want to leave the graphicbox white, since that
would accomplish nothing!  We could fill with any of the
16 Liberty BASIC colors, then flush.  That would work
fine:

    print #1.g, "down; fill lightgray; flush"

Remember that we wanted to use regular buttons in part so
that they would be colored to match the user's color scheme?
The lightgray bar would be a good choice if we were using
bmpbuttons with a lightgray background, but since we are
using regular buttons, it might not look so good.  If the
user has a green color scheme, for instance, then we'd have
a lightgray button bar containing green buttons.

Oh well, I guess we are stuck with it.  How could we
possibly guess what a user's color scheme will be?

There is a way to retrieve all of the colors on a user's
system, with a call to GetSysColor.  We pass in the value
for the system item we want to know about, and the call
returns the color value as a long.  Here, we want to
match the color of the menu bar, so we use the Windows
constant COLOR_MENU, but of course LB requires that
Windows constants be preceeded by an underscore character:
_COLOR_MENU

[colorToolbar]
    '** GET SYSTEM MENU COLOR VALUE
    calldll #user,  "GetSysColor",_
        _COLOR_MENU as word,_
        menuColor as long

Hmmm... menuColor is a value like 12632264.  That's no
help!  We can do a little math to break the value
into its red/green/blue components.  The formula to
take RGB values from 0 to 255 and make a long integer
value from them is:

    longColor = (blue*256*256) + (green*256) = red

We might have an actual equation like this:

    longColor = (132*256*256) + (73*256) + 46

We use this in Liberty BASIC, but LB does the math for
us.  It applies in our RGB color commmand

    print #1.g, "color 132 73 46"

If we can retrieve the red/green/blue values we got
in our call to GetSysColor, then we can send them in
a graphics COLOR command to LB.

    print #1.g, "color ";red;" ";green;" ";blue

Notice that since red, green, and blue will be variables,
not literals, they must be placed OUTSIDE of the quote marks,
and set apart with semicolons ( ; )

We know how to do the math to make a long color value from
the red/green/blue, so we just do the opposite to get the
red/green/blue FROM the long value:

    '** RETRIEVE VALUES FOR red, green, blue
    blue = int(menuColor / (256 * 256))
    green = int((menuColor - blue *256*256) / 256)
    red = menuColor - blue *256 * 256 - green * 256

Okay, now our variable for "red" contains the proper value
for red, and so on.  Hey, we can't fill with an RGB color
in LB!  We can only use RGB color as a foreground color.
AHA, but we can set the size of our drawing pen to be as
large as we need, so we simply need to issue a color command,
the size command to make a wide pen, and then draw a line
across our graphicbox.  Here is is:

    '** FILL TOOLBAR WITH THIS COLOR
    print #1.g, "down;size 50; color ";red;" ";green;" ";blue
    print #1.g, "line 0 20 1200 20"
    print #1.g, "flush"
    RETURN

I've chosen to draw a line that is 1200 pixels long, so
that it will fill even a very wide graphicbox.  The excess
length does not show.  You could also place a drawing
command within your [resize] routine, and redraw the line
each time the Window is resized:

    print #1.g, "line 0 20 ";WindowWidth;" 20"

I think the first method is simpler, but either way is fine.
---------------------------------------------------------
COMPLETE CODE LISTING

Here is the updated code for the editor, now containing a
toolbar with buttons.  All new sections are flagged with

'** NEW **  

Next time, we'll add tooltips to the buttons.  Please do 
publish modifications to this code, adding your name to 
the list of authors.
---------------------------------------------------------
'** Liberty BASIC Newsletter
'** Open Source Editor
'** Please add your name to
'** the list of authors:
'**
'** Authors:
'**
'** Alyce Watson
'**
nomainwin
    open "user" for dll as #user
    dim info$(10,10)                'for file exist check
cursor hourglass

'variables:
'file$              name of file to open
'title$             titlebar caption
'h                  is window handle
'modflag$           is answer to modified query
'answer$            receiver variable for confirm messages
'tempfilename$      name to use when running and debugging code
'rowVar,columnVar   location of texteditor origin
'fileindex          used for counter in path/file separation
'filelength         used for counter in path/file separation
'shortFile$         just filename without path
'filePath$          path without filename
'saveit$            receiver for input from texteditor
'libertyexe$        path to Liberty.exe for running/tokenizing/debugging
'tknfile$           name of tkn to run

'** NEW **
'red                red value
'green              green value
'blue               blue value
'menuColor          system menu color


menu #1, "&File",_
    "&New",[new],_
    "&Open",[open],_
    "&Save",[save],_
    "Save &As",[saveas],|,_
    "&Print",[print],_
    "E&xit",[quit]

menu #1, "&Edit" 'LB supplies the Edit Menu

menu #1, "&Run",_
    "Ru&n", [run],_
    "&Debug",[debug],_
    "&Make TKN",[maketkn],_
    "Run &TKN",[runtkn]

menu #1, "E&xternals",_
    "&Paintbrush",[paint],_
    "&File Manager",[winfile],_
    "&Notepad",[notepad],_
    "&Calculator",[calculator]

menu #1, "&Help",_
    "&Liberty BASIC Help",[help],_
    "LB &Tutorial",[tutorial]

    texteditor #1.t, 0,40,600,400  'edit window

'** NEW **
    graphicbox #1.g, -1,-1,800,21  'toolbar
    button #1.new,     "New",   [new],       UL,3,2,30,15
    button #1.open,    "Open",  [open],      UL,34,2,30,15
    button #1.save,    "Save",  [save],      UL,65,2,30,15
    button #1.saveas,  "..As",  [saveas],    UL,96,2,30,15
    button #1.print,   "Print", [print],     UL,127,2,30,15

    button #1.run,     "Run",   [run],       UL,167,2,30,15
    button #1.debug,   "Debug", [debug],     UL,198,2,30,15
    button #1.token,   "TKN",   [maketkn],   UL,229,2,30,15
    button #1.runtkn,  "R tkn", [runtkn],    UL,260,2,30,15

    button #1.paint,   "Paint", [paint],     UL,300,2,30,15
    button #1.file,    "F Mgr", [winfile],   UL,331,2,30,15
    button #1.note,    "Note",  [notepad],   UL,362,2,30,15
    button #1.calc,    "Calc",  [calculator],UL,393,2,30,15

    button #1.help,    "Help",  [help],      UL,433,2,30,15
    button #1.tutor,   "Tutor", [tutorial],  UL,464,2,30,15

open "Open Source LB Editor" for window as #1

    h=HWND(#1)
    print #1, "trapclose [quit]"
    print #1, "resizehandler [resize]"

    calldll #user, "ShowWindow",h as word,_SW_MAXIMIZE as ushort,result As word

'** NEW **
    print #1.new,     "!font arial 0 12"
    print #1.open,    "!font arial 0 12"
    print #1.save,    "!font arial 0 12"
    print #1.saveas,  "!font arial 0 12"
    print #1.print,   "!font arial 0 12"
    print #1.run,     "!font arial 0 12"
    print #1.debug,   "!font arial 0 12"
    print #1.token,   "!font arial 0 12"
    print #1.runtkn,  "!font arial 0 12"
    print #1.paint,   "!font arial 0 12"
    print #1.file,    "!font arial 0 12"
    print #1.note,    "!font arial 0 12"
    print #1.calc,    "!font arial 0 12"
    print #1.help,    "!font arial 0 12"
    print #1.tutor,   "!font arial 0 12"


    print #1.t, "!setfocus";

'** NEW **
    gosub [colorToolbar]
    cursor normal

[loop]
    input a$



[quit]
    gosub [isModified]
    close #user: close #1
    if tempfilename$<>"" then kill tempfilename$ 
    END


[isModified]'** CHECK TO SEE IF FILE HAS BEEN MODIFIED
    print #1.t, "!modified?":input #1.t, modflag$
    if modflag$="true" then
        confirm "This file has been modified.  Save?";answer$
        if answer$="yes" then gosub [savesub]
    end if
    RETURN


[resize]'** LOCATE TEXT EDITOR
    '** LOCATE TEXTEDITOR 20 PIXELS FROM TOP OF CLIENT AREA
    print #1.t, "!locate 0 20 ";WindowWidth-1;" ";WindowHeight-21
    print #1.t, "!origin?";
    input #1.t, rowVar,columnVar

'** NEW **
    '** LOCATE GRAPHICBOX - WindowWidth+2 WIDE, 21 PIXELS HIGH
    print #1.g, "locate -1 -1 ";WindowWidth+2;" 21"

    '** UPDATE WINDOW
    print #1, "refresh"
    print #1.t, "!origin ";rowVar;" ";columnVar;"";
    goto [loop]


[new]'** CLEAR EDITOR CONTENTS, RESET VARIABLES
    gosub [isModified]
    print #1.t, "!cls"
    file$="untitled.bas"
    filePath$=""
    gosub [settext]
    goto [loop]



[open]
    gosub [isModified]
    filedialog "Open file..",filePath$+"*.bas",file$ 
    if file$="" then [loop]

    fileindex=len(file$)  'separate path and filename
    filelength=len(file$)
        while mid$(file$, fileindex,1)<>"\"
            fileindex=fileindex-1
        wend

'** USE FILES STATEMENT TO CHECK FOR FILE'S EXISTENCE
    shortFile$=right$(file$,filelength-fileindex)
    filePath$=left$(file$,fileindex)
    files filePath$,shortFile$,info$(
    if val(info$(0,0))<1 then
        notice "Error"+chr$(13)+"File does not exist."
        goto [loop]
    end if

[loadFile]'** OPEN FILE AND LOAD INTO TEXTEDITOR
    cursor hourglass
    open file$ for input as #file
    print #1.t, "!contents #file"
    close #file
    gosub [settext]
    print #1.t, "!origin 1 1";
    cursor normal
    goto [loop]



[settext]'** ADD FILENAME TO TITLEBAR
    title$="Open Source LB Editor "+file$
    calldll #user, "SetWindowText", h as word, title$ as ptr, result as void
    return



[saveas]'** SAVES CONTENTS AS file$
    filedialog "Save file as..",filePath$+"*.bas",file$ 
      if file$="" then
        notice "You must choose a file name."
        goto [loop]
      end if
    gosub [settext]

[save]'** SAVES CURRENT FILE
    gosub [savesub]
    goto [loop]


[savesub]
    cursor hourglass
    print #1.t, "!contents?";
    input #1.t, saveit$

'** IF THERE IS NO CURRENT FILENAME, ASK USER FOR ONE
    if (right$(file$,12)="untitled.bas") or (file$="") then
        filedialog "Save file as...",filePath$+"*.bas",file$ 
          if file$="" then
            notice "You must choose a file name."
            RETURN
          end if
    end if

    open file$ for output as #file
    print #file, saveit$
    close #file

    cursor normal
    notice "File saved as "+ file$
    RETURN


[print]
    cursor hourglass
    print #1.t, "!origin?";
    input #1.t, rowVar,columnVar
    print #1.t, "!contents?";
    input #1.t, saveit$ 
    lprint saveit$
    dump
    print #1.t, "!origin ";rowVar;" ";columnVar;"";
    cursor normal
    goto [loop]



[readyRun]'** MAKE A TEMP FILE TO RUN IN LIBERTY BASIC
'** GET CURRENT TEXTEDIT ORIGIN
    print #1.t, "!origin?";
    input #1.t, rowVar,columnVar

'** GET CONTENTS OF TEXTEDITOR AND SAVE TO TEMPFILE
    if tempfilename$<>"" then kill tempfilename$
    print #1.t, "!contents?"
    input #1.t, saveit$ 
    tempfilename$=filePath$+"tempfile.bas"
    open tempfilename$ for output as #temp
    print #temp, saveit$
    close #temp

    if libertyexe$="" then gosub [findLiberty]
    RETURN


[findLiberty]'** FIND LIBERTY.EXE
    filedialog "Find Liberty.exe","liberty.exe",libertyexe$ 
    RETURN


[run]
    gosub [readyRun]
    run libertyexe$+" -R -A -M "+tempfilename$ 
    print #1.t, "!origin ";rowVar;" ";columnVar;"";
    goto [loop]

[debug]
    gosub [readyRun]
    run libertyexe$+" -D -A -M "+tempfilename$
    print #1.t, "!origin ";rowVar;" ";columnVar;"";
    goto [loop]

[runtkn]
    filedialog "Choose TKN..","*.TKN",tknfile$
    run tknfile$
    goto [loop]

[maketkn]
    gosub [readyRun]
    run libertyexe$+" -T "+tempfilename$
    print #1.t, "!origin ";rowVar;" ";columnVar;"";
    goto [loop]



[paint]
    run "pbrush.exe",SHOWNORMAL
    goto [loop]

[notepad]
    run "notepad.exe",SHOWNORMAL
    goto [loop]

[winfile]
    run "winfile.exe",SHOWNORMAL
    goto [loop]

[calculator]
    run "calc.exe" ,SHOWNORMAL
    goto [loop]



[help]
    run "winhelp liberty.hlp"
    goto [loop]

[tutorial]
    run "winhelp tutorial.hlp"
    goto [loop]


'** NEW **
[colorToolbar]
    '** GET SYSTEM MENU COLOR VALUE
    calldll #user,  "GetSysColor",_
	_COLOR_MENU as word,_
	menuColor as long 

    '** RETRIEVE VALUES FOR red, green, blue
    blue = int(menuColor / (256 * 256))
    green = int((menuColor - blue *256*256) / 256)
    red = menuColor - blue *256 * 256 - green * 256

    '** FILL TOOLBAR WITH THIS COLOR
    print #1.g, "down;size 50; color ";red;" ";green;" ";blue
    print #1.g, "line 0 20 1200 20"
    print #1.g, "flush"
    RETURN
---------------------------------------------------------
Newsletter compiled and edited by: Brosco and Alyce.
Comments, requests or corrections: Hit 'REPLY' now!
mailto:brosco@orac.net.au or mailto:awatson@wctc.net
---------------------------------------------------------