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 #68 - MARS 2000

© 2000, Cliff Bros and Alyce Watson

All Rights Reserved

 

In this issue:

In future issues:


As you test the alpha/beta versions of Liberty BASIC 2.0, be sure to read the accompanying file, LBalpha.txt for updates on changes to existing features, and the addition of new features. Also, be sure to check out the sample programs included with the packet. It is a good idea to assume that any TKNs made with previous versions will need to be tokenized again to work with the current version.

Be sure to post bug reports! Test the sample programs, write routines of your own that use the new features, and try out your old programs in LB 2.0. If you do encounter an error, try to be as specific as possible when you report it, and remember that a small piece of code that duplicates the error is a real help to Carl.

And now, here is an overview of the new features:
NEW LOOK

You'll notice a new look in the Liberty BASIC editor. It now contains handy bitmap buttons on a toolbar, for the most often used features. It also contains a list of your most recently opened files in the file menu. Boy, it this one a time saver! Thanks, Carl!

New for alpha 5: system default colors are now used! Liberty BASIC matches colors to your desktop color scheme, even if it is nonstandard. This is true in the Liberty BASIC editor, itself, and in windows that are opened within your programs!


COLORS FOR WINDOWS AND CONTROLS

You may set the background color for windows of type "window" or type "dialog" to be any of the named 16 Liberty BASIC colors with the BackgroundColor$ function. If you do not choose a background color, the default system color will be used. You must name the color in Liberty BASIC syntax, but it does not matter whether you use uppercase, lowercase or a combination. If you do not specify a valid color, then the default color will be used. For instance, if you typed "blye" by accident, the default window fill color would be used.

 
Syntax:
    BackgroundColor$=   "lightgray"
 
Colors possible:
	white		black
	lightgray	darkgray
	pink		darkpink
	red		darkred
	blue		darkblue
	cyan		darkcyan
	green		darkgreen
	yellow	brown

(You may substitute "palegray" for "lightgray.")

You may alter the text color for windows of type "window" and type "dialog" as well with the ForegroundColor$ command. This will cause the text to be colored in the named Liberty BASIC color. Text for statictext, radiobuttons, listboxes, comboboxes, and checkboxes is affected, as well as text typed into textboxes and texteditors. If you do not specify a color, the system default color will be used. If you type an invalid color name, the default color will be used.

Syntax:

    ForegroundColor$=   "darkblue"
 
The color of controls can also be determined with the following
commands:
    TexteditorColor$=   "GREEN"
    TextboxColor$=      "Red"
    ComboboxColor$=     "lightPinK"
    ListboxColor$=      "yeLLow"

For all color commands, case does not matter, but spelling does matter. In all cases, if no color is chosen, or if an invalid color name is chosen, then the default color will be used. Comboboxes do not show color in the same way as the other controls. Window colors must be set before a window is opened, and control colors must be set before the command to create the control. See Colors01.bas for examples.

These commands do not affect graphics windows, or graphicboxes. You must still issue graphics color commands for them.

We've added some control color choices to the open source editor, for ForegroundColor$ and TexteditorColor$. The other colors will be system default colors.

    ForegroundColor$=   "darkblue"
    TexteditorColor$=   "lightgray"
 


CREATING CONTROLS WITH EXPRESSIONS

Prior to version 2.0, parameters for controls had to be hard-coded, but they may now be created with expressions.

For instance, if all buttons are to be the same width, you can now set a variable, say buttonwidth, to be equal to the desired width.

    buttonwidth = 120

The buttons could be created using this expression:

    button #1.1, "Open",[open],UL,10,20,buttonwidth,40
    button #1.1, "Edit",[edit],UL,200,20,buttonwidth,40

and so on. If you decide to change the button width value, you may now change it in just one place, rather than in each BUTTON statement. You may also use complex expressions, not just variables.

For instance, you may place a control at "xOrigin + 60" if you like.

Expressions may be used for placement, dimensions, labels, and even for the bitmap file used in a bitmap button:

width = 60
    height = 25
    xOrigin = 10
    yOrigin = 10
    label$ = "Button"
    bitmapFile$ = "run.bmp"
    button #main.sized, label$+" Label!", [click], UL, xOrigin+60, yOrigin,
width*3, height
    bmpbutton #main.run, bitmapFile$, [run], UL, xOrigin, yOrigin
    bmpbutton #main.bug, "bug.bmp", [bug], UL, xOrigin, yOrigin * 4
 
    listbox #main.list, array$(,[list],xOrigin,yOrigin*10,width+30,90
    combobox #main.combo, array$(,[list],xOrigin+100,yOrigin*10,width+30,90
    checkbox #main.check, "Check",[nuttin],[nuttin],xOrigin+300,yOrigin,100,24
    groupbox #main.group, "Group",xOrigin+280,yOrigin-5,150,60

At the time of this writing, you cannot use expressions for these controls:

    texteditor #main.text, 110,140,200,100
    textbox #main.box, 320,140,100,26
    radiobutton #main.radio, "Radio",[nuttin],[nuttin],450,10,100,24
    statictext #main.static, "Statictext",40,270,100,30

See Contrls.bas for examples.

We've updated the open source editor to take advantage of this new feature:

'**NEW** controls created with expressions:
    button #1.new,     "New",   [new],       UL,3,btnY,btnwide,btnhigh
    button #1.open,    "Open",  [open],      UL,34,btnY,btnwide,btnhigh
    button #1.save,    "Save",  [save],      UL,65,btnY,btnwide,btnhigh
    button #1.saveas,  "..As",  [saveas],    UL,96,btnY,btnwide,btnhigh
    button #1.print,   "Print", [print],     UL,127,btnY,btnwide,btnhigh
 
    button #1.run,     "Run",   [run],       UL,167,btnY,btnwide,btnhigh
    button #1.debug,   "Debug", [debug],     UL,198,btnY,btnwide,btnhigh
    button #1.token,   "TKN",   [maketkn],   UL,229,btnY,btnwide,btnhigh
    button #1.runtkn,  "R tkn", [runtkn],    UL,260,btnY,btnwide,btnhigh
 
    button #1.paint,   "Paint", [paint],     UL,300,btnY,btnwide,btnhigh
    button #1.file,    "F Mgr", [winfile],   UL,331,btnY,btnwide,btnhigh
    button #1.note,    "Note",  [notepad],   UL,362,btnY,btnwide,btnhigh
    button #1.calc,    "Calc",  [calculator],UL,393,btnY,btnwide,btnhigh
 
    button #1.help,    "Help",  [help],      UL,433,btnY,btnwide,btnhigh
    button #1.tutor,   "Tutor", [tutorial],  UL,464,btnY,btnwide,btnhigh
 


SET COMMAND

There is now a way to draw a single pixel in graphics windows and graphicboxes, using the SET command. If you specify an x, y location in the command, that is where the pixel will be drawn. If you do not specify a location, the pixel will be placed at the current pen position. The pixel will be drawn in the current COLOR.

Syntax:

    Print #graphics, "SET"	'draw pixel at current pen location
    Print #graphics, "SET 200 50"	'draw pixel at point 200, 50

Don't forget to put the pen "DOWN" or graphics will not show. Also, if a SIZE is chosen, other than the default SIZE 1, the SET command will place a circle in the specified location, whose diameter is equal to the current size. See Setrnd.bas for an example.

We've updated the Bitmap Previewer in the open source editor to make use of the SET command. We'll discuss this in more detail later in this article.


FONTS AND FONT DIALOG

We've been able to specify font facename and size to be used in controls and in graphics text. Version 2 gives us even more choices. We can size our fonts as we always have, by pixel size, or we can choose points as our unit of measrurement.

Applications like WordPad use points to size fonts. There are approximately 72 points in an inch. If we want to set the size as we have in the past, we simply give a facename, then a width and height for the font. If we set the width to 0, the width will automatically be computed by Windows:

print #1.textbox, "!font arial 0 14"  'default width
print #1.textbox, "!font arial 8 14"  'choose a width

Remember that we need that beginning "!" character in a font command if we are sending it to a control that can accept a new text string. This is true for statictext, button, textbox, and texteditor. We do not need the "!" character when sending a font command to a listbox, combobox, radiobutton or checkbox.

Note that we are still unable to send a command of any kind to a groupbox, since its syntax does not include an extension.

To send the above font command, but size the font by points:

print #1.textbox, "!font arial 10"

Choosing point size as a measurement means that you will need to choose a slightly smaller size than you may have done when using pixel size.

As always blank spaces in a font's facename must be replaced by underscores. "Courier New" becomes "Courier_New". The facename is not case sensitive, so "Arial" is the same as "arial".

Version 2.0 allows us to specify font attributes also, if we choose to do so. We may choose any combination of the following attributes:

bold

italic

strikeout

underscore (= underline)

The attributes are not case sensitive, so "BOLD" and "bold" are both correct. To create a font with attributes:

print #1.textbox, "!font courier_new 12 bold "

or

print #1.textbox, "!font arial 0 14 italic underscore"

The attributes may be listed in any order, but the facename should be listed first in the font command.

FONTDIALOG

We can now use a real Windows common font dialog. When the user makes a choice it is placed into a string receiver variable. The syntax for a font dialog is:

	fontdialog "facename size attributes", FontChosen$

As it might appear:

	fontdialog "courier_new 10 bold italic", newFontSpec$

Notice that you may specify a facename, size and attributes in the template string for the fontdialog command. These will be the default that displays in the font dialog. You may not send an empty template string into a font dialog. You must specify at least one of the attributes -- facename, size, or other attribute.

You may use variables to set the template string for the font dialog. If you do so, remember to place the variables OUTSIDE of the quote marks. Here is a sample:

    fontname$="arial"
    fontsize = 18
    fontattribute$ = "bold"
    fontdialog fontname$;" ";fontsize;" ";fontattribute$, myFont$ 

The user's choice is now in the variable myFont$. You can use this to set the font in a control, or in graphictext, as follows:

	print #1.button, "!font ";myFont$
	print #1.graphicbox, "font ";myFont$

See fonts.bas for an example program.

Earlier, we used Chris Robertson's excellent program to run Regedit and get a list of true type fonts, so that we could build our own font dialog. To update our open source editor for use with version 2.0, we can use this simple routine to replace our earlier one:

[fontdialog]
    fontdialog setFont$,setFont$
    print #1.t, "!font ";setFont$
    gosub [write.ini.file]
    goto [loop]


POPUPMENU

We can now have a menu popup where the mouse cursor is located with the PopupMenu command. The syntax to create a popupmenu:

PopupMenu "&Item 1", [branch1], "I&tem 2", [branch2],[....etc]

See the enclosed demo, menupop.bas for an example.

Although we cannot append a popupmenu to a regular menu item, as we see done in some applications, we can simulate this by calling a popup when a regular menu item is clicked. We have done this in our updated open source editor.

In the menu listing:
    "&Number Tools	->",    [numbertools],_
    "Time and &Date	->",   [timetools]
 
Later in the code:
[numbertools]
    popupMenu "&Decimal to Hex Converter",[dectohex],_
        "&Hex to Decimal Converter",[hextodec]
    goto [loop]
 
[timetools]
    popupMenu "Current &Time",[timenow],_
        "Today's &Date",[datenow]
    goto [loop]

Look at the code above. When the item "Number Tools" is clicked, the code advances to the branchlabel [numbertools] where a popupmenu is activated. The same happens when "Time and Date" is clicked; the program advanced to the [timetools] branchlabel and another popup menu is activated.


DECIMAL TO HEXADECIMAL CONVERSION

Prior to this, we could convert a hexadecimal number into its decimal equivalent with the hexdec() function. For instance,

print hexdec("FF")

would produce:

255

Liberty BASIC version 2.0 gives us a function to convert a decimal number into a hexadecimal number; the dechex$() function. For instance,

print dechex$(255)

would produce

FF

Note that a hexadecimal is actually a string, so you will need a string variable to hold a hex number:

num$ = dechex$(472)

These two functions can be used to make number conversion tools for use in our open source editor. We'll access them with the popupmenu we mentioned earlier.

[dectohex]
    prompt "Enter a number...";decnum$
    decnum=val(decnum$)
    notice dechex$(decnum)
    goto [loop]
 
[hextodec]
    prompt "Enter a hex number...";hexnum$
    notice hexdec(hexnum$)
    goto [loop]


TIME AND DATE FUNCTIONS

Prior to version 2.0, we had a single Date$() function:

print date$( )

Produces:

April 1, 2000

We now have several forms of the Date$() function:

  print date$()             ' Nov 30, 1999
  print date$("mm/dd/yyyy") ' 11/30/1999
  print date$("mm/dd/yy")   ' 11/30/99
  print date$("yyyy/mm/dd") ' 1999/11/30    for sorting
  print date$("days")       ' 36127         days since Jan 1, 1900

Using Date$() with empty brackets returns the date in the same form as always. If we choose, we can include a template string in our Date$() command so that the date is returned in a different format. See the list above.

Notice that we can even retrieve the number of days since Jan 1, 1990, which will be a real help in writing calendar programs and similar applications.

We've had a single time$() function to use in LB 1.4+. It returns the time on a 24-hour clock in the format hour:minutes:seconds

Print time$()

Produces

15:11:22

Version 2 offers two additional forms of the time$() function:

  time$ = time$()   'this is same old form, and does the same thing
  seconds = time$("seconds")   'returns seconds since midnight
  msecs = time$("milliseconds")  'returns milliseconds since midnight

We may use a template string within the brackets of the time$() function to return either the number of seconds that have ellapsed since midnight, or the number of milliseconds since midnight. These two forms can be useful in timing procedures, benchmarking and games.

Run the following little benchmarking program. It checks the number of milliseconds required to perform a routine using a for/next loop, and then checks the time required to perform the same routine using a while/wend loop:

msecs = time$("milliseconds")
 
for i = 0 to 100
    print i
next i
 
fornexttime = time$("milliseconds") - msecs
 
msecs = time$("milliseconds")
 
while j < 101
    print j
    j = j+1
wend
 
wendtime = time$("milliseconds") - msecs
 
print "For/next time was ";fornexttime;" milliseconds"
print "While/wend time was ";wendtime;" milliseconds"

The result of the program on my system is:

For/next time was 170 milliseconds

While/wend time was 220 milliseconds

We can now see that a for/next routine will run faster than a similar routine done with while/wend.

We can write some information tools for our open source editor that make use of the time and date functions. Let's give the user a NOTICE message, telling the various formats for Date$() and Time$():

[timenow]
    timemsg$="Current time is:  "+chr$(13)+_
    time$()+chr$(13)+chr$(13)+_
    "Seconds since midnight:  "+str$(time$("seconds"))+chr$(13)+_
    "Milliseconds since midnight:  "+str$(time$("milliseconds"))
    notice timemsg$
    goto [loop]
 
[datenow]
    datemsg$="Date is" +chr$(13)+_
    date$() +chr$(13)+_
    date$("mm/dd/yyyy") +chr$(13)+_
    date$("mm/dd/yy") +chr$(13)+_
    date$("yyyy/mm/dd") +chr$(13)+_
    "Days since Jan 1, 1900:  "+ date$("days")
    notice datemsg$
    goto [loop]


IMPROVED RANDOM FUNCTION

The previous Liberty BASIC Random function returned a gaussian distribution of numbers. This is sometimes called a "normal" curve or a "bell-shaped" curve. What it means is that numbers in the middle of the range occur far more frequently than numbers at either end. To apply this, consider a request for a random number between 1 and 10.

The code to produce this would be

 
num = int(rnd(1)*10)+1
 
RND() returns a random number between 0 and 1.  Here is a
simple random generator that uses RND() in its most simple
form:
 
	for i = 1 to 10
		num = (rnd(1))
		print num
	next i
 
The results:
 
	0.27641726
	0.65084272
	0.4926561
	0.76404649
	0.35322068
	0.37518757
	0.5097409
	0.62644616
	0.5469114
	0.73680641

As you can see, the return is always a fractional number, so we multiply by the number that is equal to the range we hope to produce, and then add 1 to "round up". A routine to produce a set of random numbers between 1 and ten:

	for i = 1 to 10
		num = int(rnd(1)*10) + 1
		print num
	next i

Here is the output:

6

6

7

6

7

4

3

7

7

6

Hmmm.... there are a lot of 6's and 7's there, but NO 1's 2's 9's and 10's. This might be "good enough" for some applications, but it often is not sufficiently random. Over time, LB programmers have come up with several methods to create a more truly random number. Now, with version 2.0, Liberty BASIC's random function is very much better.

SET and RND()

Here is a small program that uses the RND() function for multiple uses, and it also shows off the new SET graphics command:

WindowWidth = 410
WindowHeight = 440
open "random generator test" for graphics as #draw
print #draw, "trapclose [quit]"
print #draw, "down ; size 12"
for x = 1 to 5000
  print x
    if int(x/100)=x/100 then
    print #draw, "color ";int(rnd(1)*255); " ";int(rnd(1)*255); "
";int(rnd(1)*255)
    end if
  print #draw, "set "; int(rnd(1)*400); " "; int(rnd(1)*400)
next x
 
input a$
 
[quit]
    close #draw:end

The program draws 5000 spots on a grahpics window with the SET command, using the RND() function to determine the x, y placement of each spot. It also uses the RND() function to choose a red, green and blue color value for the spots. Run the program, and you will see that the entire screen fills uniformly with spots, and that the colors appear to be quite random as they are changed.

The color is changed every 100 passes through the loop.

There will be more about RGB color later in this article.


24-BIT BITMAPS

Up until now, Liberty BASIC has been able to load bitmaps into memory, and draw them on graphics windows or graphicboxes, as long as they are formatted to contain 256 or fewer colors. Programmers had to be careful to choose bitmaps with the proper format, and if a program's users were to be allowed to choose bitmaps, the bitmap file header needed to be read to trap the possibility of bitmaps in the wrong format.

If we tried to "loadbmp" a bitmap with 24-bit (16,000,000) color, the program would halt with an error.

That limitation no longer exists in Liberty BASIC 2.0. Liberty BASIC can now load and display all bitmaps!

To load and display a bitmap:

 
LOADBMP "hello","howdy.bmp"
	or
LOADBMP "hello",DefaultDir$+"\howdy.bmp"
	or
LOADBMP "hello","c:\pics\family\howdy.bmp" 

You may use the filename alone, with no path information, if the bitmap is contained in the program directory. This is the preferred way, actually! If you hard code a path, you cannot be sure that a program's users will have the bitmap in the same drive and directory as you have it. I see this quite a lot in sample programs, and it always generates an error. If the user chooses a bitmap to open with a filedialog, then the filename will contain the complete path information, but this is necessary for this kind of usage.

You may use 24-bit bitmaps for bmpbuttons.

Let's talk about filenames for a moment. Liberty BASIC, like all 16-bit applications, requires filenames to have no more than the DOS 8.3 format: 8 characters, maximum, then a dot, then an extenstion that has 3 characters, maximum. If you need bitmap files within your program, for bitmap buttons or displays, be sure to give them 8.3 style filenames. If the user chooses a file with a filedialog, the name will automatically be truncated properly for use with Liberty BASIC. Also, note that it doesn't matter what extension the file has! If you do not want your users to alter your program's bitmaps, you may give them filenames with a unique extension. I have a habit of using my initials for the file extension, so you will often see bitmaps, data files, etc. as part of my programs that have an .aaw extension. Example:

LOADBMP "hello","howdy.aaw" 

Here is a tiny program that will load and display any bitmap. You must, of course, substitute a valid bitmap filename!

open "Test" for graphics as #1
LOADBMP "hello","what.bmp" 
print #1, "drawbmp hello 0 0"
input a$ 

There are two more pieces of good news. The BMPSAVE command has existed in Liberty BASIC for a long time, but it has never worked. IT WORKS NOW!

BMPSAVE will save a named picture with the filename you specify. The named picture may be a bitmap you have loaded with LOADBMP, or a part of a graphic you have captured with GETBMP. Here is the syntax:

BMPSAVE "Picture", savebmpfile$ 
 
"Picture" can be from:
	LOADBMP "Picture","sample.bmp"
		or
	Print #graph, "Getbmp Picture 10 6 100 230; flush"

I have found it better to save a bitmap that is loaded with Getbmp rather than one loaded with LOADBMP to insure the correct color values. The syntax for GetBMP is:

print #graph, "Getbmp bmpname xorigin yorigin width height"

Don't forget that hard-coded values appear within the quote marks:

print #graph, "Getbmp nameit 12 67 122 144; flush"

while variable values must be OUTSIDE of the quote marks:

print #graph, "Getbmp ";nameit$;" ";xorg;" ";yorg;" ";bmpwide;" ";bmphigh
print #graph, "flush"

NOTE that you must preserve the BLANK spaces bewteen parameters! You must also FLUSH after a Getbmp command, if you want the BMPSAVE command to work!

Now, the other piece of good news is that GETBMP will work on displays set to 32-bit color resolution. Up until now, issuing a GETBMP command when a user's display was set to more than 24-bit resolution would halt the program with an error.

We'll change the way we save a bitmap in the Bitmap Previewer tool in the open source editor:

print #bit.box1, "getbmp tempPicture 0 0 ";bmpwidth;" ";bmpheight
    print #bit.box1, "flush"
    BMPSAVE "tempPicture", savebmpfile$ 


UPDATING THE OPEN SOURCE EDITOR TO LIBERTY BASIC 2.0

We've mentioned a number of items that have been changed in the open source editor, so that it makes use of the new features in version 2.0. The code is attached to this newsletter, and it is called open09.bas. All changes have been flagged as '**NEW**.

As of this writing, LB 2.0 requires us to place a graphicbox command last in the list of controls. This is the opposite of the way it was done previously, when the graphicbox was placed first in the list. If you forget to relocate the graphicbox commands in your programs, you will discover that buttons and other controls appear in the graphicbox, but they do not work!

BUT WAIT! Because the default window background color is now the proper system color, we no longer need to use a graphicbox at all to hold the buttons on our "toolbar". We'll remove the graphicbox and the routine to get the system menu color to fill the graphicbox. We'll also remove references to this graphicbox in our resize routine.

Because we are now using features that are only available in LB 2.0, we'll do a version check at the start of the code:

    if val(Version$)<2 then
        notice "This program is meant for in LBv2.0!"
        end
    end if

We should now add a list of functions to the list of branch labels in our Branch Label routine, since functions are now a part of Liberty BASIC coding. We'll add this check to the routine that loads branch labels into an array to be placed in the listbox in our Branch Label dialog window:

if lower$(left$(trim$(line$),8))="function" then
        branch$(bx)=trim$(line$)
        bx=bx+1
    end if

!!BUGS!!BUGS!!BUGS!!BUGS!!BUGS!!BUGS!!BUGS!!BUGS!!BUGS!!BUGS!!

At the time of this writing, groupboxes do not work properly in version 2.0. Also, version 2.0 alpha cannot be run from the command line. If you use the open source editor to write code, it must be code that will run in version 1.4x, and it must call up Liberty.exe, not LBalpha.exe. Carl is working these out!


REWRITE BITMAP PREVIEWER & RGB COLOR CHOICE

We've already mentioned that it is no longer necessary to trap bitmaps with more than 256 colors, so we can throw that part of the code out the window! We'll now be able to load and display any bitmap the user chooses to preview.

We can take advantage of the new user functions to get the height and width of the bitmap. Let's call the functions HeightBitmap and WidthBitmap to be nicely descriptive.

We'll set them up so that we include the filename of the bitmap as a parameter, and the function returns the dimension of the bitmap:

 
    bmpheight=HeightBitmap(picFile$)
    bmpwidth=WidthBitmap(picFile$)

Here are the functions:

function WidthBitmap(name$)
open name$ for input as #pic
pic$=input$(#pic,29)
close #pic
WidthBitmap = asc(mid$(pic$,19,1)) + (asc(mid$(pic$,20,1)) * 256)
    end function
 
    function HeightBitmap(name$)
        open name$ for input as #pic
        pic$=input$(#pic,29)
        close #pic
        HeightBitmap = asc(mid$(pic$,23,1)) + (asc(mid$(pic$,24,1)) * 256)
    end function

Just as we did before, we open the file and read the first 29 characters so that we can evaluate the dimensions of the bitmap. We end by setting the function equal to that value. In the case of the width:

WidthBitmap = asc(mid$(pic$,19,1)) + (asc(mid$(pic$,20,1)) * 256)

For the height:

HeightBitmap = asc(mid$(pic$,23,1)) + (asc(mid$(pic$,24,1)) * 256)

In the routine that opens the bitmap, we call the functions like this:

    bmpheight=HeightBitmap(picFile$)
    bmpwidth=WidthBitmap(picFile$)

Now the dimensions of the bitmap are contained in the variables bmpwidth and bmpheight.

When the user chooses to SAVE, we no longer need to read the entire bitmap file into a string and write it to a new file. We can capture it from the screen with GetBmp and save it AS the filename chosen by the user:

    filedialog "Save As..","*.bmp",savebmpfile$ 
    if savebmpfile$="" then [loop]
 
    print #bit.box1, "getbmp tempPicture 0 0 ";bmpwidth;" ";bmpheight
    print #bit.box1, "flush"

BMPSAVE "tempPicture", savebmpfile$


RGB COLOR CHOICE

Liberty BASIC allows us to set the foreground color with an RGB value, in addition to using the 16 named LB colors.

Each attribute can have a value from 0 to 255, with 0 being none of a color, and 255 being complete saturation. Color 0 0 0 is black and Color 255 255 255 is white. Red is Color 255 0 0 and Green is Color 0 255 0. The syntax for setting color:

print #graphics, "color 211 187 54"

or

red=211 : green=187 : blue=54
print #graphics, "color ";red;" ";green;" ";blue

As always, variables must be placed outside of the quote marks in commands. It must seem redundant to mention this so many times, but it is a very common mistake that we all make from time to time, so the caution bears repeating.

Let's add the ability to draw directly on our bitmaps in the bitmap previewer, now that we can save them in an altered form. Thomas Watson has written a little utility that allows you to preview an RGB color choice. He has set up a dialog window that contains textboxes for entering the separate Red, Green and Blue values, and a preview box to see the results, when the Get Color button is clicked. Since this is a dialog window, the user can tab through the controls to input the desired color values. Setting the button handle of the Get Color button to #color.default makes that the action that is performed when the user presses ENTER.Each textbox is queried to retrieve the contents:

print #color.red, "!contents?"
    input #color.red, colorred

Then, a test is made to insure that the values are between 0 - 255:

if colorred>255 then colorred=255
    if colorred<0 then colorred=0

When the color choice has been retrieved for all three colors, a COLOR command is sent to the preview graphicbox. Remember that we need to place the variables outside of the quote marks!

print #color.color, "down; size 200; color ";colorred;" ";colorgreen;" ";colorblue
print #color.color, "line 50 0 50 100"

Giving the pen a size of 200 insures that the graphicbox will be filled with the chosen color when we draw the line across it.

When the user has obtained the color of his choice, the values are placed into a string variable to be used in a color command to the graphicbox that contains the bitmap:

    drawcolor$=str$(colorred)+" "+str$(colorgreen)+" "+str$(colorblue)
    print #bit.box1, "color "+drawcolor$

Now, the user can draw upon the bitmap in the color of his choice. Here is the complete code for the color choice, followed by a discussion of drawing on the bitmap:

WindowWidth=320:WindowHeight=350
    button     #color.default,   "Get Color",[get.col],
UL,200,10,100,30
    button     #color.okay,      "Close",
[quitColorChoice],UL,200,50,100,30
    textbox    #color.red,       20, 50, 150, 30
    textbox    #color.green,     20, 150, 150, 30
    textbox    #color.blue,      20, 250, 150, 30
    statictext #color.redtext,   "Red:  (0-255)", 20, 20, 100, 20
    statictext #color.greentext, "Green:  (0-255)", 20, 120, 100, 20
    statictext #color.bluetext,  "Blue:  (0-255)", 20, 220, 100, 20
    statictext #color.colorname, "Color is...", 200, 120, 100, 20
    statictext #color.colornum,  " ", 200, 150, 100, 20
    graphicbox #color.color,     200, 180, 100, 100
    open "RGB Color Choice" for dialog as #color
    print #color, "trapclose [quitColorChoice]"
    print #color.red,   str$(colorred)
    print #color.green, str$(colorgreen)
    print #color.blue,  str$(colorblue)
 
[get.col]
    print #color.red, "!contents?"
    input #color.red, colorred
    print #color.green, "!contents?"
    input #color.green, colorgreen
    print #color.blue, "!contents?"
    input #color.blue, colorblue
 
    if colorred>255 then colorred=255
    if colorred<0 then colorred=0
    print #color.red, str$(colorred)
 
    if colorgreen>255 then colorgreen=255
    if colorgreen<0 then colorgreen=0
    print #color.green, str$(colorgreen)
 
    if colorblue>255 then colorblue=255
    if colorblue<0 then colorblue=0
    print #color.blue, str$(colorblue)
 
    longcol=(colorblue*65536)+(colorgreen*256)+colorred
    print #color.colornum, str$(longcol)
 
    print #color.color, "down; size 200; color ";colorred;" ";colorgreen;"
";colorblue
    print #color.color, "line 50 0 50 100"
    print #color.red, "!setfocus"
    goto [loop]
 
[quitColorChoice]
    drawcolor$=str$(colorred)+" "+str$(colorgreen)+" "+str$(colorblue)
    print #bit.box1, "color "+drawcolor$
    close #color
    goto [loop]


DRAWING WITH THE SET COMMAND

The new Liberty BASIC SET command makes it REALLY easy to draw with our mouse cursor. We must SETFOCUS to the graphicbox, so that it is ready to receive mouse events.

We also must tell it the branch label to execute when a mouse even occurs. We'll send the program to our drawing routine whenever the left button is depressed, or when the mouse is moved while the left button is down. We must remember to put the pen down. We'll change the pen size to 2, since drawing with a 1-pixel-wide pen would be very tedious.

    print #bit.box1, "down"
    print #bit.box1, "size 2; color ";drawcolor$
    print #bit.box1, "setfocus; when leftButtonMove [drawit]"
    print #bit.box1, "when leftButtonDown [drawit]"

The coordinates for the mouse cursor are contained in the MouseX and MouseY variables. Since we have already issued a color command, all we need to do is use a SET command to place a dot where the mouse is located:

    print #bit.box1, "set ";MouseX;" ";MouseY

We'll trap the drawing routine so that it only allows drawing when the mouse is within the width and height of the bitmap:

if MouseX>bmpwidth or MouseY>bmpheight then [loop]

The code for the entire open source editor is attached to this newsletter. The code for the new version of the bitmap preview tool follows. This bitmap viewer/editor is quite limited in scope. Perhaps some of you would like to have a go at writing a better one?


[bmp] '** BITMAP PREVIEWER

'**NEW**  enlarge bitmap preview window:
    WindowWidth=640:WindowHeight=480
 
    button #bit.open, "Open",   [openbmp],  UL,350,5,80,26
    button #bit.save, "Save As",[savebmp],  UL,350,35,80,26
    button #bit.exit, "Exit",   [closebit], UL,350,65,80,26
 
'**NEW**  add drawing feature:
    button #bit.draw, "Choose Pen Color",[colorChoice],UL,450,5,160,26
    statictext #bit.ins, "Click and drag to draw on bitmap.",450,40,150,60
    textbox #bit.t1, 20,35,300,26
    textbox #bit.t2, 20,65,300,26
 
 
'**NEW**  enlarge graphicbox:
    graphicbox #bit.box1, 20,100,600,340
 
    statictext #bit.s, "Bitmap File:",20,5,150,20
    open "Bitmap Preview" for dialog_modal as #bit
    print #bit, "trapclose [closebit]"
 
    print #bit.t1,   "!font Times_New_Roman 0 16"
    print #bit.t2,   "!font Times_New_Roman 0 16"
    print #bit.save, "!font Times_New_Roman 0 16"
    print #bit.open, "!font Times_New_Roman 0 16"
    print #bit.exit, "!font Times_New_Roman 0 16"
    print #bit.draw, "!font Times_New_Roman 0 16"
    print #bit.s,    "!font Times_New_Roman 0 16"
    print #bit.ins,  "!font Times_New_Roman 0 16"
 
    print #bit.box1, "down"
    print #bit.box1, "size 2; color ";drawcolor$
    print #bit.box1, "setfocus; when leftButtonMove [drawit]"
    print #bit.box1, "when leftButtonDown [drawit]"
 
    goto [loop]
 
[closebit]
    print #bit.box1, "cls"
    close #bit
    if bmploaded=1 then unloadbmp "tempPicture"
    bmploaded=0
    goto [loop]
 
[drawit]
    if bmploaded=0 then
        notice "You must choose a bitmap first."
        goto [loop]
    end if
 
    if MouseX>bmpwidth or MouseY>bmpheight then [loop]
 
'**NEW**using SET command
    print #bit.box1, "set ";MouseX;" ";MouseY
    goto [loop]
 
 
 
[savebmp]
    if picFile$="" then
        notice "It is not possible to SAVE.  No Bitmap has been chosen."
        goto [loop]
    end if
 
    filedialog "Save As..","*.bmp",savebmpfile$ 
    if savebmpfile$="" then [loop]
 
'**NEW** new save bmp routine with BMPSAVE
'**be sure to GETBMP before saving, to preserve color!
    print #bit.box1, "getbmp tempPicture 0 0 ";bmpwidth;" ";bmpheight
    print #bit.box1, "flush"
    BMPSAVE "tempPicture", savebmpfile$ 
 
    cursor normal
    notice "Bitmap saved as "+savebmpfile$ 
    goto [loop]
 
 
[openbmp]
    filedialog "Open Bitmap","*.bmp", picFile$
    if picFile$ = "" then [loop]
 
    print #bit.t1, lower$(picFile$)
 
'**NEW** use functions to get bitmap dims:
    bmpheight=HeightBitmap(picFile$)
    bmpwidth=WidthBitmap(picFile$)
 
    if bmploaded=1 then unloadbmp "tempPicture"
    loadbmp "tempPicture", picFile$
    bmploaded=1
 
    print #bit.t2, "Width:  ";bmpwidth;"    Height:  ";bmpheight;""
    print #bit.box1, "cls"
    print #bit.box1, "drawbmp tempPicture 0 0"
    print #bit.box1, "flush"
    goto [loop]
 
 
 
'** NEW **
[colorChoice]
    print #bit.box1, "getbmp tempPicture 0 0 ";bmpwidth;" ";bmpheight
    print #bit.box1, "flush"
 
    WindowWidth=320:WindowHeight=350
    button     #color.default,   "Get Color",[get.col], UL, 200, 10, 100, 30
    button     #color.okay,      "Close",
[quitColorChoice],UL,200,50,100,30
    textbox    #color.red,       20, 50, 150, 30
    textbox    #color.green,     20, 150, 150, 30
    textbox    #color.blue,      20, 250, 150, 30
    statictext #color.redtext,   "Red:  (0-255)", 20, 20, 100, 20
    statictext #color.greentext, "Green:  (0-255)", 20, 120, 100, 20
    statictext #color.bluetext,  "Blue:  (0-255)", 20, 220, 100, 20
    statictext #color.colorname, "Color is...", 200, 120, 100, 20
    statictext #color.colornum,  " ", 200, 150, 100, 20
    graphicbox #color.color,     200, 180, 100, 100
    open "RGB Color Choice" for dialog as #color
    print #color, "trapclose [quitColorChoice]"
    print #color.red,   str$(colorred)
    print #color.green, str$(colorgreen)
    print #color.blue,  str$(colorblue)
 
[get.col]
    print #color.red, "!contents?"
    input #color.red, colorred
    print #color.green, "!contents?"
    input #color.green, colorgreen
    print #color.blue, "!contents?"
    input #color.blue, colorblue
 
    if colorred>255 then colorred=255
    if colorred<0 then colorred=0
    print #color.red, str$(colorred)
 
    if colorgreen>255 then colorgreen=255
    if colorgreen<0 then colorgreen=0
    print #color.green, str$(colorgreen)
 
    if colorblue>255 then colorblue=255
    if colorblue<0 then colorblue=0
    print #color.blue, str$(colorblue)
 
    longcol=(colorblue*65536)+(colorgreen*256)+colorred
    print #color.colornum, str$(longcol)
 
    print #color.color, "down; size 200; color ";colorred;" ";colorgreen;"
";colorblue
    print #color.color, "line 50 0 50 100"
    print #color.red, "!setfocus"
    goto [loop]
 
[quitColorChoice]
    drawcolor$=str$(colorred)+" "+str$(colorgreen)+" "+str$(colorblue)
    print #bit.box1, "color "+drawcolor$
    close #color
    goto [loop]
 
'**NEW** functions for bitmap dimensions:
    function WidthBitmap(name$)
        open name$ for input as #pic
        pic$=input$(#pic,29)
        close #pic
        WidthBitmap = asc(mid$(pic$,19,1)) + (asc(mid$(pic$,20,1)) * 256)
    end function
 
    function HeightBitmap(name$)
        open name$ for input as #pic
        pic$=input$(#pic,29)
        close #pic
        HeightBitmap = asc(mid$(pic$,23,1)) + (asc(mid$(pic$,24,1)) * 256)
    end function

Brosco and Alyce have written a Book for Liberty BASIC, which is available in electronic form on a CDROM. For details: http://alyce.50megs.com/sss/cd.htm   Newsletter compiled and edited by: Brosco and Alyce. Comments, requests or corrections: Hit 'REPLY' now! mailto:brosc-@orac.net.au or mailto:awatso-@wctc.net