ObjectViRCScript Documentation (for ViRC '97 1.00 and above) - Revision 7
=========================================================================

Introduction
============

What is ObjectViRCScript? ObjectViRCScript is a set of object-based (and now
object-oriented) extensions to the main ViRCScript language, adding a
class/property/event model to ViRCScript, much as C++ adds a similar model to
C.

Anyone who has programmed in Delphi before will feel instantly familiar with
OVS - it uses Delphi's class library!! This was facilitated by Delphi's very
comprehensive RTTI system which allows sufficient data to be extracted from
classes and so forth at runtime to make programming the ObjectViRCScript
interpreter relatively simple - OVS adds only about 1900 lines of code to the
main ViRCScript interpreter.

What does ObjectViRCScript add?
-------------------------------

OVS adds New and Destroy to create and destroy class objects, a new @p
statement to set properties, and a $prop function to get properties. Also added
is the ability to call object methods, either as statements or as functions to
return values. New MAPOBJECT function and UNMAPOBJECT statement added to enable
customization of ViRC '97's GUI (server windows, channel windows etc.). Adds
WITH/ENDWITH for making setting multiple properties of an object easier.
ADDTOSETPROP and REMOVEFROMSETPROP commands for dealing with objects' set
properties.

In addition, 0.94pre10 and above allow you to define your own classes, which
behave identically to the built-in Delphi VCL classes. Class support is
currently rather primitive (although properties, methods and simple
inheritance are supported), but will improve in later versions.

You can also use OVS to add tabs to the Client setup dialog and buttons to the
toolbars. This is described under the MAPOBJECT function, which is central to
extending V97's user interface with your own OVS code.

What's new in this release of ObjectViRCScript?
-----------------------------------------------

New in 0.82: support for enumerated and set properties, many object methods,
many new classes supported (including the TSockets class for writing sockets
applications in ViRCScript, and TTimer for making timers), and
MAPOBJECT/UNMAPOBJECT for customizing V97's built-in windows. New WITH/ENDWITH
statements. ADDTOSETPROP/REMOVEFROMSETPROP statements.

New in 0.82a: a few more implemented methods documented, corrected many
documentation inaccuracies (esp. in TSockets documentation).

New in 0.91: added SORT method to TListBox and TComboBox. Added GETDIRLIST and
GETFILELIST methods to TStringList object. Corrected documentation
inaccuracies (again). Improved TSockets a little. A few minor bug fixes.

New in 0.92: added InternalCall mechanism for chaining object events to
built-in code. Improvements to TSockets for multiple simultaneous connections
and other things. Added Execute method to TStringList. Added TFileListBox,
TDirectoryListBox, and TDriveComboBox. Documented TShape object (added a long
time ago, documented only now).

New in 0.94pre4: Added BEGINUPDATE and ENDUPDATE methods to TForm. Added new
TBitmap and TOfficeButton objects.

New in 0.94pre9: Added new TIcon object.

New in 0.94pre10: Added simple class support. Added CLASSOF and PARENTCLASSOF
functions.

New in 0.94pre10a: Support for TToolbarButton97, TToolbarSep97, and TEdit97
objects, and the ability to add objects to the toolbars. Added new POSITION
keyword to the NEW function.

New in 1.00rc3: Added new LoadFromAlias, SaveToAlias, LoadFromEvent and
SaveToEvent methods to TStringList.

New in 1.00almost-final2: Extended MAPOBJECT function to support DCC/TDCC file
transfer windows.

New in 1.00almost-final3: Added new MAPPEDOBJECTEXISTS function. Documentation
for MAPOBJECT function substantially improved.

New in 1.00final: The ability to pass parameters to an OVS class's constructor
has been added.

What's currently wrong with ObjectViRCScript?
---------------------------------------------

This is a difficult one ... not all methods of all Delphi objects are currently
supported, although support will improve greatly in future versions.

A very quick introduction to ObjectViRCScript
=============================================

This section has been updated slightly in 0.92.

This section is not intended to be a complete description of OVS and all its
subtleties. For the complete treatment, read the rest of the file. :) This is
just a quick section to get you started on the basics.

ObjectViRCScript adds objects to ViRCScript. An object can be a visual thing
like a form or a button on the screen, or a more abstract thing like a list
of strings (TStringList) or a socket control (TSockets) to send data over the
internet.

Objects are created with the $new() function, and destroyed with the Destroy
statement. Here's an example of some code which creates a form with a button
on it. You'll soon see what all of this code does after you've read the rest
of this section and the detailed descriptions of the $new() function later on.

        @ $form = $new(TForm)
        @p $form.Left = 10
        @p $form.Top = 10
        @p $form.Width = 200
        @p $form.Height = 200
        @p $form.Caption = Test form
        @p $form.Visible = True

        @ $button = $new(TButton ownedby $form)
        @p $button.Left = 10
        @p $button.Top = 10
        @p $button.Width = 75
        @p $button.Height = 25
        @p $button.Caption = Click me!
        @p $button.Visible = True

Anyone who has used the Borland Delphi or Borland C++Builder VCL class library
will feel instantly familiar with this style of coding.

To describe just a few of the lines above:

        @ $form = $new(TForm)     This creates a new object of class TForm
          ^            ^          and stores it in a variable called $form.
          Variable     Class

        @p $form.Visible = True   This assigns the value True to $form's
                 ^         ^      Visible property, making it appear on the
                 Property  Value  screen.


        @p $button = $new(TButton ownedby $form)  This creates a button that
                                          ^       is owned by $form. This just
                                          Owner   means the button sits inside
                                                  $form when it's displayed.

An object property is simply a "data slot" in the object which can store a
value, which can be a number, some text, or even another object. To set a
property, use:

        @p $object.Property = Value

For example, to change $form's caption to "Test form", do this:

        @p $form.Caption = Test form

When you are finished with an object, destroy it with the Destroy statement.
After an object has been destroyed, it disappears from the screen (if it's
a visual object) and is no longer accessible. For example, to destroy $form:

        Destroy $form

Destroying an object will also automatically destroy all objects that it owns.
So, in the above example, you don't need to destroy $button as well as
destroying $form. Destroying $form will destroy $button too, as $button was
created by the function $new(TButton ownedby $form).

Properties are read with $prop($object.Property), for example, this piece of
code will display $form's caption in the server notices window:

        TextOut > . clBlack $prop($form.Caption)

A method actually executes a piece of code within the object. A method "tells"
an object to do something, and may, or may not, return a value. An example of
a method is the TStringList object's Add method, which adds an item to the
string list, and returns the position in the list at which the item was added.
If you call the method as a command, as follows, the return value is discarded:

        $list.Add Test 1

However, the method can also be called as a function, which returns the index
the item has been added at, as follows:

        @ $itemindex = $list.Add(Test 1)

Notice that, when calling methods as functions, brackets are REQUIRED around
the parameters, as with calling a regular ViRCScript function. When calling
methods as statements (i.e. not as functions), DO NOT USE BRACKETS, just as
with as regular ViRCScript statement.

To try to make this clearer, in the object documentation given later on in this
file, the methods of each object are described. Some methods are given like
this:

        MethodX         - Does something

Others are given like this:

        MethodY()       - Does something, and returns something

MethodX returns nothing, and so can only be called like this:

        $object.MethodX

MethodY returns a value. It can either be called like this, to obtain the
value:

        @ $x = $object.MethodY()

Or it can be called like this, to discard the value:

        $object.MethodY

Remember that, although the method is documented as MethodY(), if you're
calling it as a statement, rather than a function, you DO NOT USE BRACKETS,
EVER. Remember this:

        Method called as a FUNCTION     - use brackets
        Method called as a STATEMENT    - do not use brackets

This is really no different to the standard ViRCScript syntax.

This syntax is pretty flexible, with the exception of one advanced situation
which you are probably never likely to encounter, but which I will mention
anyway just in case. There is currently a parser restriction in that, when
calling methods as functions, the object's name must consist of one variable
only. Let me illustrate this with an example:

This code WILL work:

        @ $itemindex = $list.1.2.Add(Test 1)

This will add the item "Test 1" to the object $list.1.2. However, imagine you
had 1 in $var1, and 2 in $var2, the following code WILL NOT work:

        @ $itemindex = $list.$var1.$var2.Add(Test 1)

This is because the object name is composed of more than one variable. Note
that every other usage of this object will work. These will all work:

        $list.$var1.$var2.Add Test 1
        @ $count = $prop($list.$var1.$var2.Count)

There is, however, a way around it. That is, assign a temporary variable
with the value of the object, and then use that. So, instead of doing:

        @ $itemindex = $list.$var1.$var2.Add(Test 1)

You would actually do:

        @ $temp = $list.$var1.$var2
        @ $itemindex = $temp.Add(Test 1)
        -@ $temp

I hope this is clear. I will attempt to remove this limitation in a future
version of OVS, although with the current state of the parser this change would
break existing code, which I always endeavour to avoid. I hope this is clear.

ObjectViRCScript extension details
==================================

NEW function
------------

Usage: $new(class)
       $new(class ownedby object)
       $new(class ownedby object position left,top,width,height)

Creates a new object of type class, and returns an instance handle. Class can
be any of the Delphi VCL classes, for example, TForm, TEdit, TButton, and so
forth. If the optional ownedby object parameter is specified, the object's
owner is set to that parameter. If you're creating a TForm, which has no owner
(actually ViRC '97 itself is the owner), you must leave this parameter off -
forms owning other forms does not make sense. Note: some objects, such as
TForm, are created invisible. To show them, you need to change their Visible
property (see the @P statement below). Examples:

The following code makes a new TForm object and assigns its instance handle to
the $form variable. Notice that

        @ $form = $new(TForm)

Once the form has been created and assigned to $form, the following code adds a
button to the form:

        @ $button = $new(TButton ownedby $form)

In addition, 0.94pre10a and above support the new position keyword. This allows
an object to be positioned and sized correctly during creation, avoiding the
need to set the Left, Top, Width and Height properties later on. Using the
position keyword makes control creation faster and less flickery. Example:

        // This creates a button at coordinates (10,20) which is 100 pixels
        // wide and 25 high. Equivalent to setting Left = 10, Top = 20,
        // Width = 100, Height = 25 after the button has been created.

        @ $button = $new(TButton ownedby $form position 10,20,100,25)

DESTROY command
---------------

Usage: Destroy object

Destroys object, along with all the objects it owns, freeing all the memory
associated with the objects. You should always use DESTROY to destroy any
object you have created to conserve memory. Note that, if you have a TForm
which owns a number of other controls, you only have to call DESTROY once for
the form object, as all the controls it owns will be automatically destroyed as
well. Example:

To destroy the form object $form and all controls that it owns:

        Destroy $form

DESTROY does _NOT_ deallocate the variable specified, only the object itself.
Therefore, unless you're storing the object handle in a local variable, you
should deallocate the variable after using the DESTROY command (e.g. -@ $form).

Note that Delete is synonymous with Destroy - you can use either.

@P command
----------

Usage: @p $object.Property = Value

Assigns Value to $object's Property property. For example, to change the TForm
object $form's caption, and make it visible, you could do:

        @p $form.Caption = This is a test!!
        @p $form.Visible = True

Please note that only a subset of Delphi's types are currently supported for
properties. They are: integers, strings, booleans (use either 1 and 0 or True
and False), enums, sets, and objects. Floats and variants are currently NOT
supported, although this will probably change in future versions of OVS. For
example, to set the active control on the form object $form to the button
object $button, use:

        @p $form.ActiveControl = $button

You can set enum properties either by name, or by index. You will probably
prefer to set them by name!! For example, the 2 lines below are equivalent,
causing the $edit object to align itself with the bottom of its parent form.

        @p $edit.Align = 2
        @p $edit.Align = alBottom

Of course, the latter is far more self-explanatory.

You can also retrieve set properties. Set properties are similar to enums,
except that multiple elements within the set can be set at once. A very common
set property (possibly the only one you will ever use) is TForm.BorderIcons.
This controls what icons are present in a form's border. The BorderIcons set
may contain any combination of biSystemMenu, biMinimize, and biMaximize. Set
properties MUST be included in []'s (even if there is only one member (or even
no members) in the set) and multiple elements must be separated by , (a comma).
Examples:

        // Makes the form's border contain only a system menu icon
        @p $form.BorderIcons = [biSystemMenu]

        // Show all the icons in a form's border
        @p $form.BorderIcons = [biSystemMenu,biMinimize,biMaximize]

        // Show no icons in the form's border
        @p $form.BorderIcons = []

You can also add or remove specific values from a set. See the ADDTOSETPROP and
REMOVEFROMSETPROP commands below for more information.

In addition, OBJECT EVENTS may be set with @p. This can be illustrated with
some very simple examples:

        @p $button.OnClick = MessageBox You clicked me!!

        @p $form.OnClose = Beep

If you want to execute more than one command in response to an object event,
define an alias and call that, for example:

        @p $closebutton.OnClick = CLOSEBUTTON_CLICK

        Alias CLOSEBUTTON_CLICK
          MessageBox I'll now close the form!!
          Destroy $form
        EndAlias

In addition, with some events, ViRC '97 will pass in extra information in the
form of local variables. In fact, V97 always passes in $Sender, which is the
object that caused the event. Thus you can create one alias which handles
events from a number of objects of different types. The following events are
supported:

        Mouse events
        ------------

        Event names: OnMouseUp, OnMouseDown
        Variables passed in: $Button - button pressed. Can be mbLeft, mbRight
                                       or mbMiddle.
                             $X      - X coordinate of where the mouse is.
                             $Y      - Y coordinate of where the mouse is.

        Mouse move events
        -----------------

        Event names: OnMouseMove
        Variables passed in: $X      - X coordinate of where the mouse is.
                             $Y      - Y coordinate of where the mouse is.

        Key events
        ----------

        Event names: OnKeyUp, OnKeyDown
        Variables passed in: $Key    - The scan code for the key pressed. You
                                       can modify this value in your
                                       OnKeyUp/OnKeyDown events to simulate as
                                       if a different key were pressed. For
                                       example, if you wish to nullify a key
                                       press, set $Key to 0 in your code.

        Key press events
        ----------------

        Event names: OnKeyPress
        Variables passed in: $Key    - The key pressed (e.g. A, 0, & etc). You
                                       can change this value in your
                                       OnKeyUp/OnKeyDown events to simulate as
                                       if a different key were pressed.

