SlickEdit Slick-C - Macro - Programming - Guide - Book
SlickEdit Slick-C - Macro - Programming - Guide - Book
SlickEdit Slick-C - Macro - Programming - Guide - Book
Guide
SlickEdit® Inc.
3000 Aerial Center Parkway, Suite 120
Morrisville, NC 27560 USA
www.slickedit.com
Information in this document is subject to change without notice and does not represent a
commitment on the part of SlickEdit Inc. The software described in this document is
protected by U.S. and international copyright laws and by other applicable laws, and may be
used or copied only in accordance with the terms of the license or nondisclosure agreement
that accompanies the software. It is against the law to copy the software on any medium
except as specifically allowed in the license or nondisclosure agreement. The licensee may
make one copy of the software for backup purposes. No part of this manual may be
reproduced or transmitted in any form or by any means, electronic or mechanical, including
photocopying, recording, or information storage and retrieval systems, for any purpose other
than the licensee's personal use, without the express written permission of SlickEdit Inc.
SlickEdit, Visual SlickEdit and Clipboard Inheritance are registered trademarks of SlickEdit
Inc. Context Tagging, DIFFzilla, Dynamic Difference Editing and SmartPaste are trademarks
of SlickEdit Inc. All other trademarks are the property of their respective owners. Protected
by U.S. Patent 5,710,926.
ISBN 0-9716563-8-X
Contents
Contents
Contents.............................................................................................................................. 1
Introduction ....................................................................................................................... 1
About This Guide ......................................................................................................... 1
Intended Audience................................................................................................ 1
Key Conventions .................................................................................................. 1
Additional Visual SlickEdit Documentation ................................................................ 2
Contacting SlickEdit Inc. ............................................................................................ 2
Slick-C Overview ............................................................................................................... 3
Some History ................................................................................................................ 3
A Recurring Theme .............................................................................................. 3
Recognizing Problems in C++ ............................................................................. 3
Why Slick-C ......................................................................................................... 4
Work Still to be Done............................................................................................ 5
Container Variable Sample Code ......................................................................... 5
Getting Started by Example ............................................................................................. 8
A Key Bindable Command .......................................................................................... 8
An Event Driven Dialog Box....................................................................................... 9
A Batch Macro ........................................................................................................... 10
Compiling and Loading Macros..................................................................................... 12
Language Constructs....................................................................................................... 13
Comments .................................................................................................................. 13
String Literals............................................................................................................. 13
Numeric Constants ..................................................................................................... 14
Defining Constants..................................................................................................... 15
Mathematical Operators............................................................................................. 16
Assignment Operator ................................................................................................. 19
Scoping and Declaring Variables............................................................................... 19
Simple Variables ................................................................................................ 20
Details About Variable Initializations ................................................................ 21
struct ................................................................................................................... 21
Differences from C++................................................................................. 22
union ................................................................................................................... 23
Anonymous Unions ............................................................................................ 24
Arrays ................................................................................................................. 24
Differences from C++................................................................................. 25
Hash Tables ........................................................................................................ 25
Pointers Variables............................................................................................... 26
Differences from C++................................................................................. 27
Pointers to Functions .......................................................................................... 27
Differences from C++ ................................................................................. 28
Type Casting .............................................................................................................. 28
Differences from C++................................................................................. 29
i
Contents
ii
Contents
iii
Contents
iv
Introduction
Introduction
About This Guide
This Slick-C Macro Programming Guide describes the features of the Slick-C programming
language, developed by SlickEdit Inc. Slick-C will allow you to extend the Visual
®
SlickEdit code editor by writing macros. Visual SlickEdit is the most comprehensive and
flexible code editor you can use. This guide is a companion piece to several products: Visual
SlickEdit and Visual SlickEdit Plug-In for WebSphere Studio & Eclipse.
Intended Audience
This guide is intended for anyone who uses Visual SlickEdit and would like to extend the
Visual SlickEdit code editor to customize and extend it beyond the built-in customability
features.
NOTE: Any item in the guide or online help that refers to UNIX also applies to Linux.
Key Conventions
Commands are specified in the following format: ("Search", "Find"). This means you
activate the menu bar (press ALT), select the "Search" menu bar word, and select the "Find"
menu item. The following keying conventions are used throughout this guide:
Click a button on the screen Button shown in quotation marks Click “Save”
Click left or right mouse but- L” or “R”precedes the word “button” Click Rbutton
tons
• Visual SlickEdit
- Visual SlickEdit User Guide (PDF and hardcopy manual)
- Visual SlickEdit Installation Guide (PDF and hardcopy manual)
- Slick-C Macro Programming Guide (PDF and hardcopy manual)
- Individual Quick Reference Sheets (PDFs) for the following emulations:
Brief, CodeWarrior, CUA, Epsilon, GNU Emacs, ISPF, SlickEdit (Text
Mode), VI, and Visual C++
Sales: 800.934.3348
+1 919.473.0070
sales@slickedit.com
Fax +1 919.473.0080
* NOTE: A current Maintenance and Support contract is required for telephone support
past the 30-day warranty period.
Slick-C Overview
Some History
Why did Microsoft decide to use the BASIC language as the extension language for products
such as Word and Excel? Why is Hypercard based on an English-like grammar with
container variables? Why did database companies develop their own languages instead of
using C++?
The answer is simple. The C and C++ languages are incomplete, difficult to use, and allow
developers to create unintended consequences too easily. While software products have all
become easier to use, why hasn't C++, the most popular object-oriented language, followed
suit?
When considering the alternatives for extending a graphical version of Visual SlickEdit, it
was very clear that DLLs written in C/C++ should not be the primary means. Extending the
editor had to be easier to use, safe, and preferably more portable. This was the reason
SlickEdit created the Slick-C language.
A Recurring Theme
The easier to use programming languages always seem to add one or more of the features
below to overcome problems in C++:
To date some of the valiant attempts include Java, Delphi, Visual Basic, and Tcl/Tk.
Why Slick-C
Slick-C has addressed a number of important problems in C++. The strongest features in
Slick-C are listed below:
• Language syntax is C++ with few major deviations. Unlike Java, you can pass
any type by reference or value as you would expect in C++.
• Many built-in language and compiler/linker features for building graphical user
interfaces. Language supports graphical resources such as dialog box
templates, menu templates, and bitmaps. A control type is built-in so that
compiler/linker detect references to controls which don't exist on a dialog box.
• Typed and untyped variables. A major problem with existing highly typed
object oriented languages such as C++ and Java is that they can't provide users
with a good container type variable. This makes these languages unsuitable for
simple editor macros, database applications, or shell scripts which don't need
the speed or rigidness of a highly typed language. How many times have you
heard someone first learning C++ say that they want to define a container class?
Then they find out it can't be done in any useful manner. Slick-C is highly
typed, but also provides a very powerful container type. See container variable
code samples below.
• Better built-in types. Arrays in Slick-C dynamically grow without requiring the
user to allocate memory. Hash tables (sometimes called associative arrays)
dynamically grow and can be pre-initialized similar to an array. A hash table is
an array which can be indexed with a string. One array or hash table can be
assigned to another as long as the types are compatible. The string type is much
more integrated into the language. String constants are converted to numeric
types and boolean (1 or 0) types. The switch statement supports string
expressions. There are separate string operators so you can determine whether
a string or numeric operation will be performed without knowing the variable
types.
• C++ style classes. Like Java, classes in Slick-C will not require a delete to free
objects.
• Speed and size improvements for typed variables. Currently Slick-C has been
optimized for typeless container variables and strings. String concatenation is 5
times faster than Java. Integer arithmetic is 3 times slower than Java. Floating
point performance is much worse but can easily be optimized. Some of the
poor performance is due to the fact that Slick-C does 32 DIGIT integer and
floating point arithmetic. 64 bit longs and smaller floating point numbers
would really be big enough.
• instanceof operator like Java. This primitive is very important because it can
tell if a variable is an instance of a particular class which may be in the
inheritance chain. Currently, Slick-C has a _varformat() method, but it can't be
enhanced to traverse an inheritance chain.
• C++ code generation. That is, the ability to convert Slick-C code into source
code which can be compiled by any C++ compiler.
Here is how to define a set of stack routines in Slick-C which support any type of element.
stack[++stack[0]]=value;
}
typeless stackpop(typeless &stack)
{
if (stack[0]<=0) return('');
// Make a copy of the element.
result=stack[stack[0]--];
// Free space allocated by value and delete array element. _deleteel is a
// built-in method which operates on arrays and hash tables.
stack._deleteel(stack[0]+1);
return(result);
}
defmain()
{
// The above routines can handle variables of any type, including string
constants.
struct RECORD {
int i;
_str s;
};
// You can't make a limit on the number of elements in an array.
// We will add support for initially allocating a specific number of elements.
RECORD arecord[];
arecord[0].i=4;arecord[0].s="element 0";
RECORD symboltable:[]; // Declare a hash table/associative array
symboltable:["name1"].i=1;symboltable:["name1"].s="element 0";
stacknew(stack);
stackpush(stack,arecord); // Push an array onto the stack
stackpush(stack,symboltable); // Push a hash table/associative array onto the
same stack
stackpush(stack,"string"); // Push a string constant onto the same stack
}
This example illustrates how a container variable can access structure members as an array.
This cannot be implemented elegantly in any other language.
/*
Read lines of a file which contains tab delimited data into an
array of structures. Each line represents an array structure element.
The tab delimited data on each line represents fields in the structure.
We will assume the file contains valid data for filling this structure.
*/
int ReadTable(_str filename,typeless (&table)[])
{
// Use an editor buffer to open and cache the file. Data is read
// in blocks from the file only. We don't need this much power, but
// Slick-C needs a few more non-editor file I/O functions.
status=_open_temp_view(filename,temp_view_id,orig_view_id);
if (status) return(status);
top();up(); // Place cursor on line 0 before first line of file.
for (j=0;;++j) {
if (down()) break;
get_line(line);
if (line:=="") continue;
rest=line;
p= &table[j]; //Make p point to this structure element.
// Here we access structure members as an array of elements
p->[0]="";
Open a new file called "test.e" (invoke the File Open menu item and type "test.e") and insert
the following lines:
_command hello()
{
message("Hello World");
}
Invoke the load command (F12 or "Macro", "Load Module...") to compile and load your
macro. You have now defined a new command called hello which may be bound to a key or
invoked from the Visual SlickEdit command line. Press Esc to place the cursor on the
command line, type "hello" and press Enter. You should see the message "Hello World" in
the message area.
• Invoke the Bind Command to Key dialog box ("Tools", “Configuration”, "Key
Bindings...").
• Press Alt+5.
• Press Esc or click on the Quit button to close the dialog box. The editor will prompt
you to save the configuration when you exit. Newly loaded macro modules require
the state file to be saved.
Perform the following steps to create a simple event driven dialog box:
• Double click on the command button bitmap in the dialog editor Properties dialog
box (above the check box bitmap which is a square box with an X in it).
• Double click on the text box bitmap in the dialog editor Properties dialog box (the
one with the letters "ab" in it). If you can't see the dialog editor Properties dialog
box, invoke the Properties menu item ("Window", "Properties") to make it visible.
• Move the command button or the text box so they do not overlap. Click on the
object with the left mouse button, hold it, and drag to move the object.
• Double click on the command button that appears on the form (not the bitmap in the
dialog editor Properties dialog box). The Select An Event dialog box should appear
with "lbutton_up" displayed in the combo box. Press Enter to select the event.
• You should be prompted with an Open dialog box for a new file which is to contain
the source code for this dialog box. Type "form1.e" and press Enter.
NOTE: If at any point you can't see the dialog editor Properties dialog box, invoke the
Properties menu item ("Window", "Properties") to make it visible.
After the performing the above steps, you should see a file called "form1.e" with the
following lines of code:
#include "slick.sh"
defeventtab form1
ctlcommand1.lbutton_up()
{
If you don't see the above code, probably because you already have a "form1.e" file. If this is
the case, modify the "form1.e" file to contains the lines of code above.
Modify the above code to add the statement (ctltext1.p_text="Hello World";) as shown
below.
#include "slick.sh"
// Since this is the first event handler defined for this control
// and the name of this control does not match the last
// defined event table,
// the Slick-C translator automatically defines
// the event
// table form1.ctlcommand1 and defines the lbutton_up
// event handler within this
// new event table
ctlcommand1.lbutton_up()
{
// Set the p_text property of the text box control
ctltext1.p_text="Hello World";
}
Invoke the run_selected command ("Macro", "Load and Run Form") to load the code, load
the dialog box, and display the dialog box. Click the "ctlcommand1" button to display "Hello
World" in the text box. To close the Form1 dialog box, double click on the system menu (not
all UNIX window managers close windows when you double click on the system menu) or
press Ctrl+Shift+Space (in the running version of form1; not the edited copy). You can press
Ctrl+Shift+Space while any dialog box is running in order to edit it (including the Properties
dialog box).
You can display this dialog box from the command line by typing "show form1". To display
the dialog box modally enter "show -modal form1" on the command line. The show
command provides many powerful options. The editor will prompt you to save the
configuration when you exit. Dialog box templates and compiled macros are stored in the
state file ("vslick.sta" or "vslick.stu" under UNIX).
The example code below illustrates how to write a command which displays a dialog box.
This is useful for binding a command to a key which displays a dialog box.
#include "slick.sh"
_command run_form1()
{
// The -modal option disables other windows while the dialog box
// is displayed.
show("-modal form1");
}
A Batch Macro
Batch macros are useful for trying out Slick-C primitives and are easier to share between
multiple users. Batch macros may not be bound to a key; however, you can execute a batch
macro from the command line or a menu item.
Open a new file called "hello.e" (invoke the File Open menu item and type "hello.e") and
insert the following lines:
defmain()
{
message("Hello World");
}
You have now defined a new batch macro called hello which may be invoked from the Visual
SlickEdit command line. Save your macro and press Esc to place the cursor on the command
line. Type "hello" and press Enter. You should see the message "Hello World" in the
message area. Batch programs must be saved before they are executed in order for the macro
to get compiled. Visual SlickEdit automatically compiles batch programs if no
corresponding pcode file exists (hello.ex in this example), or the date of the source file is
newer than the pcode file.
A module which was loaded with the load command may be unloaded with the unload
command ("Macro", "Unload Module..."). However, Visual SlickEdit's symbol table (called
the names table) will still contain the names of globally scoped variables, procedures, and
commands until you save the configuration. The configuration is automatically saved when
you exit the editor. You can invoke the save_config command from the Visual SlickEdit
command line to save the configuration at any time.
Language Constructs
Comments
Slick-C supports both C++ comment styles. Two forward slashes (//) declares the rest of the
line to be a comment. The two characters '/*' open a comment and '*/' closes a comment.
Unlike C++, comments may be nested.
Example:
String Literals
Strings may be in single or double quotes. Double quoted strings are identical to C++. A
backslash followed by a character has special meaning as follows:
If single quotes are used, two single quotes consecutively represents one single quote
character. If double quotes are used, a backslash followed by a double quote represents one
double quote character. The operator ":==" used in the example below compares two strings
for exact equality. The Slick-C language does have a operator "==". However, this operator
will strip leading and trailing spaces and tabs from both operands (see “Mathematical
Operators” on page 16 for a list of operators).
Example:
"abc":=='abc'
"Can't find file":=='Can''t find file'
"\t":==_chr(9)
A backslash (not inside quotes) followed by a character or a number has the special meaning
as shown below. However, using this feature is not recommended because it is not C++
compliant. This features remains only for backward compatibility.
\a Bell character
\b Backspace character (8)
\f Form feed character(12)
\n New line
\r Carriage return
\t Tab
\v Visual character (11)
\xdd Hexadecimal character code dd
\ddd Decimal character code ddd
Quoted strings adjacent to escape sequences are read (lexed) as one string. No concatenation
is performed.
Example:
\n:==\10
\r:==\xd
\r:==\x0d
"\r\n":==\r\n
"1"\50 + "1"\49:== "23" // 12 + 11=="23", not 1:+(2+1):+1=="131"
Numeric Constants
The Slick-C language supports floating point numbers and hexadecimal numbers. The
mantissa is limited to 32 digits and the exponent is limited to 9 digits. When precision is lost,
the result is rounded. Overflow and underflow are detected. Floating point numbers have the
following syntax:
OR
[+|-] [.][digits][E[+|-]digits]
Hexadecimal numbers are defined in the same way as the C language, as follows:
0x hexdigits
Example:
4
" + 4e2"
4e2
" -4E-2"
-4E-2
0xff
Defining Constants
Slick-C has full support for #define textual replacements like C/C++. #define can be used
for defining constants or in-line functions.
Use a backslash at the end of a line to indicate that the value text continues to the next line.
Any occurrence of name is replaced with the text value before the source is compiled.
WARNING: When value represents a constant expression, you should place parentheses
around it to make sure there is not a problem with operator precedence.
Example:
#define MAXLINES15
#define MAXLINESP1 (MAXLINES+1)
#define max(a,b) ( ( (a) >= (b) ) ? (a) : (b) )
#define min(a,b) ( ( (a) <= (b) )\
? (a) : (b) )
defmain()
{
x=4;
y=7;
a=max(x,y);
}
Now that Slick-C has full support for #define replacement macros you should not use the
current const primitive. The Slick-C const primitive is more like Pascal than C++ and will
confuse C/C++ programmers.
The variable name is not prefixed with a type name, the expression must be a constant, and
the comma is optional. The syntax for declaring a constant is:
const
variable = constant_expression [',']
variable = constant_expression [',']
....
The comma is used to declare more than one constant on the same line. Constants may be
used in mathematical expressions.
Example:
const
LONG_CONST = "line 1" :+
"line 2" :+
"line 3"
DOS_INT = 0x21
Mathematical Operators
Slick-C uses the operator precedence of C. An expression can have the following unary
operators:
The binary and ternary operators for the Slick-C language are listed below from lowest to
highest precedence.
Operator Description
Operator Description
^= Bitwise XOR left operand with right operand and assign to left
operand.
&= Bitwise AND left operand with right operand and assign to left
operand.
e1?e2:e3 If expression e1 is TRUE (not the string '0'), expression e2 is
returned. Otherwise, expression e3 is returned.
&& Logical AND. If left hand expression is false, right hand expres-
sion is not evaluated.
|| Logical OR. If left hand expression is true, right hand expression
is not evaluated.
| Bitwise OR.
^ Bitwise XOR.
& Bitwise AND.
== Equal. Performs a numeric or string comparison depending on the
operands. This function is NOT identical to the C strcmp function
(see :== operator below).
NOTE: If both operands are numbers, a numeric comparison is per-
formed. Otherwise a string comparison is performed. In any case,
leading and trailing spaces and tabs are stripped before the com-
parison is performed.
> Greater than. Performs a numeric or string comparison depending
on the operands. See note under == operator.
>= Greater than or equal. Performs a numeric or string comparison
depending on the operands. See note under == operator.
< Less than. Performs a numeric or string comparison depending on
the operands. See note under == operator.
<= Less than or equal. Performs a numeric or string comparison
depending on the operands. See note under == operator.
!= Not equal. Performs a numeric or string comparison depending on
the operands. See note under == operator.
:== Exactly equal. Always performs string comparison. This is
equivalent to the C expression (strcmp(a,b)==0).
:!= Not exactly equal. Always performs string comparison.
:<= Exactly less than or equal. Always performs string comparison.
Operator Description
In addition to the operators above there is also implied concatenation. When there is no
binary operator between two unary expressions, concatenation is automatically performed.
All numeric operators, except bitwise operators, support floating point numbers. Bitwise
operators support 32 bit integers for all platforms. In the future, bitwise operators will
support 32 bit int and 64 bit long for all platforms. There are two sets of comparison
operators. The operators <, >, =, !=, <=, and >= perform a numeric comparison if both string
expressions are valid numbers. The operators :<, :>, :==, :!=, :<=, and :>= always perform a
string comparison. Select the appropriate comparison operator for performing a string or
numeric comparison.
NOTE: Expressions may extend across line boundaries if the line ends in a binary
operator or if the line ends with a backslash.
Example:
(1.0==1) == 1
(1e2==100) == 1
(1e2:==100) == 0
(" abc "=="abc") == 1
(" abc ":=="abc") == 0
(" abc ":!="abc") == 1
(" 1 "==1) == 1
(" 1 ":==1) == 0
("abc":<"def") == 1
1 2 :=="12"
1 ( 2) :=="12"
pow(4,2) ==16
5%2 ==1
5/2 ==2
5/2.0 ==2.5
5 intdiv 2.0 ==2
5&2 ==0
5|2 ==7
(10<7) ==0
(10:<7) ==1
Assignment Operator
The simple assignment statement has the syntax:
variable=expression;
Example:
i=1;
i=i+1;
Variables are declared just like in C++; however, there are the following differences:
• char, short, and float types are not yet available. All numeric types are signed.
The current Slick-C numeric types are int, long, and double.
• Arrays, while declared similarly, cannot have a size limit. Arrays elements are
always dynamically allocated.
• Slick-C provides a :[] hash table operator which is similar to the array operator []
except that you index hash tables with a string type.
• There is no sizeof operator. Since the Slick-C interpreter currently stores all types
as container variables, the sizeof operator has no meaning.
• Only global or static variables can be initialized with the "=" operator.
Rather than show you the complex BNF syntax for declaring variables, we will break up
declarations into categories. If you don't already know the C language, a book on the C
language would help you learn more about Slick-C.
Simple Variables
“Simple variable” means a variable which is not an array, hash table, structure, or function
pointer.
boolean Use this for variables which only take on the value 0 or 1 and are
typically used with the boolean operators &&, ||, and !. You can
also use the new keywords "true" and "false".
int 32 bit signed integer.
long 32 DIGIT signed integer. In the future this will be a 64 bit
signed integer. Same as older keyword bigint. Currently this
type supports a 32 DIGIT number.
double 32 DIGIT mantissa and 32 bit signed exponent. Same as older
keyword bigfloat.
_str String. Same as older keyword bigstring.
typeless Container variable. You can assign a typeless variable anything.
Even an array of “whatever”, or hash table of “whatever”.
StructName See "struct" below.
The comma is used to declare more than one variable of the same type. Local variables do
NOT have to be defined. Using a variable not already defined as global or constant declares
the variable to be a local typeless variable. However, you may declare variables within the
scope of a function to ensure that the variable will be local even if the name has been
declared elsewhere as a global or constant.
Examples:
defmain()
{
_str s; // Declare a local string variable
t=gi; // Copy gi into local container variable t.
message("t="t);
}
Global and static variables declared as typeless or _str are initialized with "" (a zero length
string) when no user-specified initialization value is given.
Examples:
boolean globalboolean=true;
int globalint;
double globaldouble;
defmain()
{
// will see "globalboolean=1 globalint=0 globaldouble=0"
message("boolean="globalboolean" "globalint" "globaldouble);
}
struct
Structures are typically used to logically group data. For example, a record in a database
might have a name, address, and phone number. This can be logically grouped into a
structure to make easier for you to remember what data needs to be filled in for a complete
record. Structures also can have the added effect of reducing the number of global variables.
There is no sizeof operator like in C++. Since the Slick-C interpreter currently stores all
types as container variables, the sizeof operator has no meaning.
The struct declaration gives you the choice of defining your own type called StructName
and/or to declare one or more variables. The syntax of member-variable-decls is identical to
declaring other variables except that static structure members are not supported.
variable.member-name
• Space for structure elements is allocated when you access the element.
• Structure data is not continuous. This is obvious for string, array, and hash table
member variables which contain variable size data. However, even other types are
sometimes stored elsewhere. In the future, after a few changes are made, the way
Slick-C lays out memory will be documented.
• There is not a sizeof function which tells you the size of a structure in bytes.
Examples:
defmain()
{
messageNwait("Name="gPR.Name" PhoneNumber="gPR.PhoneNumber);
t=gPR; // Copy phone record data into a local container variable
//Container variables can access structure elements as an array.
messageNwait("Name="t[0]" PhoneNumber="t[1]);
}
union
Unions are typically used in place of a struct in the case where you have mutually exclusive
member variables. In this case, a union will require less memory than a struct. Memory is
only allocated for one member variable at a time.
The union declaration gives you the choice of defining your own type called UnionName
and/or to declare one or more variables. The syntax of member-variable-decls is identical to
declaring other variables except that static union members are not supported.
variable.member-name
Examples:
union {
int i;
_str s;
double d;
}gu={1}; // Type checking here is with first member variable.
#define KIND_INT 1
#define KIND_STRING 2
#define KIND_DOUBLE 3
defmain()
{
struct {
int kind;
// Here we are nesting a union inside a struct.
// This union only requires space for one of these
// members at a time.
union {
int i;
_str s;
double d;
}u;
} x;
x.kind=KIND_INT;x.u.i=1;
...
switch (x.kind) {
case KIND_INT:
messageNwait("x.u.i="x.u.i);
break;
case KIND_STRING:
messageNwait("x.u.s="x.u.s);
break;
case KIND_DOUBLE:
messageNwait("x.u.d="x.u.d);
break;
}
}
Anonymous Unions
An anonymous union is a union member variable which has no name. This saves you from
having to type the union member variable name.
Example:
defmain()
{
struct {
int kind;
union {
int i;
_str s;
double d;
}; // No name for this union member variable
} x;
x.kind=1;x.i=1;
}
Arrays
Array variables are excellent for keeping track of a list of items. If you want to prompt a user
to select from an array of strings, use the _sellist_form (see online help).
The first element of an array starts at 0. Use more than one set of [] for multi-dimensional
arrays. Notice that you do not define the maximum number of elements in the array, because
array elements are allocated when you access them. The maximum number of elements that
may be placed in an array is about 2 billion. Your machine will likely run out of memory
before this limit is ever reached.
Use the _length() method to determine the number of elements in an array. The syntax for
using this method is variable._length().
• Space for array elements is allocated when you index into the array.
• You cannot limit the number of elements that the array may contain.
• Specifying an array variable WITHOUT the [] operator does not return a pointer to
the first element. Instead it refers to the entire array. This allows you to copy one
array to another or define a function which returns a copy of an array.
• There is not a sizeof function which tells you the size of the array in bytes. There is
a _length method which tells you the number of elements in the array.
Examples:
intgai[]={1, 7, 12};
intgaai[][]={{1},{1,2},{1,2,3}}; //two dimensional array
_strgastring1[]={"Value1", "Value2"};
typelessgat[]={"String", 1, 2.4};
defmain()
{
t=gai; // Copy all the array elements into a local container
variable
t[t._length()]=45; // Add another array element.
for (i=0;i<t._length();++i ) {
messageNwait("t["i"]="t[i]);
}
#if 0
// This C style pointer loop is not supported.
p= &array[0];pend= &array[2];
for (;p<pend;++p){ // If p and pend were declared as pointers,
} // compiler would not allow < or ++.
#endif
}
Hash Tables
Slick-C provides a :[] hash table operator which is similar to the array operator [] except that
you index hash tables with a string type. The syntax for defining a hash table variable is:
Examples:
#include "slick.sh"
int ghi:[]={"Symbol1"=>1,"Symbol2"=>2,"Symbol3"=>3};
int ghai:[][]={"Symbol1"=>{1},"Symbol2"=>{1,2},"Symbol3"=>{1,2,3}};
_str ghstring:[]={"Symbol1"=>"Value1", "Symbol1"=>"Value2"};
defmain()
{
messageNwait("ghi:['Symbol1']="ghi:["Symbol1"]);
// Copy all the hash table elements into a local container variable
t=ghi;
t:["Symbol4"]=4; // Add another hash table element.
// The loop below illustrates how you can traverse the elements
// in a hash table without knowing the string indexes.
typeless i;
boolean doModify;
doModify=false;
for (i._makeempty();;) {
t._nextel(i);
if (i._isempty()) break;
if (doModify) {
t._el(i)=4; // Modify element
} else {
messageNwait("i="i" value="t._el(i));
}
}
}
Pointers Variables
Pointer variables in C++ are usually considered to be a bad thing. Due to limits in the C++
language, pointers sometimes get over used. However, pointers are what make C++ a
"Systems" programming language. You can write your own memory management routines,
interface with hardware, and get that extra ounce of speed. Currently you cannot use pointers
in Slick-C to perform these types of low level functions. Unlike C++, Slick-C pointers
variables do not support arithmetic operations such as ‘<’, ‘>’, ’+’, and ‘-‘. Only the
operators ‘==’ , ’!=’, and ‘!’ are allowed. Pointers were added to Slick-C because we felt that
there are a few programming tasks which are more easily solved by pointers. If you access
an invalid pointer, Visual SlickEdit will not crash; however, your Slick-C macro will stop
running.
WARNING: When a module is reloaded, static variable addresses change. Make sure
you reinitialize global pointer variables which point to static (module scope) variables.
The unary & operator is used to return the address of a variable. The unary * operator is used
to pick up a pointer. However, you need to use the operator -> (ex. p->member-variable) to
access members of a pointer to a structure. See example below.
• Space for array elements is allocated when you index into the array.
• You can't limit the number of elements that the array may contain.
• There is not a sizeof function which tells you the size of the array in bytes. There is
a _length method which tells you the number of elements in the array.
Example:
intgi=1;
int*gpi=&gi;
defmain()
{
// Print out what gpi pointers to.
messageNwait("*gpi="(*gpi));
*gpi=2; // Change value of gi
messageNwait("get 2 here gi="gi);
struct {
int x,y;
} st,*pst;
st.x=1;st.y=2;
pst=&st;
messageNwait("pst->x="pst->x" pst->y="pst->y);
// This works too, but using -> is easier
messageNwait("(*pst).x="(*pst).x" (*pst).y="(*pst).y);
}
Pointers to Functions
Function pointer variables are useful for arguments to sort functions or for lookup tables
which contain a particular function to call. If you access an invalid function pointer, Visual
SlickEdit will not crash;. however, your Slick-C macro will stop running.
WARNING: When a module is reloaded, static function addresses change. Make sure
you reinitialize global function pointer variables which point to static (module scope)
functions.
Where ArgDecl has the almost the same syntax as a variable declarations except static is not
supported and the "&" operator is used to specify call by reference parameters. Call by
reference array and hash table parameters require parentheses around the & and id. See
“Argument Declarations” on page 34.
(*pfn)([e1, e2,...])
Only one syntax is currently supported for making a call with a pointer to a function variable.
The pfn(p1,p2,...) syntax is not supported. This limitation is necessary for container variables
because the compiler does not know the type of the variable. However, support will be added
for the other syntax for typed pointers to functions.
Examples:
void proc(_str a)
{
messageNwait("a="a);
}
defmain()
{
// Assign a pointer to a function to the container variable "t"
t=proc;
(*t)("First call");
// Declare a local pointer to function variable
void (*gpfn)(_str a);
gpfn=proc;
(*gpfn)("Second call");
}
Type Casting
Slick-C enforces strict type checking on typed non-container variables. There are times
when you need to convert one type to another to prevent the compiler from “complaining”.
Type casting helps tell the compiler you know what you are doing. In addition, type casting
can change the value of an expression.
(VarDeclWithoutName) expression
Some casts are not allowed because they would require unrealistic assumptions by the
compiler. For example, you can't cast a struct type to another struct type.
• Currently Slick-C only supports the less ambiguous C style type casting. Support
for the new C++ type casting syntax TypeName(variable) may be added in the
future. The argument is between having an elegant less ambiguous grammar or
conforming to C++ so there is nothing new to learn.
• Because Slick-C does not allow low level manipulation of memory, you can't do
things like type cast an "int *" to a "long *".
Examples:
defmain()
{
int i;
double d;
d=1.2;
i=(int)d; // i gets the value 1, NOT 1.2
typeless t;
t=1.2;
i=t; // Here i gets 1.2 BUT
// IN THE FUTURE this will NOT be the case because it
// will be physically impossible to store a float in a 32 bit int.
boolean b;
b= i!=0; // Can't use cast here.
i=(int)b; // Need cast here.
}
The container variable can store the contents of ANY typed variable. This is easy for the
interpreter since all typed variables are stored as container variables. At run-time, the
interpreter must check the current type of the container variable (and sometimes convert it) to
perform an operation.
Examples:
typeless t;
struct {
int x;
int y;
} st;
st.x=1;st.y=2;
t=st;
// Print out the elements of the structure
for (i=0;i<t._length();++i ) {
messageNwait("t["i"]="t[i]);
}
The IF Statement
The syntax for an if statement is:
if (expression ) statement
[else statement]
statement may be a C style statement block which contains multiple statements. See
“Statement Block” on page 31 for more information.
IMPORTANT: The value 0 for all types is false. All other values are true. Like C++,
Slick-C uses the value 0 for null pointers. For the string type, only a one byte length
string where the first character is an ASCII '0' is false. A 0 length string ("") is true when
used in a boolean expression.
Examples:
if (x<y) a=1;
if (x="a") {
y=1;
} else if (x="b") {
y=2;
} else if (x="c") {
y=3;
} else if (x="d") {
y=4;
}
Statement Block
A statement block is typically used to allow multiple statements within an if or loop
construct. However, it can also be used to declare a new local scope block. Syntax:
{
statement
statement
...
}
Example:
if (i<1) {
int x=1;
{
int x; // Can do the assignment here
x=3;
}
// The variable x will be 1 here and not 3.
}
Loops
The syntax for valid Slick-C language loops are:
The while loop evaluates condition_exp first and then executes statement if condition_exp is
true (not the value 0), the statement will continue to be executed until condition_exp
becomes false (0) or a break statement is reached.
The C-style for loop is somewhat free form. The expressions before the first semicolon of
the for loop are executed before entering the loop. The condition_exp expression is checked
before entering the for loop as well. If condition_exp is true (not the value 0), the statement
is executed. The statement will continue to be executed until condition_exp becomes false
(0) or a break statement is reached. When the bottom of the for loop is reached, but before
condition_exp is checked again, the expressions after the second semicolon are executed.
The do loop executes statement first and then evaluates condition_exp. If expression is true
(not the value 0). The statement will continue to be executed until expression becomes false
(0) or a break statement is reached.
Loops may be exited with the break primitive. The continue primitive may be used to skip
to the top of a loop. Both of these primitives support an optional label argument (like Java).
If specified, the label must match the label of one of the loops you are currently in.
Using continue on a for loop causes the expressions after the second semicolon to be
executed before condition-_exp is checked. When continue is used on a do statement the
condition_exp is not checked and execution resumes at the top of the loop.
Example:
outerloop:
for(i=1;i<3;++i){
for (j=1;;++j) {
if (j==3) break outerloop; //Exit both loops
messageNwait("i="i);
}
}
for(i=1;i<10;++i){
messageNwait("i="i);
}
// The above for loop is equivalent to the loop below.
i=1;
for (;i<10;) {
messageNwait("i="i);
++i;
}
for(i=1;i<10;++i) messageNwait("i="i);
i=1;
while (i <10) ++i;
i=1;
do {
messageNwait("i="i);
} while (i<10);
Switch Statement
Slick-C supports the C switch statement. The Slick-C switch supports integers and string
types. The syntax of the switch statement is as follows:
switch (expression) {
[ case expression:
statements
]
[ case expression:
statements
]
...
[ default:
statements
]
}
The switch expression is evaluated and compared against all the case expressions. Once a
match is found, ALL statements below the case are executed, including those statements
found in the next case and the default, until a break statement is reached.
Example:
outerloop:
for (i=1;;++i) {
switch (i) {
case 1:
case 2:
messageNwait("i=1 or i=2");
break; // Done with these cases
case 3:
break outerloop;
}
}
Functions
We use the term “function” to mean something that can be called in some manner from the
macro language. Slick-C has three kinds of functions: procedures, commands, and built-ins.
The sections that follow describe each of these, and their differences.
Defining a Procedure
There are several differences between defining a procedure and defining a command with the
_command primitive. Unlike command functions, the scope of a procedure may be limited
to a module. Command functions may be invoked by typing the name on the Visual
SlickEdit command line, from a menu item definition, by using the execute function, or by
typing the name followed by arguments in parentheses in a Slick-C expression. Procedures
may only be called by the latter method and may not be bound to keys. A procedure name
must be a valid Slick-C identifier (same as C identifier). However, the name of a command
may be a keyword (not recommended), or a string constant containg a single character such
as "/" (Visual SlickEdit uses this to define a search command).
Argument Declarations
The syntax for ArgDecls is the same as for declaring a variable except that the static keyword
may not be used. In addition, an & before the id declares a call by references parameter. Call
by reference array and hash table parameters require parentheses around the & and id. See
example below.
The last argument in the declaration list may be an ellipsis ('...' 3 dots) to indicate that the
function accepts more arguments of any type. Use the arg function to access these optional
arguments.
TypeName specifies the return type of the function. See “Where TypeName may be any of
the following:” on page 20. If TypeName or void is not specified, return statement checking
is relaxed. When the void type is used, a value cannot be specified to the return statement.
The return statement is used to specify the result of the function call and exit the function.
The optional static key word is used to limit the scope of a procedure to the module it is
defined in. By default, procedures are global and may be accessed by any module.
Procedures are called by specifying the name followed by comma delimited arguments, if
any, in parentheses.
Example:
// NOTE: The list, p1, and pfn parameters are call by reference
// parameters. Like C++, the list parameter requires parentheses
// around the & reference operator and the name because the []
// operator would otherwise be
// processed first. This avoids
// deviating much from C++ syntax.
//
// pfn is a reference to a pointer to a function.
boolean proc(int &p1,_str p2,_str (&list)[],int (*&pfn)(int))
{
return(true);
}
Default Arguments
The assignment operator has special meaning in an argument declaration. It defines a default
value
for an argument. The default value is used if the caller does not specify the parameter.
Default arguments should always be specified in the function definition but not in the
prototype. Unlike C++, default arguments in prototypes have no effect on the compiled code.
Example:
Procedures may have up to 15 arguments defined. The procedure may be called with more
arguments than defined by the procedure declaration. These "extra" arguments as well as the
arguments defined in the procedure declaration can be retrieved by the function arg.
Defining arguments with default values instead of using the arg function makes your code
more understandable. Calling the arg function with no parameters returns the number of
parameters the function was called with. The minimum number of arguments the procedure
may be called with is defined by the procedure heading.
The above syntax has the same meaning as the following new syntax:
The keyword var placed before the parameter declares the parameter to be a call by reference
parameter.
Defining a Command
The _command primitive is used to define a new command with argument completion.
Commands must be translated into pcode and loaded by the load command ("Macro", "Load
Module..."). A command may be invoked by typing its name on the Visual SlickEdit
command line, selecting it from a menu item definition, pressing a key, quoting its name in a
Slick-C function, or typing its name followed by arguments in parentheses in a Slick-C
expression. Command procedures always have the scope global and may be bound to a key
with the Bind Command to Key dialog box ("Tools", “Configuration”, "Key Bindings...").
TypeName specifies the return type of the command. See “Where TypeName may be any of
the following:” on page 20. If TypeName or void is not specified, return statement checking
is relaxed. When the void type is used, a value cannot be specified to the return statement.
The return statement is used to specify the result of the function call and exit the function. If
your command uses the arg function to access arguments, specify an ellipsis ("...") for the
last argument.
The syntax for ArgDecls is the same as for declaring a variable except that the static keyword
may not be used. In addition, an & before the id declares a call by references parameter. Call
by reference array and hash table parameters require parentheses around the & and id.
However, all typed or named arguments must have a default value.
The last argument in the declaration list may be an ellipsis ('...' 3 dots) to indicate that the
function accepts more arguments of any type. Use the arg function to access these optional
arguments.
The name of a command may be a valid Slick-C identifier, keyword (not recommended), or
string constant of length one such as "/" (Visual SlickEdit uses this to define a search
command).
Example:
Commands get unnamed command line arguments by calling the arg function. When a
command is invoked from the command line, the expression arg(1) contains the rest of the
command line after the name with leading spaces removed. For example, invoking the edit
command "e file1 file2" calls the e command with "file1 file2" in arg(1). The parse built-in
is an excellent function for parsing a command line string (see online help for more
information on parse). When another macro calls a command, more than one argument
string may be passed. Calling the arg function with no parameters returns the number of
parameters the command or procedure was called with.
name_info Attributes
The optional constant expression const_exp is used for argument completion, restricting
when the command can be executed, and a few other options.
The first argument in const_exp indicates the type of word arguments the command accepts
and is used for argument completion purposes. For a list of already defined argument types,
look in the file "slick.sh" for constants that end in "_ARG". const_exp may contain one or
more of the _ARG constants. Separate each _ARG constant with a space. A asterisk ('*')
character may be appended to the end of a completion constant to indicate that one or more of
the arguments may be entered.
The second argument (after the quoted comma) specifies when the command should be
enabled/disabled, and a few other things. One or more of the following flags may be
specified and ORed together with the bitwise OR ('|') operator:
Example:
#include "slick.sh"
// This command supports completion where the first argument
// is a filename and the second argument is an environment variable.
_command test1(...) name_info(FILE_ARG" "ENV_ARG)
{
parse arg(1) with file_name env_name;
message("file_name="file_name" env_name="env_name);
}
// This command is enabled only when the target is an editor control
// which has a selection.
_command void gui_enumerate()
name_info(','VSARG2_REQUIRES_EDITORCTL|VSARG2_REQUIRES_AB_SELECTION)
{
...
}
// This commmand supports completion on multiple filenames
_command e,edit(...)
name_info(FILE_ARG'*,'VSARG2_CMDLINE|VSARG2_REQUIRES_MDI)
{
...
}
The edit command allows any number of file name arguments to be given. When the user is
presented with a selection list of file names, many files may be selected with the Space Bar
key. If a asterisk ('*') is appended to the end of a completion constant, that command must
support a space delimited list of strings. Double quotes are placed around arguments with
embedded spaces.
Function Prototypes
Function prototypes are used to give the compiler type information about a function without
providing any code. Slick-C reduces the need for prototypes by performing some argument
checks at link time. When the linker finds an uninitialized variable error, it will recommend
that you add a function prototype to your source so the compiler can find your error. You
may need a function prototype if you want to use the function address in an expression.
Prototypes are not allowed for event functions.
The syntax for defining a function prototype is identical to defining a function except that a
semicolon (';') is placed after the closing parenthesis of the parameter list. Unlike C++,
default arguments in prototypes have no effect on the compiled code. No code or name_info
is given.
Example:
There are two ways to find the function you are looking for. First, you can use the menu item
("Help", "Macro Functions by Category...") which shows you smaller lists of these functions
by category. Second, you can look at source code for existing commands we have already
written which you think will call functions similar to ones you need. For example, let’s say
you want to write a macro which gets the word at the cursor and does something with it. Try
to think of a command in Visual SlickEdit which operates on the current word. The
upcase_word command (Ctrl+Shift+U) uppercases the word at the cursor. If you don't know
the name of the command but you do know the key which invokes the command, use the
what_is command ("Help", "What is Key...") to find out the name of the command that gets
executed. Now that you have the name of the command, use the find_proc command
("Macro", "Find Slick-C Proc...") to bring up the macro source code. If you executed the
command "find_proc upcase_word", you will quickly see a procedure called cur_word
which looks like what you want. Place your cursor on any part of the cur_word procedure
and press the "?" button on the button bar.
• Commands and built-ins always have the scope global. Procedures may have the
scope static (module) or global.
• Commands may be invoked from the command line or the execute function. Built-
ins and procedures may not.
• Built-ins that do not return a value do not require parentheses when called (this will
change in the future). For example, the left built-in may be called by with left or
left(). You should always use parentheses so you don't have to remember which
function requires parentheses.
• A command may be given the same name as a built-in. However, this limits how
the command may be called within a macro (use the execute function). None of the
commands have the same name as a built-in so you can call any command just like
any other function.
...
}
TypeName specifies the return type of the function. See “Where TypeName may be any of
the following:” on page 20. If TypeName or void is not specified, return statement checking
is relaxed. When the void type is used, a value cannot be specified to the return statement.
The return value of defmain is placed in the predefined rc global variable. Beware, the
execute function only supports returning an int type. You will need to check the global rc
variable for other types.
The arg function is used to retrieve the command line arguments passed to the defmain
procedure. ALL command line arguments will be in arg(1). Use the parse statement to
easily parse multiple space delimited arguments.
Example:
defmain()
{
messageNwait("Arguments given: "arg(1));
parse arg(1) with word1 word2 .;
messageNwait("word1="word1" word2="word2);
return(0)
}
The example above displays the arguments given to the macro on the Visual SlickEdit
message line. If you define a procedure in a batch program, use the static keyword when
possible to conserve memory. Visual SlickEdit stores the names of global procedures and
variables in a names table.
Extending the editor with a batch macro has the advantage of conserving memory and
reducing the size of the state file. In addition, batch macros can more easily be shared
between multiple users. The editor keeps the batch macro loaded only while it is executing.
External batch macro names and arguments are not supported by completion. To provide
completion, you must define a command with the _command primitive and have it call the
external batch program. If you name the command the same name as the batch program
(without the extension), use the xcom command to bypass internal command searching.
1. Enter the name of the module (extension not necessary) followed by arguments on
the Visual SlickEdit command line.
2. Enter "vs -p program" at the shell prompt, where program is the name of the batch
program and "vs" is the name of the editor. The "-p" option tells Visual SlickEdit to
terminate when the batch program completes. Alternatively, you may use the "-r"
option to have Visual SlickEdit remain resident after the batch program completes.
For any of the above methods, Visual SlickEdit will invoke the translator to compile the
source code file if the source code file exists and its date is later than the date of the pcode
(.ex) file.
Preprocessing
Preprocessing in Slick-C is indentical to C/C++ except that a #error directive has been
added. Preprocessing allows you to conditionally compile source code or define textual
replacements.
#if expression
[statements]
{#elif expression
[statements]}
[#else
[statements]]
#endif
There may be nothing more than space or tab characters preceding a '#'. Text on the same
line following #else or #endif is not allowed. Unlike in C, the expression specified MUST be
valid.
To display an error message and abort the compile, use the #error directive as shown below.
#error expression
Typically, preprocessing is used to help write macros which operate on multiple operating
systems or environments. The following constants are automatically defined by the Slick-C
translator.
The above identifiers have two underscore characters, one before and one after the alphabetic
name part.
In addition, you can use the Slick-C translator -d option to define a constant for use by
preprocessing. To test whether a constant has been defined, use the defined() function.
Example:
#if !defined(my_constant)
#define my_constant "default value"
#endif
#if __PCDOS__
name="c:\util\myprog"
#elif __UNIX__
name="/usr/bin/myprog"
#else
#error "Don't know what to do for this OS"
#endif
Pragma
#pragma is used to change various options during the course of a compile. Slick-C offers
the following options:
When on, semicolons must terminate all statements. We recommend using this
pragma so that smart editing features work better and some odd ball compilation
errors don’t occur.
#pragma option(strictparens [, ( on | off ) ] )
Turns strictparens on/off. If the second argument is not given, value is restored to
command line invocation value. Default is off.
When on, parenthesis must given on all built-in functions. We recommend using
this pragma since it makes the code a little more understandable.
NOTE: All #pragma options may be specified by command line compiler options. Run
vst.exe (UNIX: vst) with no arguments to view compiler options. You may use the VST
environment variable to specify compiler options for all your macros.
#include string_constant
Includes the file specified by string_constant for compiling. If string_constant does not
specify a path, the Slick-C translator will look in the same directory of the main source file.
Otherwise, the path specified by string_constant is searched. If the file is not found, the
Slick-C translator will look for the include file in the directories specified by the
VSLICKINCLUDE and VSLICKPATH environment variables. Include files may be nested.
Defining Controls
Typically, you do not need to tell the compiler about a control you are referring to; however
there are a couple of cases in which you must declare a control. This can happen when the
compiler can't safely assume that you are referring to a control, or when the compiler is
confused about what dialog box the control you are accessing is on. The compiler needs to
tell the linker which dialog box is supposed to contain your control.
The _nocheck keyword tells the compiler not to check if the control exists on the current
dialog box.
Example:
def primitive
The def primitive is used to bind a key sequence or event to a command or procedure and is
not typically used when creating event driven dialog boxes. The defeventtab primitive
selects the active event table that the def primitive sets the bindings to. If no defeventtab
declaration exists before the first def primitive the default_keys event table is used. The
default_keys event table defines the event handlers for Fundamental mode. The source code
representing the bindings is translated and then the event tables are loaded either by the load
command or by executing the module as a batch program. See “defmain. Writing Slick-C
Batch Files” on page 41 on for information on batch programs. Even though executing the
module as a batch program unloads the module when the defmain function terminates, the
event table changes remain present. This is because the command or procedure names and
the event tables are both maintained separately from the modules. The def primitive adds or
replaces the binding of an event table already in memory and may be used to unbind a key.
Example:
The defeventtab primitive is used to define a new event table. The syntax for defining a an
event table is:
defeventtab name;
name may contain a period ('.') character. The period is used to separate the form name from
the control name. The def primitive changes the binding of events of the last event table
defined. If no event table is defined, the default_keys event table is used.
Example:
defeventtab c_keys;
def " "=c_space;
def "ENTER"=c_enter;
Event tables are global in scope. When an event table is loaded by the load command or by
executing the module as a batch program, the new bindings will replace the event bindings of
the existing event table. If the event table specified by defeventtab does not exist, a new one
is created.
The _inherit primitive lets you link one event table to another. This makes it possible to
perform Clipboard Inheritance. If no name follows the _inherit keyword, the inheritance is
unlinked. You can add event handlers by using the def primitive or by defining an event
handler function as follows:
If ctl_name is the same name as the last event table form name (name before dot), the event
handler is attached to an event table named form_name. Otherwise, the event handler is
attached to an event table named form_name.ctl_name. The word event above may be any
valid event name. Some event names do not need to be enclosed in quotes. However, you
may want to always enclose the event names in quotes so you don't have to remember which
ones require quotes.
The syntax for ArgDecls is the same as for declaring a variable except that the static keyword
may not be used. In addition, an & before the id declares a call by references parameter. Call
by reference array and hash table parameters require parenthesis around the & and id.
#include "slick.sh"
// Since this is the first event handler defined for this control
// and the name of this control does not match the last defined event, the
// table, the Slick-C translator automatically defines the event table
// form1.ctlcommand1 and defines the lbutton_up event handler within
// this new event table
void ctlcommand1.lbutton_up()
{
// Set the p_text property of the text box control
ctltext1.p_text="Hello World";
}
When the above code is loaded with the load command ("Macro", "Load Module..."), the
editor attaches the form1.ctlcommand1 event table to a control named ctlcommand1 on
form form1. A form1 event table is not created because no event handler for this event
table was defined. When you save the configuration ("Tools", "Configuration", "Save
Configuration..."), event tables that are not used are deleted.
Module Initializations
The Slick-C language provides two module initialization functions called definit and
defload. If present, the procedures definit and defload are called when a module is loaded.
definit is called before defload. Once the module is saved by the write_state command, the
definit procedure is invoked each time the editor is invoked. This gives your modules a
chance to perform initializations such as creating a temporary file, or allocating a selection,
or bookmark.
The syntax for defining the special functions definit and defload are:
definit()
{
statements
}
defload()
{
statements
}
The return value of these functions is always void. Therefore, you cannot specify an
argument to the return statement. For better performance, use the defload primitive instead
of the definit primitive when possible since the definit primitive forces a module to be
loaded when the editor is invoked.
When definit is called, the expression arg(1) indicates whether the module was loaded with
the load command or when the editor initialized. When a module is loaded, arg(1) returns
'L'. Otherwise arg(1) returns a null string ("").
The remainder of this section will present two useful examples of using definit.
Example:
There are two subtle points to this example when assuming that the gmarkid variable will be
used to contain an allocated mark id (also called selection handle). First, the variable
gmarkid is scoped as global and not static. This is because the mark needs to remain
allocated when this module is reloaded. When the module is reloaded, an unload of the
module occurs first and the _free_selection built-in is not called to free a mark already
allocated (there is no defunload primitive). Modules with static variables (module scope)
lose their value when reloaded. Second, the value of arg(1) is used to make sure that the
variable gmarkid is initialized only when the editor is invoked and not when the module is
loaded.
Use the example below as a template for creating a temporary buffer in the hidden window:
#include "slick.sh"
definit()
{
get_view_id(view_id);
activate_view(HIDDEN_VIEW_ID);
status=find_view(".bookmark");
if ( status ) {
/* Create a buffer and view in hidden window. */
status=load_files("+c +t");
if (status) {
// The nls function may be used for national language support
// in the future.
_message_box(nls('Could not create bookmark buffer'))
return;
}
p_buf_name=".bookmark";_delete_line();
p_buf_flags= THROW_AWAY_CHANGES|HIDE_BUFFER|KEEP_ON_QUIT;
}
// Note: ELSE case cannot empty bookmark buffer unless mark ids
// are freed. Might as well leave them.
get_view_id(bookmark_view_id);
activate_view(view_id);
}
Debugging Macros
The Slick-C translator (vstw.exe UNIX: vstw) is fast, so that debug messages may be
inserted into your code and compiled quickly. Use the messageNwait (pronounced as
"message and wait") function to display a message and wait until a key is pressed. The
_message_box function may be used to display a dialog box with a message and wait until
you press Enter. Two helpful features are described in the following sections that will help
you debug and work on Slick-C macros.
mmessageNwait("%\n: %\c");
mb_message_box("%\n: %\c");
Finding Procedures
The find_proc command ("Macro", "Find Slick-C Proc...") finds Slick-C source code or help
for a Slick-C tag name you specify. This is especially useful if you are browsing a macro and
you want to find out what a function does. You can find the procedure at the cursor by
pressing the Ctrl+Period key.
find_proc proc_name
Here are some key differences between Slick-C and Visual Basic:
• When an event is sent to a control or dialog box, the object receiving the event
MUST be the active object (not necessarily the same as the system focus). This is a
major difference between Slick-C and Visual Basic. If a button control receives an
event and executes a statement like this, p_caption="New button caption", the
button's caption is changed and NOT the caption for the dialog box, which is what
Visual Basic would do.
• Built-in properties all start with the prefix "p_" to avoid these keywords from
conflicting with your own identifiers.
• Almost all properties that can be accessed at design-time may also be accessed at
run-time. For example, the p_name property for a control or dialog box may be set
after the dialog box is displayed.
• Event tables are used to group event handlers for controls. Event tables in Slick-C
are used in a similar fashion to classes in C++.
• Slick-C has sophisticated and powerful Dialog Box Inheritance Order. See
“Dialog Box Inheritance Order” on page 67 for more information.
• Parent, child, next, and previous (p_parent, p_child, p_next, p_prev) creation
order relationships are all maintained when dialog boxes are created.
• Event tables can be linked together. One event table can inherit the event handlers
of another event table. The event table links can be changed at run-time.
• The dialog editor allows event tables to be transferred through the clipboard.
Controls from the same or different dialog boxes may reference the same event
tables. There is no need for control arrays. See “Clipboard Inheritance” on
page 64 for more information.
Creating Controls
• Double click left mouse button on Properties dialog box control bitmap to create a
control in the center of the form.
• Click left mouse button on Properties dialog box control bitmap. Click left mouse
button on form, drag, and lift mouse button to create control the size of the dotted
rectangle.
Deleting Controls
Select control(s). Press Backspace or Del key to delete the selected controls.
Setting Properties
• Select control(s). Click left mouse button on property in Properties list box. Type
new value in Properties combo box. Use combo box arrow or dots to assist in
entering a value. Press Enter when Properties list box is active, this sets the
property.
• Select control(s). (some properties) Double click the left mouse button on the
property in the Properties list box to go to the next value of the property. For color
and picture properties, a dialog box is displayed.
Aligning Controls
Select the control you want to align the other controls with. Select the other controls
with Shift+LButton. Double click the left mouse button on one of the properties x or y
to align the controls in the x or y direction. Press Enter on the value in the Properties
combo box.
Sizing Controls
• To size a single control, select the control and click and drag one of the selection
handles with the left mouse button.
• To size multiple controls, select the controls and set the width or height property.
• To size multiple controls, select the controls and press Shift+Left, Shift+Right,
Shift+Up, or Shift+Down arrow to move lower right corner of the selected controls
by one pixel.
Moving Controls
• Select control(s). Click and drag with the left mouse button on any of the selected
controls.
• Select control(s). Press Left, Right, Up, or Down arrow to move selected controls
by one pixel.
The remainder of this section discusses various pieces of building a dialog box.
Creating a Form
The term form refers to the outer window of a dialog box. The objects within the dialog box
are called "controls". The term form also refers to the entire dialog box as well. A new form
can be created in one of two ways:
OR
2. Use the Open Form menu item ("Macro", "Open Form...") and specify the name of
a form that does not already exist.
Adding Controls
("Window", "Properties..." or show_properties command)
The bitmaps on the left of the Properties dialog box are used to create controls. There are
two ways to create a control. You can double click the left mouse button on the bitmap of the
control you want to create. This will place a new control in the middle of the selected form
(form1 for this sample session). However, with this method, you cannot create a control and
place it within another control. The Picture Box and Frame controls allow you to place
controls inside of them.
Here's another way to create a control. Perform these steps now to create a text box control
on form1:
• Single click on the text box bitmap so that it appears pressed in (like the arrow
bitmap above).
• Move your mouse so that it appears on top of the form, form1, you are editing. If
you can no longer see the form you are editing, use the Selected Form menu item
("Window", "Selected Form") to display it. Notice that the mouse pointer does not
look like a pointer any more. It should look like a large, thin plus sign. This
indicates that you are in creation mode.
• To create the text box control, click the left mouse button, and, while holding it
down, move the mouse pointer to the right to create a dotted rectangle. When you
release the mouse, the text box control will appear within the rectangle.
• Double click on the control in the dialog box for which you want to add code (not
the bitmap in the Properties dialog box). The Select An Event dialog box should
appear.
• If this is the first event handler for this dialog box, you will be prompted with an
Open dialog box for a new file to contain the source code for this dialog box. Type
a unique filename. Usually this filename is derived from the name of the dialog
box you are creating (like "form1.e").
After performing the above steps, the dialog editor will insert an event function definition
into your source file and place your cursor in the function.
You can display your dialog box from the command line by typing "show <FormName>".
To display the dialog box modally enter "show -modal <FormName>" on the command line.
The show command provides many powerful options. For more information, see
“Displaying Dialog Boxes” on page 59. Dialog box templates and compiled macros are
stored in the state file ("vslick.sta" or "vslick.stu" under UNIX).
The example code below illustrates how to write a command which displays a dialog box.
This is useful for binding a command to a key which displays a dialog box.
#include "slick.sh"
_command void run_form1()
{
// The -modal option displays other windows while the dialog box
// is displayed.
show("-modal form1");
}
Saving a Form
Click on the form being edited and press Ctrl+S.
• Set the "caption" property to "OK", set the "default" property to TRUE, and set the
"name" property to "ctlok"
• Double click on the command button control in the dialog box for which you want
to add code (not the bitmap in the Properties dialog box). The Select An Event
dialog box should appear.
• If this is the first event handler for this dialog box, you will be prompted with an
Open dialog box for a new file to contain the source code for this dialog box. Type
a unique filename. Usually this filename is derived from the name of the dialog
box you are creating (like "form1.e").
After performing the above steps, the dialog editor will insert an event function definition
into your source file and place your cursor in the function. Add the code as shown below:
#include "slick.sh"
defeventtab form1
// Code for OK button.
ctlok.lbutton_up()
{
// Close the dialog box and return a value. The _delete_window
// function allows modal dialog boxes to return a value. For
// more information, see
// "Displaying Dialog Boxes" below. Each object
// in the dialog box will receive an on_destroy event.
// NOTE: If "" is a valid return value. Return 1 here and store
// your results in the global _param1 variable.
p_active_form._delete_window("return value");
There are some subtle points about closing a dialog box. First, if a modal dialog box returns
a value, the value "" (zero length string) MUST be returned to indicate the dialog box has
been canceled. This convention is used so that when running a dialog box, you can press
Ctrl+Shift+Space to safely cancel and edit the dialog box. Second, you will need to use the
global container variables _param1.._param10 to return multiple strings. Alternately, you
can make an array or structure and place it in _param1. If you do place your string results in
the global variables _param1.._param10, make sure your dialog box returns 1 (or any value
other than "") to indicate that the dialog box was not canceled.
show cmdline
-xy Restore the previous x, y position of the dialog box. If the old
position cannot be found, the dialog box is centered. When the
dialog box is closed, the x, y position is automatically saved (the
dialog manager calls _save_form_xy).
-hidden Do not make the form visible.
-modal Run the form modally. All other forms are disabled. Control
returns to the caller when the form window is deleted with
_delete_window.
-nocenter Do not center the form.
-new Normally, when a form is already displayed, the existing form is
given focus. This option allows for multiple instances of a form
to be displayed.
-reinit UNIX only. Ignored by other platforms. Causes
_delete_window function to make the form invisible instead of
deleting the form. The destroy events are dispatched even
though no windows are actually destroyed. Next time show is
called for the same dialog box, the invisible dialog box is made
visible, some properties are reinitialized, and the create events
are sent. Be careful when using this option. Not all dialog
boxes can use this option without minor modifications. The
form_parent() function will not work because the next time the
form is used, the parent is not changed to the new parent
specified.
-hideondel UNIX only. Same as -reinit option except no properties are
reinitialized when the invisible dialog box is shown again.
form_name specifies a form or menu resource. If it is an integer, it must be a valid index into
the names table of a form or menu. Otherwise, it should be the name of an existing form or
menu that can be found in the names table.
If the "-modal" option is not given, the form window id is returned if successful. Otherwise,
a negative error code is returned.
Example:
// This example requires that you create a form called form1 with a
// command button and load this file.
#include "slick.sh"
_command mytest()
{
result=show("-modal form1");
if (result=="") {
return(COMMAND_CANCELLED_RC);
}
message("_param1="_param1" _param2="_param2);
}
defeventtab form1
ctlcommand1.on_create()
{
// Global variable _param1.._param10 are defined in "slick.sh" to
// allow for multiple strings
// to be returned in separate variables. Alternatively, if the
// return strings do not contain spaces, you could concatenate
// them together with a space and use the parse built-in to easily
// separate them.
_param1="string1";
_param2="string2";
// Close the dialog box and indicate that the dialog box was not
// canceled. Each object in the dialog box will receive an
// on_destroy event.
p_active_form._delete_window(1);
}
Example:
// This example requires that you create a form called form1 with a
// command button and load this file.
#include "slick.sh"
_command void mytest()
{
show("-modal form1","param1 to on_create", "param2 to on_create");
}
defeventtab form1
ctlcommand1.on_create(_str arg1="", _str arg2="")
{
//arg1=arg(1); Could get the arguments with the arg built-in
//arg2=arg(2);
messageNwait("arg1="arg1" arg2="arg2);
}
Example:
#include "slick.sh"
defmain()
{
index=find_index("form1",oi2type(OI_FORM));
if (!index) {
messageNwait("form1 not found");
return(1);
}
// Can specify name table index instead of name
// When show is called without the "-modal" option, the
// positive window id (instance handle) of the form created
// is returned.
form_wid=show("-hidden -nocenter "index);
if (form_wid<0) {
return(1);
}
// Place the form at the top left corner of the display.
form_wid.p_x=form_wid.p_y=0;
// Make the form visible
form_wid.p_visible=1;
return(0);
}
Modeless Example:
#include "slick.sh"
defmain()
{
// When show is called without the "-modal" option, the positive
// window id (instance handle) of
// the form created is returned.
form_wid=show("-hidden -nocenter form1");
if (form_wid<0) {
return(1);
}
// Place the form at the top left corner of the display.
form_wid.p_x=form_wid.p_y=0;
// Make the form visible
form_wid.p_visible=1;
return(0);
}
Sometimes you need to display a status dialog box while doing some sort of processing. This
requires a modeless dialog box so control is returned to you. However, you still probably
want to disable all other dialog boxes including the MDI window while you do your
processing.
#include "slick.sh"
static typeless gcancel;
_command void test()
{
Second, the parent window is used by some dialog boxes such as the Find, Replace, and
Spelling dialog boxes to determine which buffer to operate on. This allows these dialog
boxes to support the editor control. To do this, they call the _form_parent function during
an on_create event to get the window id of the window which contains the buffer to be
operated on. This does mean that these dialog boxes only support certain parent windows.
For example, the Find dialog box will not run correctly if the "-app" option of the show
command is used.
Clipboard Inheritance
Clipboard Inheritance (US patent No. 5,710,926) is a mechanism by which objects can be
transferred from one place to another via the clipboard in order to create new instances which
inherit the code of the original objects. Code for the new instances can be added, affecting
only the new instances, and code of the original instances can be modified, affecting both
instances. This is just another way to describe inheritance. The difference is that, with Slick-
C, the inheritance links are built for you in a very intuitive, natural way.
The most obvious flaw in Microsoft Visual Basic, Hypercard, and other high level user
interface building systems, is the loss of event handlers when you copy controls or dialog
boxes from one place to another via the clipboard.
A sample problem with some heavily commented source code will best demonstrate how the
Slick-C language works.
Assume you want to create a group of controls which are needed by Visual SlickEdit's File
Open dialog to allow the user to specify the various supported file formats. The file formats
Visual SlickEdit supports are:
Here is a partial dialog box which can handle Visual SlickEdit's file formats:
// The names of the radio button controls are ctlopendos, ctlopenmac, ctlopenunix,
// ctlopenauto
// The first text box is named ctlopenlinesep and the text box below it is named
// ctlopenwidth.
defeventtab form1;
// Define the lbutton_up event for the DOS radio button. This function will
// get called when any of the radio buttons get turned on. The event
// table automatically
// created here is called form1.ctlopendos.
ctlopendos.lbutton_up()
{
// Set the text displayed in both text boxes to nothing so the users
// knows that the radio button format has been chosen.
ctlopenlinesep.p_text='';
ctlopenwidth.p_text='';
}
static zap_radio_buttons()
{
ctlopendos.p_value=0;
ctlopenmac.p_value=0;
ctlopenunix.p_value=0;
ctlopenauto.p_value=0;
}
// Define the on_change event for the first text box. For a text box, the on_change
// event gets called when the users modifies the text in the text box.
// The event table
// automatically created here is form1.ctlopenlinesep.
ctlopenlinesep.on_change()
{
if (p_text!='') {
ctlopenwidth.p_text=''; // Clear out the other text boxes text.
zap_radio_buttons(); // Turn off all the radio buttons
}
}
// Define the on_change event for the second text box. The event table
// automatically created here is form1.ctlopenwidth.
ctlopenwidth.on_change()
{
if (p_text!='') {
ctlopenlinesep.p_text=''; // Clear out the other text boxes text.
zap_radio_buttons(); // Turn off all the radio buttons
}
}
Notice that only the first radio button ctlopendos has an event handler defined. The other
radio buttons just need to use the form1.ctlopendos event table. This can be accomplished
in the dialog editor in one of two ways. You can use Clipboard Inheritance or, if the radio
buttons are already created, you can just set the p_eventtab property of the other radio
buttons to form1.ctlopendos. To use Clipboard Inheritance you would simply write the
lbutton_up event code for the DOS radio button, copy the DOS radio button to the
clipboard, paste it back onto the dialog box within the frame, and set the p_caption property
for the new radio button to MAC. Either of these two methods can be used to propagate an
event table. When ctlopendos.lbutton_up() function gets called, it simply gets and sets the
properties of controls which obviously exist on this dialog box.
Below is a picture of Visual SlickEdit's ("fast" Windows 3.1 style) Open File dialog box:
The Open File dialog box has the form name _edit_form. This dialog box was actually
created by copying the _open_form dialog box (code links and all) to the clipboard, pasting
it, and then adding the "Find File..." button and the advanced controls. The _open_file form
can be thought of as the base File Open dialog box class. It is used for all other File Open,
and Save As operations except for opening files for editing which requires additional
controls. The inherited code from the base class File Open dialog required no changes except
for the OK button. For this, the OK button code was replaced with new code. The "Find
File..." displays a dialog box which has all the same advanced controls. The advanced
controls were taken from the Open File dialog box ("_edit_form") and all its related controls,
and copied them onto the Find File dialog box. The only additional code required was for
the OK button which was needed to return the results of the advanced options to the caller.
Visual SlickEdit's Find and Replace dialog boxes both allow searching across multiple files
and/or buffers. The controls for selecting files and buffers on these dialog boxes share the
same code. You might want to copy these controls onto your own dialog box to provide
multiple file and buffer processing for some other operation. In Visual SlickEdit 2.0, these
controls were pasted onto the Print dialog box.
It is important to note that while these examples did not require linking one event table to
another, the dialog editor recognizes when inheritance is required and will automatically link
event tables together. The statement below illustrates the syntax for linking one event table
to another.
Visual SlickEdit uses a pre-defined inheritance order called the Dialog Box Inheritance
Order. When a control receives an event, the following search begins to determine which
event handler should get control:
1. IF and ONLY if the event we are searching is a key event, check the dialog box
frame's user level 1 inheritance.
4. Check automatic inheritance. Only the text box, combo box, and editor window
can have any automatic inheritance. This is how your emulation is supported in
these controls.
As soon as an event handler is found, the search stops and the event handler is executed.
Each inheritance level can have up to 20 linked event tables. This limit is only to avoid
infinite event table link loops. At run-time it is possible, but unusual, to change all
inheritance links and event tables for any object. The eventtab_inherit function can be used
to get or set an event table inheritance link.
Through trial and error, we found the above Dialog Box Inheritance Order to be the most
powerful. It allowed the dialog manager and all the default control event handling to be
written in Slick-C.
IMPORTANT: Changing the active object does NOT change the focus. Use the
_set_focus method to change the focus.
Instance Expressions
The following examples illustrate common instance expressions:
// instance expression x.
(x+1-1).p_text="test"; // Same as previous statement. This
// shows that any valid Slick-C
// language expression may be
// used to get the window id.
x.(x+1-1).x.p_text="test";
// Same as the previous statement but wastes
// more code spaces. This shows that multiple
// periods ('.') may be used in an
// instance expressions.
form_wid=p_active_form; // Get the window id of the active form.
form_wid.ctltext1.p_text="test";
// Lookup ctltext1 as if the object
// referred to
// by the variable form_wid
// were the active object.
p_next.p_next.p_prev.p_prev.p_text="test";
// Waste some code space and access the p_text
// property of the active object.
p_window_id=_control ctltext1;
// Make the ctltext1 control the
// active object.
p_text="test"; // Access the p_text property of
// the active object.
_cmdline.p_text="test"; // _cmdline is a constant window id defined
// in "slick.sh". Set the command line p_text
// property to "test". Cool!!
#include "slick.sh"
defmain()
{
// Call the tbupcase function as a method to operate on Visual
// Visual SlickEdit's command line. _cmdline is a constant instance
// handle defined in "slick.sh".
_cmdline.tbupcase();
}
Notice that tbupcase is not defined to be a method of a particular class. There are a
significant number of functions which can operate on any Visual SlickEdit object. This
feature also allows macros written in text mode Visual SlickEdit to be converted into Visual
SlickEdit macros and used as methods. Also, most functions in Visual SlickEdit are written
to operate on the current object which means you have access to a lot of methods. Using
functions as methods is very useful when writing dialog box event handlers. The online help
indicates which functions can be used as methods for specific objects. If a function is called
and a statement within the function is not valid for the current object, the macro is stopped,
and a dialog box is displayed indicating the error. The find_error command ("Macro",
"Find Slick-C Error") may then be used to locate the source of the error. While using a
function as a method is useful, it lacks the ability to define two functions with the same name
which operate on different objects. This can be solved by allowing a class name (possibly
one of the built-in class names: _text_box, _combo_box etc.) when defining the function, like
in C++. This will be added to a future version of Visual SlickEdit.
Built-in Controls
Label Control
The label control is used to display text in any font. Labels optionally may be aligned left,
aligned right, centered horizontally, centered vertically, or centered horizontally and
vertically. If you don't need to align the label, we recommend you should the p_auto_size
property to TRUE to ensure that the text inside the window fits. A common use of a label
control is to place it to the left of a text box to tell the user about what goes in the text box.
Select the Label control and use the up, down, left, and right arrow keys to help you center
the label to the text box.
For a complete list of label control properties, methods, and events, use the menu item
("Help", "Macro Functions by Category").
Spin Control
The most common use of a spin control is to increment or decrement a number displayed in a
text box (the text box usually appears to the left of the spin control). This can be performed
WITHOUT writing any code, simply by making the "tab_index" property of the text box one
less than the "tab_index" property of the spin control. You will get an error if there is no text
box with a tab index one less than the spin control, unless the "increment" property of the
spin control is set to zero. The easiest way to do this is to create the text box and then create
the spin control. You will want to turn off the "auto_size" property of the text box so you can
make the height of the text box larger than the font. Once you get the Spin control close to
the right edge of the text box, select the spin control and use the up, down, left, and right
arrow keys to help you center the Label to the spin control. The default increment is 1. You
can also use the spin control to increment/decrement the value in a gauge or scroll bar control
or increment/decrement a hexadecimal number displayed in a text box. In this case,
however, you must set the "increment" property of the spin control to zero and process the
on_spin_up and on_spin_down events yourself. The on_change event is called with a
reason set to CHANGE_NEW_FOCUS before an on_spin_up or on_spin_down event to
allow you to return the window id of the control you want to get focus, after spinning is
completed. Return an empty string ('') if you don't care.
Example:
#include "slick.sh"
// This example requires form name form1 with a text box and spin
// control. The spin control should be named ctlspin1 and the
// "increment" property should be zero. The tab index of the text
// box MUST be one less than the spin control. This code does not
// reference the name of the text box so that you can use Clipboard
// Inheritance(R) to create multiple working copies of a spin control
// capable of incrementing/decrementing the value in a text box control
For a complete list of spin control properties, methods, and events, use the menu item
("Help", "Macro Functions by Category").
Example:
#include "slick.sh"
_command void upcase_line()
name_info(","VSARG2_TEXT_BOX|VSARG2_REQUIRES_EDITORCTL)
{
init_command_op();
get_line(line);
replace_line(upcase(line));
retrieve_command_results();
}
Bind the upcase_line command above to Alt+F12. Notice that this command works in all
text boxes, edit windows, and editor controls. The key binding may not work in a text box if
you bind upcase_line to one of the CUA keys Alt+A..Alt+Z, Ctrl+X, Ctrl+C, or Ctrl+V. Use
the Redefine Common Keys dialog box ("Tools", "Configuration", "Redefine Common
Keys...") to allow all key bindings to be inherited into text box controls.
For a complete list of text box control properties, methods, and events, use the menu item
("Help", "Macro Functions by Category").
Editor Control
The editor control allows a user to enter multiple lines. The editor control is used to view
clipboards, for the calculator, and for version control comments. Almost all your key
bindings for an MDI edit window will work in an editor control even if you have changed
your emulation. You can use macro recording to write a new command which works in an
edit window and editor control. Just check the "Allow in non-MDI editor control" check box
when you finish recording your macro.
For a complete list of editor control properties, methods, and events, use the menu item
("Help", "Macro Functions by Category").
Frame Control
The frame control is used to group a set of related controls. Often, radio buttons are placed
inside of a frame control to tell the dialog manager that only one of the radio buttons in the
group can be turned on at a time.
• Click the left mouse button on the bitmap in the Properties dialog box of the control
you want to place inside the frame. Click and drag with the left mouse button
inside the frame control to create the control with the size of the rectangle
displayed.
• Copy or cut the control you want to place inside the frame to the clipboard. Select
the frame control and press Ctrl+V to paste the control inside the frame control.
For a complete list of frame control properties, methods, and events, use the menu item
("Help", "Macro Functions by Category").
For a complete list of command button control properties, methods, and events, use the menu
item ("Help", "Macro Functions by Category").
Use one of the following methods to place a radio button inside a frame or picture box:
• Click the left mouse button on the radio button bitmap in the Properties dialog box.
Click and drag with the left mouse button inside the frame or picture box control to
create the control with the size of the rectangle displayed.
• Copy or cut the radio button(s) you want to place inside the frame or picture box to
the clipboard. Select the frame or picture box and press Ctrl+V to paste the
control(s) inside it.
For a complete list of radio button control properties, methods, and events, use the menu item
("Help", "Macro Functions by Category").
For a complete list of check box control properties, methods, and events, use the menu item
("Help", "Macro Functions by Category").
Example:
A combo box consists of 4 controls: the root window, text box, picture box, and list box. The
properties and methods of the sub-controls may be accessed individually with the p_cb,
p_cb_text_box, p_cb_picture, p_cb_list_box instance handle properties. The
p_cb_picture property is only available when the control is displayed.
Example:
defeventtab form1;
ctlcombo1.on_create()
{
// To make the loop a little more efficient, activate the list
// box of the combo box control
p_window_id=p_cb_list_box;
for (i=1;i<=100;++i){
// Add an item to the active list box.
_lbadd_item("line="i);
}
// Activate the root window of the combo box.
p_window_id=p_cb;
}
Example:
#include "slick.sh"
defeventtab form1;
ctlcombo1.on_create()
{
// Show a picture which indicates that clicking on the picture
// box button displays a dialog box. _pic_cbdots is a global
// variable defined in "slick.sh" which is a handle to a picture.
p_cb_picture.p_picture=_pic_cbdots;
}
ctlcombo1.lbutton_down()
{
// Check if the left mouse button was pressed inside the picture
// box of the combo box
if (p_cb_active==p_cb_picture) {
result=show("-modal form2");
// process result here
return("");
}
// Skip user level 1 inheritance and execute the default event
// handler defined by user level 2 inheritance.
call_event(p_window_id,lbutton_down,2);
}
Example:
{
// Check if text in combo box text is valid. You might think you
// could use a non-editable style combo box. However, many users
// prefer typing in names using completion, to using the mouse to
// select an item out of a list box.
status=ctlcombo1._cbi_search("","$");
if (status) {
_message_box("Combo box contains invalid input");
return("");
}
// have valid input
}
ctlcommand1.lbutton_up()
{
// Add some items to the combo box list
ctlcombo1.p_cb_list_box._lbadd_item("Hello")
ctlcombo1.p_cb_list_box._lbadd_item("Open");
ctlcombo1.p_cb_list_box._lbadd_item("New");
// Make the correct item in the combo box list current so combo
// box retrieval works better. _cbi_search searches for p_text
// in the combo list box. The "$" specifies that an exact match
// should be found and not a prefix match.
status=ctlcombo1._cbi_search("","$");
if (!status) {
messageNwait("Found it!");
// Select the line in the combo box so that an up or down arrow
// selects the line above or below and not the current line.
ctlcombo1.p_cb_list_box._lbselect_line();
}
}
A combo box receives an on_change event with a reason argument under the following
circumstances:
reason Description
The on_drop_down event is sent to a combo box with a reason argument. The reason
argument specifies one of the following conditions:
Example:
#include "slick.sh"
defeventtab form1;
ctlcombo1.on_drop_down(reason)
{
if (reason==DROP_INIT) {
if (p_user=="") {
p_user=1; // Indicate that the list box has been filled.
// Insert a lot of items
p_cb_list_box._insert_name_list(COMMAND_TYPE);
p_cb_list_box._lbsort();
p_cb_list_box._lbtop();
}
}
}
For a complete list of combo box control properties, methods, and events, use the menu item
("Help", "Macro Functions by Category").
Example:
if (p_Nofselected) {
ctlok.p_enabled=1; // Enable the OK button
} else if(!ctlok.p_enabled){
ctlok.p_enabled=0; // Disable the OK button
}
}
}
Example:
defeventtab form1;
ctllist1.on_create()
{
// Add some extra line height.
p_pic_space_y=PIC_LSPACE_Y;
// _pic_??? arguments are global variables defined in "slick.sh"
// which are name table indexes to pictures. You can create and
// load your own pictures. All the bitmaps are shipped with the
// editor. Use the bitmap file "_drremov.bmp" as a template for
// creating your own bitmap for a list box. You can load your
// own bitmap files with the _update_picture function.
_lbadd_item("a:",PIC_LINDENT_X,_pic_drremov);
_lbadd_item("b:",PIC_LINDENT_X,_pic_drremov);
_lbadd_item("c:",PIC_LINDENT_X,_pic_drfixed);
// The p_picture property must be set to indicate that this list box
// is displaying pictures and to provide a scaling picture for
// the p_pic_point_scale property. The p_pic_point_scale property
// allows the picture to be resized for fonts larger or smaller than
// the value of the p_pic_point_scale point size. If
// p_pic_point_scale is 0, the picture is not scaled.
p_picture=picture;
p_pic_point_scale=8;
}
Example:
// Add on a little to account for the left and right borders of the
// list box. Have to convert client width because its in pixels.
list_width=longest+ p_width-_dx2lx(p_xyscale_mode,p_client_width);
form_wid=p_active_form;
p_width=list_width;
form_wid.p_width=form_width;
// Now make sure the whole dialog box can be seen on screen
form_wid._show_entire_form();
}
Example:
// This example illustrates how to disable a list box and make the
// items in the list box appear grayed.
#include "slick.sh"
defeventtab form1;
ctllist1.on_create()
{
_lbadd_item("item1");
_lbadd_item("item2");
p_no_select_color=1;
p_enabled=0;
p_forecolor=_rgb(80,80,80);
}
For a complete list of list box control properties, methods, and events, use the menu item
("Help", "Macro Functions by Category").
The on_change event is sent after dragging the thumb box is completed. The p_value
property contains the new scroll position and will be in the range p_min..p_max.
The on_scroll event is sent while you click and drag the thumb box of a scroll bar.
Example:
#include "slick.sh"
defeventtab form1;
ctlvscroll1.on_scroll()
{
message("on_scroll p_value="p_value);
}
ctlvscroll1.on_change()
{
message("on_change p_value="p_value);
}
For a complete list of scroll bar control properties, methods, and events, use the menu item
("Help", "Macro Functions by Category").
The drive list control receives an on_change event with a reason argument of
CHANGE_DRIVE when the drive is changed by selecting a different drive from the combo
list box.
Example:
#include "slick.sh"
defeventtab form1;
ctlcombo1.on_change(reason)
{
if (reason==CHANGE_DRIVE) {
message(
"Item selected from list. Current drive is now
"_dvldrive()
);
}
}
For a complete list of drive list control properties, methods, and events, use the menu item
("Help", "Macro Functions by Category").
A file list box receives an on_change event with a reason argument under the following
circumstances:
reason Description
Example
#include "slick.sh"
defeventtab form1;
ctlcommand1.lbutton_up()
{
ctllist1._flfilename("*.bat","c:\\");
}
ctllist1.on_change(reason)
{
if (reason==CHANGE_FILENAME) {
message("File list display directory "_flfilename());
}
}
For a complete list of file list box control properties, methods, and events, use the menu item
("Help", "Macro Functions by Category").
A file list box receives an on_change event with a reason argument under the following
circumstances:
reason Description
reason Description
Example:
For a complete list of directory list box control properties, methods, and events, use the menu
item ("Help", "Macro Functions by Category").
For a complete list of picture box control properties, methods, and events, use the menu item
("Help", "Macro Functions by Category").
Gauge Control
The gauge control is typically used to indicate the completion status of a process.
For a complete list of gauge control properties, methods, and events, use the menu item
("Help", "Macro Functions by Category").
Example:
Image Control
The image control performs a subset of the features of the picture box control. However, it
has better performance because an operating system resource known as a window is not
required. The image control is best used for creating bitmap buttons for button bars or tool
bars.
2. Create an image control. Double click on the image control (Looks like slanted
rectangle) in the dialog editor.
3. Set the p_picture property to bbfind.bmp. Make sure you specify the full path
(c:\vslick\bitmaps UNIX: /usr/lib/vslick/bitmaps) is the default path used by
installation program).
4. Set the p_command property to gui_find. Notice that the down arrow of the
combo box displays all the editor commands.
In step 3, you enter the "bbfind.bmp" bitmap as an example. The "bb" prefix indicates that
this is a bitmap that can be used by a toolbar. You can edit the "bbfind.bmp" file with paint
brush (pbrush.exe). Use "bbblank.bmp" as a template for creating your own bitmap
buttons.
Example:
Example:
index=_update_picture(-1,bitmap_path_search("bbfind.bmp"));
if (index<0) {
if (index==FILE_NOT_FOUND_RC) {
_message_box("Picture bbfind.bmp was not found");
} else {
_message_box(
"Error loading picture bbfind.bmp\n\n"get_message(index));
}
return("");
}
p_picture=index;
p_command="gui_find";
p_message="Searches for a string you specify";
p_style=PSPIC_BUTTON;
}
ctlimage1.lbutton_down()
{
// Reset the button counter so we don't get
// double and triple click events.
get_event('B');
mou_mode(1)
mou_capture();
done=0;
event=MOUSE_MOVE;
for (;;) {
switch (event) {
case MOUSE_MOVE:
mx=mou_last_x("m"); // "m" specifies mouse position in
// current scale mode
my=mou_last_y("m");
if (mx>=0 && my>=0 && mx<p_width && my<p_height) {
if (!p_value) {
// Show the button pushed in.
p_value=1;
}
} else {
if (p_value) {
// Show the button up.
p_value=0;
}
}
break;
case LBUTTON_UP:
case ESC:
p_value=0; // Restore the button state
done=1;
}
if (done) break;
event=get_event();
}
mou_mode(0);
mou_release();
say('out');
return("");
}
For a complete list of image control properties, methods, and events, use the menu item
("Help", "Macro Functions by Category").
Example:
// This example illustrates how to add dialog box retrieval to your own
// dialog boxes. Create a form named "form1", a text box (any name), a
// check box (any name), and a command
// button named "ok".
#include "slick.sh"
defeventtab form1;
ctlok.on_create()
{
// Retrieve the previous response to this dialog box.
_retrieve_prev_form();
}
ctlok.lbutton_up()
{
_save_form_response();
p_active_form._delete_window(1);
}
Menus
You may change or add menu items by using the Menu Editor dialog box ("Macro",
"Menus...", select a menu). To create a new menu, use the Open Menu dialog box ("Macro",
"Menus...") and press the New button. A quick way to bind a pop-up menu to a mouse click
is to use the Show button on the Open Menu dialog box while recording a macro. When you
are finished recording the macro, the Bind Command to Key dialog box is displayed which
allows you to bind your new macro to a mouse click.
_menu menu_name {
submenu menu_item, help_command, help_message, categories
menu_item, command, categories, help_command, help_message
...
endsubmenu
submenu
...
endsubmenu
...
}
menu_item Menu item name in double quotes. Use '&' to choose selection
character.
command Any editor command. Place cursor on Visual SlickEdit
command line and press '?' to list all editor commands.
help_command Command to be executed when F1 is pressed. Usually it is a
help or popup_imessage command.
categories Specifies zero or more help categories in double quotes.
Multiple help categories are separated with '|'.
help_message A single line message in double quotes displayed on message
line.
_menu mymenu {
submenu "&File", "Help file menu", "Displays File drop-down menu","ncw"
"&New", "new", "", "help new", "Creates a new file to edit"
"&Open\tCtrl+O","gui_open","", "help gui_open", "Open a file"
endsubmenu
submenu "&Edit", "Help edit menu", "Displays Edit drop-down menu","ncw"
"Cu&t", "cut", "sel|nrdonly", "help cut", "Deletes the selection and copies it to the
clipboard"
}
Example:
#include "slick.sh"
_command void top_of_buffer()
name_info(',' VSARG2_READ_ONLY| VSARG2_REQUIRES_EDITORCTL)
{
...
}
The above command will be disabled when there is no editor control to operate on.
#include "slick.sh"
static boolean gSomeOtherState;
/*
This function gets called if your command is used in a menu _ or toolbar.
You must return a combination of the MF_ flags ORed together.
#include "slick.sh"
// Create a form called form1 and set the border style to anything BUT
// BDS_DIALOG BOX. Windows does not allow forms with a dialog
// box style border to have menu bars.
defeventtab form1;
form1.on_load()
{
// Find index of Visual SlickEdit MDI menu resource
index=find_index(def_mdi_menu,oi2type(OI_MENU));
// Load this menu resource
menu_handle=p_active_form._menu_load(index);
// _set_menu will fail if the form has a dialog box style border.
// Put a menu bar on this form.
_menu_set(menu_handle);
// You DO NOT need to call _menu_destroy. This menu is destroyed
// when the form window is deleted.
}
form1.on_init_menu()
{
// Gray out all menu items which are not allowed when there
// no child windows.
_menu_set_state(p_menu_handle,"!ncw",MF_GRAYED,"C");
}
#include "slick.sh"
defmain()
{
// Low-level code to display Visual SlickEdit menu bar as pop-up.
// Could just use show or mou_show_menu function.
index=find_index("_mdi_menu",oi2type(OI_MENU))
if (!index) {
message("Can't find _mdi_menu");
}
menu_handle=_menu_load(index,"P");
// Display this menu in the menu of the screen.
x=_screen_width()/2;y=_screen_height()/2;
flags=VPM_CENTERALIGN|VPM_LEFTBUTTON;
_menu_show(menu_handle,flags,x,y);
_menu_destroy(menu_handle);
}
There are a lot of commands which display dialog boxes. To find out what command a
particular menu item invokes, place your cursor on the menu item and press F1 to get help
on the command. If a key displays a dialog box, you can find out the command the key
executes by using the Bind Command to Key dialog box ("Tools", “Configuration”, "Key
Bindings...").
Use the menu item ("Help", "Search...") to get detailed online help and examples for these
functions. For a list of string related functions, use the help item ("Help", "Macro Functions
by Category").
Use the menu item ("Help", "Search...") to get detailed online help and examples for these
functions.
gui_find Displays Find dialog box and performs search using the find or
_mffind functions.
gui_replace Displays Replace dialog box and performs search using
gui_replace2 or _mfreplace functions.
gui_replace2 Performs a search and replace based on arguments given. This
function is very similar to the replace function, except that this
function uses a dialog box to prompt the user where to replace.
find_next Searches for next occurrence of search string used by any of
these high level search functions. This function is not affected
by previous searches done with low-level built-in functions.
find Performs search based on arguments given.
replace Performs a replace based on arguments given. The user is
prompted where to replace via the message line.
_mffind Performs a multiple file and/or buffer search based on the
arguments given.
_mfreplace Performs a multiple file and/or buffer search based on the
arguments given.
Example:
p_window_id=orig_wid;
top(); // Place the cursor at the top in column 1.
status=search("if","w@");// Case insensitive word search for "if"
// "@" specifies no string not found
// message.
for (;;) {
if (status) {
break;
}
get_line(line);
// Place the cursor at the end of the line so no more
// occurrences can be found on this line.
_end_line();
output_wid.insert_line(line);
status=repeat_search();
}
// Make the output window active so we can see the results
p_window_id=output_wid;
}
Example:
// This example is very similar to the example above except that the
// output data is placed in a view and buffer. The only advantage in
// using a view and buffer is that the output can be displayed
// in a list box without the user having to see a new window created.
#include "slick.sh"
defmain()
{
// Create a temporary view and buffer within the current window.
// Each window can store multiple cursor positions (views) to
// any buffer.
orig_view_id=_create_temp_view(temp_view_id);
if (orig_view_id=="") {
return("");
}
activate_view(orig_view_id);
top(); // Place the cursor at the top in column 1.
status=search("if","w"); // Case sensitive word search for if
for (;;) {
if (status) {
// clear the pending message caused by built-in search failing
clear_message();
break;
}
get_line(line);
// Place the cursor at the end of the line so no more occurrences
// can be found on this line.
_end_line();
activate_view(temp_view_id);
insert_line(line);
activate_view(orig_view_id);
status=repeat_search();
}
Use the menu item ("Help", "Search...") to get detailed online help and examples for these
functions.
Using selection functions is also an excellent way to transfer buffer data. For more
information, see “Selection Functions Overview” on page 101.
Example:
// This code copies selected text and keeps the resulting selection on
// the source text instead of the destination text.
if (_select_type()==""){
message(get_message(TEXT_NOT_SELECTED_RC));
return(1);
}
mark_id=_duplicate_selection() // Make a copy of the active
selection.
_copy_to_cursor();
// Save the selection id.
old_active_mark_id=
duplicate selection("");
// Must make another mark active before the old active mark can
// be freed.
show selection(mark_id); // Make copy of visible mark active
free selection(old_active_mark_id);
Example:
// This batch program converts the marked text into hexadecimal ASCII
// codes. Each hexadecimal ASCII code is separated by a comma. One
// possible use of this function could be to convert a binary font
// file into hexadecimal ASCII codes to be compiled into a C program.
#include "slick.sh"
defmain()
{
if (_select_type()=="" ) {
message(get_message(TEXT_NOT_SELECTED_RC));
return(TEXT_NOT_SELECTED_RC);
}
// Under scores must be converted to dashes.
return(filter_selection("hex-filt"));
}
_str hex_filt(string)
{
line="";
for(i =1;i<=length(string);++i) {
line=line:+dec2hex(_asc(substr(string,i,1))):+","
}
return(line);
}
When Visual SlickEdit is running in UTF-8 mode (by default, vs.exe for Windows runs in
this mode), buffers can contain either SBCS/DBCS data or UTF-8 data depending on how a
buffer is loaded. In order to make it easier for macros to support these two buffer data
formats, almost all macro functions accept and return UTF-8 strings. This allows most
macros to automatically work. Macros which use or set column positions often don’t work
correctly for both buffer data formats. The solution is to call “raw” functions.
Example:
Example:
The _UTF8() macro function will tell you whether Visual SlickEdit is running in UTF-8
mode. The p_UTF8 property tells you whether the current buffer contains UTF-8 data. The
p_encoding property indicates what format the buffer will be saved in by default.
Like typical programming languages (Java, C++), Slick-C source files are code page
dependant. Strings are converted from the current code page to UTF-8. You only have to
worry about this if you enter characters above 127.
All macro functions and properties accept and return UTF-8 except the following:
_default_option(VSOPTIONZ_SPECIAL_CHAR_XLAT_TAB)
All other options for this function are UTF-8.
All seek functions
goto_point(), _QROffset(), _GoToROffset, _nrseek(), point(), and seek()
For performance reasons all seeking is done on raw data. Since our tag database
stores seek positions, buffers need to be loaded in the same raw format so that seeks
works.
All _rawXXX() or XXX_raw() functions
(Unlike the C API) The Slick-C functions get_text(), and _expand_tabsc() return UTF-8 data.
p_display_xlat
_expand_tabsc_raw()
get_line_raw()
get_text_raw()
insert_line_raw()
_insert_text_raw()
replace_line_raw()
_rawLength()
_rawSubstr()
_rawText()
The following are Slick-C new that optional support raw data:
_MultiByteToUTF8()
_UTF8()
_UTF8Asc()
_UTF8Chr()
_UTF8ToMultiByte()
These function always return raw data. Use the vsUTF8() function or check the
VSP_XLAT property to determine if you need to translate the buffer data. Since these
API functions assume that the maximum buffer length is the same as the read length, it
would be useless for these functions to return translated data.
For performance reasons all seeking is done on raw data. Since our tag database stores
seek positions, buffers need to be loaded in the same raw format so that seeks works.
DLL Interface
Visual SlickEdit has a DLL interface for Windows. We recommend using the Slick-C macro
language instead of the DLL interface except when you need to interface to another
program’s DLL, when you need better speed, or when the Slick-C macro language is missing
a function you need. Once a DLL function has been added, it can be called from a Slick-C
macro just like any other Slick-C function. In fact, DLL functions can be used for timer call
backs and anyplace a Slick-C function is used. To get started using the DLL interface, edit
the file "simple.c" in the <vslick>\samples\simple directory and read its comments for more
information. Currently, all VSAPI functions have the prefix "vs". Use the ("Help",
"Search") menu item and type "vs" for a list of VSAPI functions.
Example:
// Capture the output of Slick GREP and process the error messages.
dos("-e grep DEBUG *.c");
// Redirect the output of grep to a file.
shell("grep DEBUG *.c >junk");
// Run the DOS dir command and wait for a key to be pressed before
// closing command shell window.
shell("dir *.c >junk","w");
// Display the Calculator dialog box. Show is an internal command.
execute("show _calc_form");
_exit_cleanup_stuff()
{
messageNwait("Got here");
}
Example:
#include "slick.sh"
// This command supports completion on a filename followed by an
// environment variable argument
_command test1() name_info(FILE_ARG" "ENV_ARG")
{
parse arg(1) with file_name env_name;
message("file_name="file_name" env_name="env_name);
}
// This command supports completion on a filename followed by an
// environment variable argument
_command test1(_str cmdline="") name_info(FILE_ARG" "ENV_ARG")
{
parse cmdline with file_name env_name;
message("file_name="file_name" env_name="env_name);
}
The string constant expression given to the name_info keyword is used for argument
completion, restricting when the command can be executed, and a few other options. See
"name_info Attributes" on page 222 for more information.
get_string Procedure
The get_string procedure reads a single argument from the user. See get_string procedure
in the online help for a complete description.
Example:
#include "slick.sh"
_command test2()
{
if (get_string(file_name,"Filename: ",FILE_ARG";Help message")) {
Example:
You may have critical modules which you want permanently stored in memory. Place the
no_code_swapping key word somewhere in your module (usually the top) to force a module
to be loaded and permanently stored in memory when Visual SlickEdit is invoked; then,
should a critical disk failure occur reading the state file, Visual SlickEdit will not be crippled.
A few of Visual SlickEdit's modules which provide basic editing capabilities remain
permanently in memory.
By convention, functions which use integer error codes return negative error codes which
correspond to the error codes in "rc.sh". For these functions, 0 means success and positive
codes means the error code is not in "rc.sh".
Some functions display an error message on the Visual SlickEdit message line. Use the
clear_message function to clear the message.
Example:
// Cause a message
_deselect();_copy_to_cursor();
// Clear the message
clear_message();
Typical Mistakes
Common programming mistakes occur when writing Slick-C language programs. One
mistake is to use the rc variable in an IF statement when the rc variable has changed. The
following example illustrates the mistake:
The above sequence of statements does NOT work because the _alloc_selection function
sets the rc variable. If you execute a statement before the IF statement, save the value of rc
in a temporary variable, as shown below:
Another common programming mistake is to rely on rc for all error conditions. Most
functions do not set rc. It is better to assign the return value to a variable, as shown below:
The _suspend primitive pushes a new level of error handling. After a _suspend has been
executed, errors which halt the interpreter will set the variable rc to the error code and
resume execution at the first statement after _suspend. The _resume function restores the
interpreter stack and code position to the state it was in after the last _suspend was executed.
Be sure to execute _resume to restore the suspend/resume stack.
Example:
defmain()
{
status=_suspend();
if (status) {
if (status<0) {
_message_box(get_message(status));
return(status);
}
return(0);
}
i=arg(1)+1; /* Handle invalid number argument */
message("i="i);
rc=1; // rc is the return value of the _suspend function
resume();
}
Event Names
Event names are used as arguments to the def primitive. Event names are also used when
comparing events returned by the get_event or test_event built-in functions or when
defining an event handler function.
An event name is a string literal of length one or more. An event name string of length one
specifies an ASCII character. To keep the macro source compatible, some event names do not
have to be enclosed in quotes as long as the “_” character is used instead of the “-” character.
The acceptable event constants are listed below:
ASCII characters
\0..\255 Backslash is used for non-displayable keys. You may also quote
displayable characters such as "a" or "4". The keys \1..\29 are also
represented by the keys "C-A", "C-B".."C-Z", "C-[","C-\\","C-]", "C-^",
and "C-_". The ASCII keys \129..\255 are the same key binding as \128.
Function keys
"F1", "F2"..."F12"
Extended keys
"ENTER", "TAB", "ESC", "BACKSPACE", "PAD_STAR", "PAD_PLUS",
"PAD_MINUS", "HOME", "END", "LEFT", "RIGHT", "UP", "DOWN", "PAD_5",
"PGUP", "PGDN", "DEL", "INS", "PAD_SLASH"
Mouse events
"LBUTTON-DOWN", "LBUTTON-DOUBLE-CLICK", "LBUTTON-TRIPLE-
CLICK", "RBUTTON-DOWN", "RBUTTON-DOUBLE-CLICK", "RBUTTON-
TRIPLE-CLICK", "MBUTTON-DOWN", "MBUTTON-DOUBLE-CLICK",
"MBUTTON-TRIPLE-CLICK", "LBUTTON-UP", "RBUTTON-UP", "MBUTTON-
UP"
ON events
"ON-CHANGE", "ON-CHANGE2", "ON-CLOSE", "ON-CREATE", "ON-CREATE2",
"ON-DESTROY", "ON-DESTROY2", "ON-DROP-DOWN", "ON-GOT-FOCUS",
"ON-HSB-BOTTOM", "ON-HSB-LINE-DOWN", "ON-HSB-LINE-UP", "ON-HSB-
PAGE-DOWN", "ON-HSB-PAGE-UP", "ON-HSB-THUMB-POS", "ON-HSB-
THUMB-TRACK", "ON-HSB-TOP", "ON-LOAD", "ON-LOAD-FOCUS", "ON-
RESIZE", "ON-SCROLL", "ON-SCROLL-LOCK", "ON-SPIN-DOWN", "ON-SPIN-
UP", "ON-VSB-BOTTOM", "ON-VSB-LINE-DOWN", "ON-VSB-LINE-UP", "ON-
VSB-PAGE-DOWN", "ON-VSB-PAGE-UP", "ON-VSB-THUMB-POS", "ON-VSB-
THUMB-TRACK", "ON-VSB-TOP"
Ctrl/Shift/Alt combinations
"A-<Extended key>", "C-<Extended key>", "S-<Extended key>", "S-C-<Extended key>"
For example, "A-ENTER", or "A-TAB" or "S-C-END"
"A-<Function key>", "C-<Function key>", "S-<Function key>"
For example, "A-F1".
"A-<Mouse event>", "C-<Mouse event>", "S-<Mouse event>"
For example, "A-LBUTTON-DOWN"
"C-S-<Extended key>", "C-<Extended key>", "S-<Extended key>"
For example, "A-ENTER" or "A-TAB"
"A-<ASCII character 0..128>"
Note that "A-a" is a different key than "A-A" which requires Alt and Shift keys to
be pressed. Also note that "A-"\1 is equivalent to "A-C-A".
"C-A", "C-B"..."C-Z", "C-[", "C-\\", "C-]", "C=^", "C-_", "C-,", "C-."
The keys \1..\29 are also represented by these keys.
"C-S-A,"C-S-B"... "C-S-Z", "C-S-1", "C-S-2"..."C-S-0"
"C-A-A".."C-A-Z"
Note that "A-"\1 is equivelent to "A-C-A".
Miscellaneous keys
"C-A-ENTER", "C-A-TAB", "C-A-ESC", "C-A-BACKSPACE"
"C-PRTSC", "C-CTRL", "A-ALT"
Miscellaneous events
"ON-SELECT"
Here are examples of uses for key names in the Slick-C language:
ctlcombo1.on_change()
{
}
ctlcombo1."c-s-a"()// Define event handler for Ctrl+Shift+A
{
}
ctlcombo1."a"-"z", "A-"Z"()// Define event handler for characters A-Z
// upper and lowercase
{
}
void p()
{
for (;;) {
key=get_event();
if (key:==name2event("ESC") break;
if (key:==name2event("UP")) {
...
} else if (key:==name2event("DOWN") ) {
...
}
}
}
• Structure data is not continuous. This is obvious for string, array, and hash table
member variables which contain variable size data. However, even other types are
sometimes stored elsewhere. In the future, after a few changes are made, the way
Slick-C lays out memory will be documented.
• There is not a sizeof function which tells you the size of a structure in bytes.
Arrays
• Space for array elements is allocated when you index into the array.
• You can't limit the number of elements that the array may contain.
• Specifying an array variable without the [] operator does not return a pointer to the
first element. Instead it refers to the entire array. This allows you to copy one array
to another or define a function which returns a copy of an array.
• There is not a sizeof function which tells you the size of the array in bytes. There is
a _length method which tells you the number of elements in the array.
Example:
struct PHONERECORD {
_str name;
_str PhoneNumber;
};
defmain()
{
PHONERECORD list[]; // No size limit is allowed here.
PHONERECORD list2[];
list2=list; // Copy the entire array into list2
Hash Tables
Slick-C provides a :[] hash table operator which is similar to the array operator [] except that
you index hash tables with a string type.
Assignment Statement
Assignment statements in Slick-C are not as shallow as C++. Array, hash table and structure
types are recursively traversed. Pointers are not traversed.
Example:
struct {
int a[];
} s1,s2;
s1.a[0]=1;
s2=s1; // Copy stucture and all elements of array.
Comparison Operator
The == and != operators support comparing container types, arrays, hash tables and
structures. Complex types are traversed recursively like the assignment statement. Strings
within an array, hash table or struct must match exactly (spaces matter).
Preprocessing
Preprocessing expressions may use string and floating point expressions.
switch Statement
The switch statement supports string expressions in addition to integer expressions.
Labeled Loops
break and continue statements take an optional label parameter so you can break a specific
loop (like Java).
Example:
outerlabel:
for (;;) {
for (;;) {
if (...) break outerlabel;
if (...) continue outerlabel;
}
}
defmain()
{
p("Param1",2,x);
}
void p()
{
messageNwait("Called with "arg()" arguments");
for (i=1;i<=arg();++i) {
messageNwait("arg("i")="arg(i));
}
//All undeclared variable parameters are passed by reference.
//so when a variable is passed, we can change the contents of
//the callers variable.
arg(3)="New value for x";
}
Clipboard Inheritance
Clipboard Inheritance provides inheritance specifically for dialog boxes. This feature allows
you to copy parts of existing dialog boxes to the clipboard, paste them elsewhere, and the
original code still runs. New code can be attached to the new controls without affecting the
original controls. New code can be added to the original controls to affect both instances of
the controls (inheritance). Creating inheritance for parts of dialog boxes is very natural
because the Slick-C language has been designed for this feature.
Type Checking
For the most part, type checking in Slick-C is identical to C++ except for the following:
• Only one syntax is currently supported for making a call with a pointer to a function
variable. The pfn(p1,p2,...) syntax is not supported. This limitation is necessary for
container variables because the compiler does not know the type of the variable.
However, support will be added for the other syntax for typed pointers to functions.
• Template classes are not supported. Container variables are sometimes a more
powerful mechanism for accomplishing much of what is done with template
classes. However, users will likely want the speed and additional type checking of
template classes and many programmers are already used to template classes.
• Currently Slick-C only supports the less ambiguous C style type casting. Support
for the new C++ type casting syntax TypeName(expression) may be added in the
future. The argument is between having an elegant less ambiguous grammar or
conforming to C++ so there is nothing new to learn. If you have an opinion on this
topic, let us know.
• Because Slick-C does not allow low level manipulation of memory, you can't do
things like type cast an "int *" to a "long *".
• There are no character constants defined using single quote characters. Slick-C
currently allows the use of single quotes to define strings. Single quoted strings are
much more readable for filenames or regular expressions which require the use of
backslashes. This syntax may be changed in the future so that single quotes may be
used for character constants.
Windows Structure
This section describes the internal data structure the editor uses to keep track of windows,
buffer positions (views), and buffer data. You need not read this unless you wish to write a
Slick-C macro which manipulates windows, views, and buffers.
The picture below represents invoking Visual SlickEdit with two files loaded (buffers b2 and
b3) and two windows (w1 and w2) viewing those files.
The extra window w0 is a hidden window used to allow quick switching to system buffers
such as ".command" and ".killed". To reduce the size of the drawing we have not given the
buffers names. Visual SlickEdit has one hidden window which cannot be displayed. If you
to attempt to leave the hidden window active, another window will be made active when the
editor refreshes the screen. Since window w1 is active, you currently see window w1 of
buffer b2. You might be able to see window w2 of buffer b3. If the window w1 does not
overlap window w2.
Visual SlickEdit maintains a ring of buffers and a ring of windows, where each window MAY
contain a ring of views. However, by convention, all windows except the hidden window
contain ONE view. Some macros temporarily create extra views in other windows, but they
delete them before they terminate. The built-ins _next_buffer and _prev_buffer activate
the next and previous buffers. _next_window and _prev_window move around the window
ring. _next_view and _prev_view move around the active view ring. The built-in function
load_files inserts views, windows, and/or buffers. _delete_buffer removes the active buffer
from the buffer ring and activates the previous non-hidden buffer. _quit_view removes the
active view from the active windows view ring. The previous view becomes the new active
view. When _quit_view is executed and only one view exists in the active window, the
window is removed and the previous window becomes active. You cannot delete the hidden
window.
A view holds the information necessary for the editor to remember your location and scroll
position in a buffer. A view also contains a window id and a buffer id. Activating a view
with the activate_view built-in activates the window and buffer specified by the view as well
as selecting the cursor location/scroll position.
Each buffer maintains a non-active view. When a buffer is activated by one of the built-ins
_next_buffer, _prev_buffer, _delete_buffer or load_files (assuming you do not use an
option which overrides this) the active view information is saved in the active buffer's non-
active view, and the new buffer’s non-active view information is copied into the active view.
1. Install Visual SlickEdit into a different directory than SlickEdit as described below.
2. Run the list_source command from the Configuration menu in SlickEdit to list the
macro source for changes you have made. This will create a file called "userdefs.e"
(UNIX: "unixdefs.e").
UNIX: Copy the "unixdefs.e" file to your local configuration directory "$HOME/
.vslick" and change its name to "vunxdefs.e".
4. Run Visual SlickEdit and type cleanup on the command line. All options that are
still present in SlickEdit will be converted.
5. Save the resulting file created by the “cleanup” command and type vusrdefs on the
command line to apply the changes to Visual SlickEdit.
To convert a macro source file you have written, perform the following steps:
1. Install Visual SlickEdit into a different directory than SlickEdit as described below
if you have not already done so.
2. Windows: Copy the macro source files you want to convert to your local
configuration directory (determined by the VSLICKCONFIG environment
variable if you have one) or to the macros directory (defaults to "c:\vslick\macros").
UNIX: Copy the macro source files you want to convert to your local
configuration directory "$HOME/.vslick".
4. Edit the macro source file in the Visual SlickEdit directory you want to convert.
The converter can convert about 95% of your macro source. Typical conversion
problems are documented in the text file "convert.doc" which is located in the
configuration directory (defaults to "c:\vslick" or "/usr/lib/vslick" for UNIX).
Index Adding 58
case 33
casting types 28
Symbols Check Box control 75
Clipboard Inheritance(R) 64
Closing a dialog box 58
#pragma 44
Combo Box control 75
_control 45, 69
Command Button control 74
_delete_window 59, 61
Commands
_inherit 48
defining 36
_resume built-in 114
Concatenation 14, 18
const 15
A Constants
numeric 14
Active form 69
Active object 69 D
Adding completion to command 38
Aligning Controls 53
Declaring (See defining) 13
AND operator 17
def 46
Anonymous Unions 24
Default Arguments 35
arg built-in 35
defeventtab 47
Argument Declarations 34
defeventtab primitive 46
Array length method 25
Defining
constants 15
B event tables 48
functions 34
Batch macro 10 keys 46
bigfloat 20 procedures 34
bigint 20 definit 49
bigstring 20 defload 49
Bitmaps defmain 41
Adding to image control 85 Dialog Box Retrieval 87
Adding to list box 79 Dialog Boxes
Bitwise Common 93
and 17 Dialog editor 52
complement 16 Dialog Editor Properties dialog box 55
or 17 Directory List Box control 82
xor 17 Drive List control 81
boolean 20, 30
break 32 E
C Editor Control 74
Environment variables
Cancel button VSLICKINCLUDE 45
VSLICKPATH 45
VST 45 L
Event driven dialog box 9
Event driven dialog boxes 48
Label control 72
Event tables 46
List Box control 78
Executing programs from macro 107
load command 47
Loading macros 12
F Loops 31
false 20 M
File List Box control 81
filter_selection procedure 101
Macro
Filtering marked text 101
prompting 109
Find Slick-C Error menu item 51
Selection filters 101
find_error command 51
Macro converting
find_proc command 51
From SlickEdit 2.3 125
Floating point numbers 14
Mark_id 101
For 31
Mathematical Operators 16
Frame control 74
Menu
Function Pointers 27
Changing or Adding 88
Menu Macro Programming 88
G Methods 70
Modal and modeless dialog boxes 62
Gauge control 83 Moving Controls 54
get_string procedure 109 Multiple Instances 69
H N
Image control 84
O
Including macro header files 45
Inheritance 48, 64, 67 Object Instances 69
initializing OK button
macro modules 49 Adding 58
Instances 69 operator 24
int 20 Operators 16
OR operator 17
P String literals 13
struct 21
suspend built-in 114
p_active_form 69
p_window_id 69
Picture Box control 83 T
Pictures
Adding to image control 85 Text Box control 73
Adding to list box 79 true 20
Pointer Variables 26 Type Casting 28
Preprocessing 43 typeless 20, 29
Product Support 2 typeless variable declaration 19
prompt procedure 110 TypeName 20
Prompting
from macros 109
Properties dialog box 55
U
prototypes 40
Unary operators 16
union 23
R
Selecting Controls 53
W
Setting Properties 53
Shelling from macro 107 wid_expression 69
Sizing Controls 54
SlickEdit 2.3 X
Converting macros 125
Spin control 72
xcom command 42
static 19
Reader’s Comments
Thank you for using Visual SlickEdit! SlickEdit Inc. invites you to give us your comments
and suggestions for improving Visual SlickEdit and this Slick-C Macro Programming Guide.
Please take a moment to complete this Reader’s Comments sheet. SlickEdit values your input
and will consider your suggestions for future product releases.
Please rate the Slick-C Macro Programming Guide on the following items:
Very
Excellent Good Fair Poor
Good
Please list your suggestions for improvements to the Slick-C Macro Programming Guide:
Please list your suggestions for improvements and/or enhancements to the Visual SlickEdit: