Liberty Basic is develeopped by Carl Gundel
Original Newsletter compiled by Alyce Watson and Brosco
Translation to HTML: Raymond Roumeas

The Liberty Basic Newsletter - Issue #59 - DEC 99

© 1999, Cliff Bros and Alyce Watson All Rights Reserved

In this issue:

  1. Thanks!
  2. 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:

and contributors:

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. f 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:brosc-@orac.net.au or mailto:awatso-@wctc.net