Drag-and-drop events are also supported, although their usage is rather
complicated, and I will refrain from documenting them until they work well.

ADDTOSETPROP command
--------------------

Usage: AddToSetProp object.property set

Combines set with the set property object.property. This is used to add values
to a set. For example, this code will display the minimize icon on a form's
border by adding the item biMinimize to the BorderIcons set:

        AddToSetProp $form.BorderIcons [biMinimize]

Note that if biMinimize is already present in the set, the second addition will
have no effect, in other words, ADDTOSETPROP will only add an item to a set if
it isn't already in the set. You can add multiple items to a set as well.
Example:

        AddToSetProp $form.BorderIcons [biMaximize,biMinimize]

ADDTOSETPROP is an efficient wrapper around the standard VS ADDTOSET function.
For example:

        AddToSetProp $form.BorderIcons [biMaximize,biMinimize]

Is equivalent to this:

        @p $form.BorderIcons = $AddToSet($prop($form.BorderIcons) [biMaximize,biMinimize])

REMOVEFROMSETPROP command
-------------------------

Usage: AddToSetProp object.property set

Removes any elements from object.property that are also present in set. For
example, the following code will remove the minimize icon from a form's border
by removing the biMinimize element from the set:

        RemoveFromSetProp $form.BorderIcons [biMinimize]

You can remove multiple items from a set at once (see above). Attempting to
remove an item from a set which isn't in the set will have no effect.

REMOVEFROMSETPROP is an efficient wrapper around the standard VS REMOVEFROMSET
function. For example:

        RemoveFromSetProp $form.BorderIcons [biMaximize,biMinimize]

Is equivalent to this:

        @p $form.BorderIcons = $RemoveFromSet($prop($form.BorderIcons) [biMaximize,biMinimize])

WITH/ENDWITH statement
----------------------

(should this be in VSCRIPT.TXT instead? Or as well?)

Usage: With command
         ...
       EndWith

This very useful statement makes setting multiple object properties (for
example) very easy. Basically, command is prepended to every line in the
WITH/ENDWITH block before execution. Example (from WEBSERV.VSC):

        @ $webform = $new(TTabbedForm)
        With @p $webform.
             Left = 20
             Top = 20
             Width = 300
             Height = 300
             FormStyle = fsStayOnTop
             Caption = ObjectViRCScript Web Server Example
             TabCaption = Web server
             Visible = True
        EndWith

This would be equivalent to:

        @ $webform = $new(TTabbedForm)
        @p $webform.Left = 20
        @p $webform.Top = 20

        etc.

As can be seen, WITH makes setting multiple object properties very simple.
Although this is the use WITH was designed for, it's possible to use it without
objects. For example, you can use this to make outputting multiple lines of
text to a query window very simple:


        With TextOut > TextQuery clBlue $null
             Hello!!
             This is a very good way ...
             ... to add lots of lines of text at once ...
             ... to a window.
        EndWith

The $null is required to ensure that the space after clBlue is not removed by
the parser.

PROP function
-------------

Usage: $prop($object.Property)

Returns the property Property of $object. For example, to retrieve the contents
of a TEdit control, you could use:

        @ $x = $prop($edit.Text)

The PROP function can currently return integer, string, boolean, enum, object,
and event properties. Other Delphi property types aren't supported yet, but
they should be in future versions of OVS.

The returning of event properties is supported as follows. The simplest usage
is something like as follows:

        @p $button.OnClick = BUTTON_CLICKED

Now, $prop($button.OnClick) will return the string BUTTON_CLICKED, as expected.
However, a more complex usage of this also exists. PROP can also be used to
return methods that are DEFINED INTERNALLY BY V97. For example, V97's main form
contains a control called ExitBtn, which is the quit button. You can use
the MAPOBJECT function to map this control, as follows:

        @ $exitbtn = $mapobject(!Main:ExitBtn)

The ExitBtn's OnClick event contains, of course, Delphi code to exit V97. But
PROP can't return anything sensible here, as it only makes sense to return OVS
code. Rather, PROP will return a string which looks something like this:

        InternalCall $004CD950 $00B11CAC $00B14D90

INTERNALCALL is a valid ViRCScript command which calls an internal section of
V97 code. The parameters passed in are pointers to the code, owner data and
object data required by the call. If the INTERNALCALL command returned by PROP
is executed, it will call the code in the original V97 event.

You will never use INTERNALCALL directly. Rather, it is very useful for
"hitching" ViRCScript code onto existing Delphi code in V97. For example, the
following code will display a message box when the user clicks on ExitBtn,
before actually calling the Delphi code which makes V97 quit:

        @ $exitbtn = $mapobject(!Main:ExitBtn)
        @ $icall = $prop($exitbtn.OnClick)
        @p $exitbtn.OnClick = HITCHED_EXIT

        Alias HITCHED_EXIT
          MessageBox ViRC '97 will now exit!!
          $icall
        EndAlias

The value returned in the variable $icall above is an INTERNALCALL command.
The command inside the variable is called with the line "$icall" in the new
code you define for the event.

OVSVER variable
---------------

Usage: $ovsver

This variable contains the ObjectViRCScript version number. V97 0.94pre10a and
above return an ObjectViRCScript version number of 7. The presence of this
variable is useful, as it allows you to test in your script whether the user is
running an ObjectViRCScript-capable version of ViRC '97 or not, for example:

if !($ovsver)
   MessageBox This script requires ViRC '96 0.80 or higher with ObjectViRCScript to function.
   Halt
endif

MAPOBJECT function
------------------

Usage: $mapobject(window[:control])

This is possibly THE most powerful function in ObjectViRCScript. What it does
is to return an ObjectViRCScript object handle for any built-in ViRC '97
window. You can then add controls to it or change properties as if it were an
object which you have created yourself. Note that this is a command for
advanced scripters only - and a knowledge of how the Delphi object-based VCL
works is highly beneficial here. If you don't understand this - don't worry.
You can write some great scripts without it, but good usage of this function
will allow you to integrate your scripts seamlessly into the V97 interface.

Practically any V97 window can be mapped as an OVS object with this function.
You can map server, channel, query, DCC Chat, and DCC/TDCC Get/Send windows.

The current server notices window is mapped by using . (a period) as the
window parameter.

A channel window is mapped by using #channel (e.g. to map #virc, use #virc as
the window parameter).

A query window is mapped by using nick (e.g. to map a query window to Mr2001,
use Mr2001 as the window parameter).

A DCC Chat window is mapped by using =nick (e.g. to map a DCC Chat window to
Mr2001, use =Mr2001 as the window parameter).

A DCC Send window is mapped by using *SEND/nick/filename (e.g. to map a DCC
Send connection to Mr2001, sending the file README.TXT, use
*SEND/Mr2001/README.TXT as the window parameter).

A DCC Get window is mapped by using *GET/nick/filename (e.g. to map a DCC
Get connection from Mr2001, getting the file README.TXT, use
*GET/Mr2001/README.TXT as the window parameter).

A TDCC Send window is mapped by using *TSEND/nick/filename (e.g. to map a TDCC
Send connection to Mr2001, sending the file README.TXT, use
*TSEND/Mr2001/README.TXT as the window parameter).

A TDCC Get window is mapped by using *TGET/nick/filename (e.g. to map a TDCC
Get connection from Mr2001, getting the file README.TXT, use
*TGET/Mr2001/README.TXT as the window parameter).

Some specific uses of this function are detailed below.

This function is designed primarily to be used in the <OnCreateWindow> event,
which is fired whenever a new channel window or server window is created. Two
parameters are passed to the event: $0 is the class of the window created
(TServerForm or TChannelForm), and $1 is the standard name of the window. So,
server windows always have a name of . (period), channel windows have names of
#channel, and so forth.

For example, this very simple example changes the colour of the text entry area
of server windows to a random colour whenever a new one is opened. Note that,
with server windows, $1 is always . (period), so you could replace
$mapobject($1:tbServerText) with $mapobject(.:tbServerText).

Event <OnCreateWindow> "TServerForm"
  @ $servertext = $mapobject($1:tbServCommand)
  @p $servertext.Color = $rand($FFFFFF)
  UnmapObject $servertext
EndEvent

Another example: to make text entry areas in channel windows appear at the top
of the window rather than the bottom, you could use:

Event <OnCreateWindow> "TChannelForm"
  @ $chanentry = $mapobject($1:tbChanCommand)
  @p $chanentry.Align = alTop
  UnmapObject $chanentry
EndEvent

Really, knowledge of all the names of the controls on server and channel
windows is needed, so I might release the source .DFM files for those forms so
anyone with Delphi can find the names themselves. However, this should get
you started:

Server window only
------------------

        tbServCommand   - TEdit entry box where the user types commands
        tbServerText    - TRichEdit where server text appears
        ServerSocket    - The actual server connection TSockets object
        ServerToolbar   - The toolbar containing the main server functions
        ServerSelectToolbar - The toolbar containing the server selection
                              combo box

Channel window only
-------------------

        tbChanCommand   - TEdit entry box where the user types commands
        tbChannelText   - TRichEdit where channel text appears
        NamesPanel      - TPanel containing lvNames
        lvNames         - TListView where the channel nick list is
                          *** NOTE *** TListView is not fully supported by
                          OVS yet. However, you can read/write a TListView's
                          ItemText and ItemIndex properties to fetch the
                          currently-selected nick in a channel nicks list, for
                          example (or you could use the regular ViRCScript
                          function $selectednick() for this, which is probably
                          a LOT simpler!! :)
        ChannelToolbar  - The toolbar containing the channel functions
        AttributesToolbar - The toolbar containing the B, U, I, and colour
                            selection buttons
        TopicToolbar    - The toolbar containing the topic selection edit box

Query windows
-------------

        tbChatCommand   - TEdit entry box where the user types commands
        tbQueryText     - TRichEdit where server text appears
        QueryToolbar    - The toolbar containing the query functions
        AttributesToolbar - The toolbar containing the B, U, I, and colour
                            selection buttons

DCC Chat windows
----------------

        DCCSocket       - The actual DCC connection TSockets object
        tbChatCommand   - TEdit entry box where the user types commands
        DCCChatToolbar  - The toolbar containing the query functions

Channel, server, query and DCC Chat windows
-------------------------------------------

        MainPanel       - TPanel containing text output box
        EntryPanel      - TPanel containing entry box
        WholePanel      - TPanel containing MainPanel and EntryPanel
        ToolbarPanel    - TPanel containing toolbar

DCC/TDCC transfer windows
-------------------------

        DCCSocket       - The actual DCC connection TSockets object
        txFrom          - TLabel corresponding to the "From:" or "To:" field
        txStatus        - TLabel corresponding to the "Status:" field
        txFilename      - TLabel corresponding to the "Filename:" field
        txSize          - TLabel corresponding to the "Size:" field
        txPercent       - TLabel corresponding to the "Progress:" field
        txSpeed         - TLabel corresponding to the "Speed:" field
        txTimeLeft      - TLabel corresponding to the "Time left:" field

The MAPOBJECT function can also be used to return an internal ViRC '97 form,
for example, the client setup form. The list of supported forms is as follows:

        !AboutBox
        !Aliases
        !ChannelBox
        !ChannelList
        !EditXDCCPack
        !EventManager
        !FingerClientDialog
        !TipForm
        !IRCServers
        !UserSetup
        !ListFilter
        !LoadingScript
        !Main
        !MenuEditor
        !PortScanner
        !ResumeDlg
        !Links
        !VSWizard
        !WHOIS
        !WhoList

You can thus add controls to any of these forms. For example:

        @ $aliasform = $mapobject(!Aliases)
        @ $button = $new(TButton ownedby $aliasform)

In addition, the !UserSetup form's page control is called SetupPages. Thus you
can add your own tab to ViRC '97's client setup dialog!! For example, this
code will add a new tab sheet to the client setup dialog which contains one
button:

        @ $SetupPages = $mapobject(!UserSetup:SetupPages)

        @ $NewTabSheet = $new(TTabSheet ownedby $SetupPages)
        With @p $NewTabSheet.
             PageControl = $SetupPages
             Caption = Test tab
        EndWith

        @ $NewButton = $new(TButton ownedby $NewTabSheet)
        With @p $NewButton.
             Left = 20
             Top = 36
             Width = 73
             Height = 65
             Caption = &Beep!!
             OnClick = Beep
        EndWith

        $SetupPages.RefreshTabs
        UnmapObject $SetupPages

If you do add pages to V97's client setup dialog, you must call the RefreshTabs
method to ensure that the pages are refreshed properly, otherwise you will
experience visual problems. You only need to call RefreshTabs on V97's client
setup dialog, never on your own tab controls.

Finally, how to add your own buttons to the toolbars. There are two supported
OVS objects for use with toolbars - TToolbarButton97, which is a toolbar
button, and TToolbarSep97, which is a button separator. These objects are
described in more detail later on, but I'll just give here a simple example on
how to add some buttons and a panel to the server window's toolbar.

First, you have to map the server window's toolbar using the $mapobject()
function. As described in the list above, the server window's toolbar is called
ServerToolbar (original, huh? :). Once it's mapped, you can add controls to it,
just like anything else.

Event <OnCreateWindow> "TServerForm"
  @l $toolbar = $mapobject($1:ServerToolbar)
  @l $form = $mapobject($1)

  // Load some bitmaps from disk - change these filenames to bitmaps
  // you actually have!!

  @l $bmp1 = $new(TBitmap)
  $bmp1.LoadFromFile bitmap1.bmp

  @l $bmp2 = $new(TBitmap)
  $bmp2.LoadFromFile bitmap2.bmp

  // Stop server form from updating until all the buttons have been added

  $form.BeginUpdate

  // Add a separator to the toolbar (toolbar buttons are now added to the right
  // of the toolbar automatically)

  @ $sep = $new(TToolbarSep97 ownedby $toolbar)
  @p $sep.Visible = True

  // Now add two buttons

  @ $button1 = $new(TToolbarButton97 ownedby $toolbar)
  @p $button1.Visible = False
  @p $button1.Glyph = $bmp1
  @p $button1.OnClick = Beep
  @p $button1.Visible = True

  @ $button2 = $new(TToolbarButton97 ownedby $toolbar)
  @p $button2.Visible = False
  @p $button2.Glyph = $bmp2
  @p $button2.Width = 90
  @p $button2.Caption = Click me!!
  @p $button2.OnClick = Beep
  @p $button2.Visible = True

  // Now add a panel

  @ $panel1 = $new(TPanel ownedby $toolbar)
  @p $panel1.Visible = False
  @p $panel1.Width = 70
  @p $panel1.Height = 20
  @p $panel1.Caption = Hello!!
  @p $panel1.Visible = True

  // Update the server form

  $form.EndUpdate

  UnmapObject $form
  UnmapObject $toolbar
EndEvent

In addition, when TToolbarButton97 objects are added to V97's server toolbar,
the Tag property takes on a special meaning. Setting Tag to 1 makes the button
automatically disable itself when not connected to a server. Setting Tag to 2
makes the button automatically disable itself when connected to a server. Note
that, when creating the button, you have to set the Enabled property to what
you want the initial state of the button to be. Tag only changes the state of
the button when a connection to the server is established or closed.

UNMAPOBJECT command
-------------------

Usage: UnmapObject object

The MAPOBJECT function creates a ViRCScript object handle representing a
built-in V97 object. You must use the UNMAPOBJECT command to remove the object
handle when you have finished using it, otherwise, the object handle will be
left in memory (each object handle only takes 4 bytes, but these can add up).
UNMAPOBJECT, unlike DESTROY, does not harm the underlying object, but merely
removes its ObjectViRCScript handle.

CLASS statement
---------------

Usage: Class name
         ...
       EndClass

       Class name extends parentname
         ...
       EndClass

Defines a new OVS class, name. If the extends keyword is specifed, the new
class will descend from parentname. This simply means that all properties and
methods are "copied" from the parentname class into your new class
automatically. Between the Class and EndClass statements are class definition
statements. You may currently define properties and methods. Properties, like
regular OVS object properties, are simply data holders. Methods are functions
which can be called, possibly passing parameters (these are available as $1,
$2 etc.). Here's a small example class:

        Class TDemoClass
          Property Name
          Method Hello
            MessageBox Hello from TDemoClass!! My name is $prop($Self.Name).
          EndMethod
        EndClass

Notice that, in every class method definition, a special local variable is
passed in, $Self. This is the object whose method is being called. If you're
using $prop() or @p inside a method to get or set a property, the property name
must be prefixed with $Self, as shown above. The above class can be used as
follows, just like a regular OVS class:

        @ $test = $new(TDemoClass)
        @p $test.Name = Fred
        $test.Hello

This will display a message box containing "Hello from TDemoClass!! My name is
Fred." on the screen. Now, I'll demonstrate inheritance. You can make a new
class, TDemoClass2, which will inherit all the properties and methods from
TDemoClass, and will add a new method too:

        Class TDemoClass2 extends TDemoClass
          Method Goodbye
            MessageBox Goodbye from TDemoClass2!! My name is $prop($Self.Name).
          EndMethod
        EndClass

The class can be used as follows:

        @ $test = $new(TDemoClass2)
        @p $test.Name = Fred
        $test.Hello
        $test.Goodbye

This will display the hello and goodbye messages, as expected. Now, everything
should be pretty clear.

Finally, there are a few special methods supported: <Create> and <Destroy>. The
<Create> method is automatically called when the object is created. The
<Destroy> method is automatically called when the object is destroyed. You can
put code in these methods, for example, to set up the properties in an object
to default values when it's created. Example:

        Class TDemoClass3
          Method <Create>
            MessageBox I'm being created!!
          EndMethod
          Method <Destroy>
            MessageBox I'm being destroyed!!
          EndMethod
        EndClass

In addition, in 1.00 final and above, extra parameters may be supplied to the
constructor, and these are available as $1, $2 etc. For example (thanks to
Mr2001 for this idea, and the following code snippet):

        Class TMessageMaker
          Method <Create>
            TextOut > $1 clGreen *** Happy new year!!
          EndMethod
        EndClass

        @l $obj = $new(TMessageMaker #virc)

Property handlers (methods that control the setting and getting of properties)
are currently not supported, but should be in a future version. Also, you
cannot inherit from any built-in (VCL) objects, like TButton. You can only
inherit from your own OVS objects you have defined yourself. This is because
insufficient information is available for the VCL objects at run-time to
inherit from them.

CLASSOF function
----------------

Usage: $classof(object)

Returns the class name of object. Returns TUnknown if object isn't valid.

PARENTCLASSOF function
----------------------

Usage: $parentclassof(object)
       $parentclassof(class)

Returns the name of the parent class of object (or class). Returns TUnknown if
the parent class cannot be retrieved, or if object or class isn't valid. All
objects ultimately derive from TObject. Objects created from OVS classes that
you have defined derive from TOVSObject, which, in turn, derives from TObject.

Special objects
===============

On startup, ViRC '97 creates the special object handle 0 which represents the
main ViRC '97 window. This is useful if you wish to manipulate the main V97
window, for example, the following code will hide ViRC '97:

        @p 0.Visible = False

Or you can add your own controls to ViRC '97's main window by setting their
owner to 0, for example, the following code will add a button to the main V97
window which quits V97 when you click on it:

        @ $mainbtn = $new(TButton ownedby 0)
        With @p $mainbtn.
             Left = 30
             Top = 60
             Width = 150
             Height = 25
             Caption = &Exit
             OnClick = Exit
        EndWith

Visual controls
===============

The following major visual controls are currently supported fully: TForm,
TTabbedForm, TButton, TBitBtn, TListBox, TComboBox, TFileListBox,
TDirectoryListBox, TDriveComboBox, TEdit, TMemo, TRichEdit, TCheckBox,
TRadioButton, TGroupBox, TPanel, TBevel, TShape, TTrackBar, TProgressBar,
TTabControl, TOfficeButton, TToolbarButton97, TToolbarSep97, TEdit97. Here I'll
try to document some of the features of each object.

All objects
-----------

The following properties are shared by all visual objects:

        Left            - X position of object
        Top             - Y position of object
        Width           - Width of object
        Height          - Height of object
        Visible         - (True or False) Controls whether object is visible
        Enabled         - (True or False) Controls whether object is enabled
        Color           - The background colour of the object
        Font.Color      - The object's font colour
        Font.Name       - The object's font name
        Font.Size       - The object's font size
        Font.Style      - (set) fsBold, fsItalic, fsUnderline, fsStrikeout
        Hint            - The tooltip that appears if the user holds the mouse over the control for a while without clicking
        ShowHint        - (True or False) Controls whether the tooltip is displayed or not
        Align           - Causes the control to "stick" to one edge of its parent, or to fill the whole client area. alNone, alTop, alBottom, alLeft, alRight, alClient
        Handle          - Returns the control's window handle (hWnd)

The following events are shared by all visual objects:

        OnClick         - Fired when object is clicked
        OnMouseMove     - Fired when mouse moved over object
        OnMouseDown     - Fired when mouse button pressed over object
        OnMouseUp       - Fired when mouse button released over object
        OnActivate      - Fired when the object gets the focus

Supported by most visual objects (where appropriate):

        OnChange        - Fired when object's selection (item, check mark, tab page, etc.) is changed

Methods:

        Repaint         - Causes the object, and all the objects it owns, to redraw themselves
        SetFocus        - Sets focus to the object
        BringToFront    - Brings the object to the front of the screen

Objects that introduce no additional properties or events will not be
documented further - their usage should be self-explanatory!!

In addition, most objects have a standard BorderStyle property (except for the
TForm), which can be bsNone for no border or bsSingle for a single black line
border.

TForm
-----

Properties:

        ActiveControl   - The control that has the focus when the form appears
        BorderStyle     - bsSingle, bsDialog, bsNone, bsSizeable
        FormStyle       - fsNormal, fsMDIForm, fsMDIChild, fsStayOnTop
        Position        - poDefault, poScreenCenter
        WindowState     - wsNormal, wsMaximized, wsMinimized
        Caption         - The form's caption
        BorderIcons     - (set) biSystemMenu, biMaximize, biMinimize
        Icon            - The form's icon. Normally the standard VS icon, but
                          this can be changed (TIcon - see the "Non-visual
                          objects" section below)

        OnClose         - Fired when the form is closed (it's a good idea to
                          DESTROY the form here, otherwise it will stay in
                          memory!!)

Events:

        OnResize        - Fired when the form's size is adjusted

Methods:

        ShowModal       - Shows the form modally. Control is not returned to
                          your program until the form is closed. If you want to
                          show the form non-modally (i.e., execution of your
                          code continues after the form has been displayed),
                          set the Visible property to True rather than calling
                          ShowModal.
        BeginUpdate     - Call this method before adding a large number of
                          controls to surpress redrawing.
        EndUpdate       - Call this method after adding a large number of
                          controls to re-enable redrawing (and also to redraw
                          the form immediately).

TTabbedForm
-----------

Exactly as above, except that a window tab (in the main V97 window) is made for
the form. Adds one new property:

        TabCaption      - The caption of the window's tab

TButton
-------

Properties:

        Caption         - The button's caption

Note that the Font.Style property is ignored on a TButton. Use a TBitBtn
instead if you wish to change the Font.Style property.

TBitBtn
-------

As above, only Font.Style can be set, for example:

        @p $bitbtn.Font.Style = [fsBold,fsUnderline]

Properties:

        Glyph           - The bitmap on the button (TBitmap - see the
                          "Non-visual objects" section below)

TListBox and TComboBox
----------------------

Properties:

        Items           - A TStringList object containing all the items
        ItemText        - The text of the currently-selected item (or empty if no item selected)
        ItemIndex       - The index of the currently-selected item (or -1 if no item selected)

Methods:

        Sort            - Alphabetically sorts the items in the list box or combo box

See later in this file under "Non-visual controls" for information on how to
manipulate a TStringList object.

TFileListBox
------------

This is just like a regular TListBox, although it is automatically filled with
a list of files and the list cannot be modified. It implements all the
properties of TListBox, and adds the following:

Properties:

        ShowGlyphs      - Controls whether small glyphs (icons) are displayed
                          to the left of each filename in the list box

TDirectoryListBox
-----------------

This is just like a regular TListBox, although it is automatically filled with
a list of directories and the list cannot be modified. It implements all the
properties of TListBox, and adds the following:

        Directory       - Returns the selected directory in complete form,
                          including the drive and full path
        FileList        - The associated TFileListBox control. If you are using
                          a TFileListBox and a TDirectoryListBox together,
                          assign the TDirectoryListBox's FileList property
                          to the TFileListBox control.

TDriveComboBox
--------------

This is just like a regular TComboBox, although it is automatically filled with
a list of drives and the list cannot be modified. It implements all the
properties of TComboBox, and adds the following:

        DirList         - The associated TDirectoryListBox control. If you are
                          using a TDriveComboBox and a TDirectoryListBox
                          together, assign the TDriveComboBox's DirList
                          property to the TDirectoryListBox control.

TEdit, TMemo, TRichEdit
-----------------------

Properties:

        Text            - Contains the text in the edit control
        SelStart        - The start of the text selection
        SelLength       - The length of the text selection (0 if nothing selected)
        SelText         - The selected text
        ReadOnly        - (True or False) Controls whether the text control is
                          read-only (not modifiable by the user)
        WordWrap        - (TMemo and TRichEdit only) Controls whether words are
                          wrapped on multiple lines
        Lines           - (TMemo and TRichEdit only) TStringList object that
                          contains the lines of text in the control (see
                          the "Non-visual controls" section below for
                          information on TStringList)

In general, you should use TEdit for single lines of text and TRichEdit for
multiple lines (in colour if necessary). You should never have to use TMemo,
which is the same as TRichEdit except you cannot output text with
TextOut > %$object (see VSCRIPT.TXT under TEXTOUT for more information on
this).

TCheckBox, TRadioButton
-----------------------

Properties:

        Checked         - Whether or not the control is checked
        Caption         - The caption on the check box or radio button

TGroupBox
---------

Properties:

        Caption         - The group box's caption

TPanel
------

Properties:

        Caption         - The text within the panel
        Alignment       - Text alignment. taCenter, taLeft, taRight
        BevelInner      - The inner bevel. bvNone, bvLowered, bvRaised
        BevelOuter      - The inner bevel. bvNone, bvLowered, bvRaised
        BevelWidth      - The width of the bevel

TBevel
------

Properties:

        Style           - The bevel's style. bsLowered, bsRaised
        Shape           - The bevel's shape. bsBox, bsFrame, bsTopLine, bsBottomLine, bsLeftLine, bsRightLine

TShape
------

Properties:

        Shape           - The shape of the shape (!). stRectangle, stSquare,
                          stRoundRect, stRoundSquare, stEllipse, stCircle
        Brush.Style     - The fill style of the shape. bsSolid, bsClear
        Brush.Color     - The fill colour of the shape
        Pen.Color       - The outline colour of the shape
        Pen.Width       - The outline width of the shape

TTrackBar
---------

Properties:

        Min             - The minimum value of the track bar
        Max             - The maximum value of the track bar
        Position        - The track bar's position
        Orientation     - trHorizontal, trVertical

TProgressBar
------------

Properties:

        Min             - The minimum value of the progress bar
        Max             - The maximum value of the progress bar
        Position        - The progress bar's position

TTabControl (tabbed notebook)
-----------------------------

Properties:

        Tabs            - TStringList containing all the tabs in the tabbed
                          notebook
        TabIndex        - The number of the currently selected tab. -1 means
                          no tab selected, 0 means the first tab is selected,
                          1 the second, etc.

For information on how to use the TStringList object, see the "Non-visual
controls" section.

TOfficeButton (Office 97-style toolbar button)
----------------------------------------------

NOTE: TOFFICEBUTTON IS NOW DEFUNCT, AND SHOULD NOT BE USED. USE
TTOOLBARBUTTON97 INSTEAD.

Properties:

        Caption         - The caption on the button
        Bitmap          - The bitmap on the button (a TBitmap object)
        NoFocusBitmap   - The bitmap on the button when it is not selected
        UnselectedColor - The colour of the caption when it is not selected
        Layout          - Where the bitmap is placed, relative to the caption.
                          blBitmapLeft, blBitmapTop, blBitmapRight, or
                          blBitmapBottom

Events:

        OnClick         - Fired when the button is pressed.

TToolbarButton97 (Office 97-style button)
-----------------------------------------

The properties of TToolbarButton97 are identical to those of TBitBtn, except
that the button is displayed with a different style.

TToolbarSep97 (button separator)
--------------------------------

This control has no properties, apart from Left, Top, Width and Height. Just
create it, set its size, and that's it.

TEdit97 (Office 97-style edit control)
--------------------------------------

This control is identical to TEdit, except that the edit box is displayed with
a different style.

Non-visual controls
===================

TStringList (string list object)
--------------------------------

The TStringList is a very powerful object. It can store a list of strings,
which can be saved to and from disk, sorted, and manipulated in a powerful
manner, rather like a sophisticated string array. TStringList objects are
used internally by TListBox, TComboBox, TTabControl etc. to maintain their
list of items. Strings start at index 0. TStringList objects grow and shrink
automatically to store all the strings.

Properties:

        Count                   - The number of strings in the list

Methods:

        GetString(i)            - Gets the string at index i
        SetString i text        - Sets the string at index i to text
        Add(text)               - Adds a string, returns the index
        Delete i                - Deletes the string at index i, moves the others up by one
        Clear                   - Clears the string list
        Exchange i j            - Exchanges the string at position i with the string at position j
        SaveToFile file         - Saves the string list to file
        LoadFromFile file       - Loads the string list from file
        SaveToSet()             - Converts the string list to a set (e.g. [one,two,three]) and returns the set
        LoadFromSet set         - Clears the string list and adds each element in the set to the string list
        SaveToAlias alias       - Overwrites alias's script code with the contents of the string list (alias MUST ALREADY EXIST!!)
        LoadFromAlias alias     - Clears the string list and loads the script code from alias into the list
        SaveToEvent event       - Overwrites event's script code with the contents of the string list (event MUST ALREADY EXIST!!)
        LoadFromEvent event     - Clears the string list and loads the script code from event into the list
        GetDirList mask         - Clears the string list and fills it with a list of directories whose names match mask. For example, mask could be set to c:\windows\*.* to return a list of directories in c:\windows, or it could simply be left as *.*.
        GetFileList mask        - Clears the string list and fills it with a list of files whose names match mask. See above (the GetDirList method) for more information.
        Execute                 - Executes the strings in the list as ViRCScript code
        Sort                    - Alphabetically sorts the strings in the string list

Note that the Sort method DOES NOT WORK for TStringList objects that are
properties of other controls (e.g. a TListBox's Items property). For these,
call the Sort method of the object (e.g. ListBox1.Sort), rather than of the
TStringList (e.g. do NOT do ListBox1.Items.Sort).

TBitmap (in-memory bitmap)
--------------------------

The TBitmap object represents a bitmap. The object has only one method.

Methods:

        LoadFromFile filename.bmp   - Reads the BMP file specified and loads it
                                      into the TBitmap object.

After a bitmap has been loaded into a TBitmap object, the object can be
assigned to a TBitBtn's Glyph property, or a TOfficeButton's Bitmap or
NoFocusBitmap properties.

TIcon (in-memory icon)
----------------------

The TIcon object represents a window icon. The object has only one method.

Methods:

        LoadFromFile filename.ico   - Reads the ICO file specified and loads it
                                      into the TIcon object.

After an icon has been loaded into a TIcon object, the object can be
assigned to a TForm's Icon property to make that icon the form's icon.

TSockets (Winsock socket control)
---------------------------------

ViRC '96 0.82 and above support a TSockets class, whose interface is similar
to the freeware Sockv3 Delphi control. Anyone who has used this will feel
instantly familiar!!

In versions before 0.91b, TSockets objects required an owner, which basically
meant that a TSockets object could only be made with a parent form. Now, this
is no longer true, and TSockets objects can be created freely without owners
(you can now do $new(TSockets), for example).

Properties:

        IPAddr          - The host name or IP address of where to connect to
        Port            - The port to connect to or listen on
        LocalHost       - The host name of the local machine
        LocalIPAddr     - The IP address of the local machine
        RemoteIPAddr    - The IP address of the remote machine the socket is
                          connected to, or about to accept an incoming
                          connection from
        SocketNumber    - Returns the socket number of the current connection.
                          If the socket is used to handle multiple incoming
                          connections, you should store the value of
                          SocketNumber for each connection in a TStringList in
                          the OnSessionAvailable event, and switch to the
                          required connection with the SetActiveConnection
                          method.

Methods:

        SConnect                - Connects to IPAddr:Port
        SListen                 - Listens for connections on Port
        SListenOnFreePort()     - Finds an unused port and listens on it.
                                  Returns the port that has been selected for
                                  listening on.
        SAccept()               - Accepts an incoming connection. Returns the
                                  unique socket number of the connection. Keep
                                  this value if you're handling multiple
                                  connections on one port, otherwise discard it
                                  by calling this method as a statement.
        SCancelListen           - Cancels a previous SListen command
        Send text               - Sends text to the remote end
        SendCRLF text           - Sends text plus a CRLF to the remote end
        SetActiveConnection(n)  - When the socket is used to handle multiple
                                  simultaneous incoming connections (for
                                  example, for a chat server to handle multiple
                                  users on the same port), this method will set
                                  the active connection to n. Any further
                                  socket operation, such as Send, will work on
                                  that connection.

Events:

        OnSessionConnected      - Called after SConnect when session connects
        OnSessionAvailable      - Called after SListen when an incoming connection arrives
        OnSessionClosed         - Called when the remote end closes the connection
        OnDataAvailable         - Called when data, sent by the remote end, is available for reception
        OnErrorOccurred         - Called when a socket error occurs
                                   $Msg = socket error message, $Error = socket
                                   error number

Connecting somewhere
--------------------

To connect somewhere, first create the socket, ownedby a form (this is VERY
important, the socket WILL NOT WORK if it is not owned by anything). Then set
the IPAddr property to the name or IP of the host you wish to connect to, and
set Port to the port. Then call the SConnect method. Example:

        @ $socketform = $new(TForm)
        @ $socket = $new(TSockets ownedby $socketform)

        @p $socket.IPAddr = post.demon.co.uk
        @p $socket.Port = 25
        $socket.SConnect

Listening for connections
-------------------------

Virtually identical to the above, except that IPAddr isn't specified and you
call SListen instead of SConnect. For example, to listen for incoming
connections on port 1234, do:

        @ $socketform = $new(TForm)
        @ $socket = $new(TSockets ownedby $socketform)

        @p $socket.Port = 1234
        $socket.SListen

To listen on a free port, assigning the port number selected to $sockport:

        @ $sockport = $socket.SListenOnFreePort()

If you don't care what port the socket has chosen for listening on, you can
call it, like any method, as a statement:

        $socket.SListenOnFreePort

The port chosen can then be retrieved later by reading the Port property.

Accepting incoming connections
------------------------------

When you are listening and an incoming connection comes in, the
OnSessionAvailable event is fired. You can then use the SAccept method to
accept the connection. Example:

        @p $socket.OnSessionAvailable = SOCKET_INCOMING

        Alias SOCKET_INCOMING
          $socket.SAccept
        EndAlias

When the remote end connects
----------------------------

After you have issued the SConnect call, V97 tries to connect with the remote
host. When a connection is established, the OnSessionConnected event is fired.
You must not send data to the socket before this event is fired!! Example:

        @p $socket.OnSessionConnected = SOCKET_CONNECTED

        Alias SOCKET_CONNECTED
          TextOut > . clBlue *** We're connected!!
        EndAlias

Socket errors
-------------

If the socket fails, either while attempting to connect or during data transfer
and so forth, the OnErrorOccurred event is fired:

        @p $socket.OnErrorOccurred = SOCKET_ERROR

        Alias SOCKET_ERROR
          TextOut > . clRed *** Error occurred while using socket!!
        EndAlias

In your SOCKET_ERROR handler, the local variables $Msg, containing the socket
error message, and $Error, containing the socket error number, are available.

Note that "Connection reset by peer" (which occurs when a client closes a
connection with a server) is treated as an error. By default, all errors will
shut V97 down (I will endeavour to change this behaviour in future releases).
Therefore it is STRONGLY recommended that, at the least (if you don't want to
do any actual error-handling), you have the following statement in:

        @p $socket.OnErrorOccurred = Nop

NOP simply does nothing - this ensures that your application will continue
whenever a socket error occurs.

In an OnErrorOccurred handler, you may want to call SClose to close the socket,
otherwise it will remain open, even though an error had occurred.

Sending data
------------

Data is sent with the SEND or SENDCRLF methods, which are identical except for
that fact that SENDCRLF sends a CRLF at the end of the line, whereas SEND does
not. Usually, you will use SENDCRLF. Example, which sends the string "Hello!!"
after connecting to the host:

        @p $socket.OnSessionConnected = SOCKET_CONNECTED

        Alias SOCKET_CONNECTED
          $socket.SendCRLF Hello!!
        EndAlias

Receiving data
--------------

When data is received on the socket, the OnDataAvailable event is fired. You
can receive the data by reading the socket's Text property. Note that reading
the Text property REMOVES THE DATA FROM THE SOCKET'S QUEUE. RETRIEVING ITS
VALUE AGAIN WILL CAUSE AN ERROR!! Therefore, assign the value of the Text
property to a variable at the beginning of the event code, and only reference
that variable. Example, which sends "Good morning!!" back whenever "Hello!!"
is received:

        @p $socket.OnDataAvailable = SOCKET_DATA

        Alias SOCKET_DATA
          @ $data = $prop($socket.Text)
          if ([$data] == [Hello!!])
             $socket.SendCRLF Good morning!!
          endif
        EndAlias

Closing the connection
----------------------

The socket connection can be closed with the SClose method, for example, this
closes the connection when QUIT is received:

        @p $socket.OnDataAvailable = SOCKET_DATA

        Alias SOCKET_DATA
          @ $data = $prop($socket.Text)
          if ([$data] == [QUIT])
             $socket.SClose
          endif
        EndAlias

When the REMOTE END closes the connection, the OnSessionClosed event is fired.
You must close the local end of the socket with SClose when this event is
fired. You could do something like this:

        @p $socket.OnSessionClosed = $socket.SClose

Similarly, in an OnErrorOccurred handler, you may want to call SClose to close
the socket, otherwise it will remain open, even though an error had occurred.

Cancelling listening
--------------------

If you're listening on a port and wish to cancel listening, call the
SCancelListen method. This listens on port 1234 and cancels listening when
an incoming connection appears:

        @p $socket.Port = 1234
        @p $socket.OnSessionAvailable = SOCKET_AVAILABLE
        $socket.SListen

        Alias SOCKET_AVAILABLE
          $socket.SAccept
          TextOut > . clBlue *** We're connected!!
          $socket.SCancelListen
        EndAlias

Handling multiple incoming connections on the same port
-------------------------------------------------------

In 0.91a and above, one single TSockets object can be used to handle a number
of simultaneous incoming connections on the same port. To use the TSockets
object in this way, just make it listen as normal, but in the
OnSessionAvailable event, call SAccept, but rather than calling it as a
statement, call it as a function and keep the return value somewhere (like a
TStringList). The value returned by SAccept is a socket number that's unique
to the current connection. If you later want to send data, for example, to
this connection, you should use the SetActiveConnection method to set the
active connection to an entry in your TStringList object. Example:

        @ $connections = $new(TStringList)
        @ $socket = $new(TSockets)
        @p $socket.OnSessionAvailable = SOCKET_AVAILABLE

          etc.

        Alias SOCKET_AVAILABLE
          $connections.Add $socket.SAccept()
          etc.
        EndAlias

          etc.

        Alias SEND_TO_FIRST_CONNECTION
          $socket.SetActiveConnection $connections.GetString(1)
          $socket.Send $1-
        EndAlias


TTimer
------

Timer creation is very simple, just use a TTimer object. First create the
object, set the Interval property for the time interval, set the OnTimer event
to the code you want executed every Interval milliseconds, and set Enabled to
True to turn the timer on.

Properties:

        Enabled         - (True or False) Controls whether the timer is enabled or not
        Interval        - The interval in milliseconds between the firing of the OnTimer event

Events:

        OnTimer         - This event is fired when the timer goes off (every Interval milliseconds)

Example:

        @ $timer = $new(TTimer)
        @p $timer.Interval = 1000
        @p $timer.OnTimer = BEEP
        @p $timer.Enabled = True

This will beep every second. How unspeakably annoying!!
