Header Bar

Rebol For Programmers

R2 Concepts, Techniques, and App Examples for Developers With Other Coding Experience
By: Nick Antonaccio

Contents:

1. Installing and Using Rebol
1.1 Using the Rebol Console
1.2 Rebol's Built In Code Editor
1.3 Setup Options
2. Rebol Language Basics
2.1 Functions
2.2 Running Complete Programs in Files and in the Console
2.3 Data Persistence
2.4 Variables
2.5 Conditional Evaluations
2.6 Error Handling
2.7 Concatenation and Multiline Strings
2.8 Lists
2.9 Loops
2.10 User Interfaces
2.11 User Created Functions
2.12 Objects
3. Rebol Dialects and Other Features
4. Some Practical GUI Dialect, File, and List Examples in Rebol
4.1 Building a Basic CRUD (create, read, update, delete) Contacts App
5. A Few More Complete App Examples
5.1 Tip Calculator
5.2 Days Between 2 Dates
5.3 Image Effects
5.4 Send Emails
5.5 Tile Game
5.6 Generic Calculator
5.7 Coin Flip
5.8 Additional Example Apps to Study
6. Graphics
6.1 Rebol "Draw" Basics
6.2 A Few Short Paint Apps
7. More About Series - Adding More Features to the Contacts App
7.1 Built in Help
8. A Few More Short Apps Using Series and Loop Constructs
8.1 Image Slideshow
8.2 Bar Chart
8.3 Paypal Report
8.4 Tric Trac Game
8.5 Shopping List and To-Do List
8.6 Calendar
8.7 Pig Latin Generator
8.8 Marching Alien Logo Army
8.9 Catch Game
8.10 Guitar Chord and Scale Diagrammer
8.11 Additional Series Apps to Study
9. Creating Web Applications using REBOL CGI
9.1 HTML Forms and Server Scripts - the Basic CGI Model
9.2 A Standard CGI Template to Memorize
10. Example CGI Applications
10.1 Generic CGI App, With HTML Form
10.2 CGI Photo Album
10.3 CGI Text Chat
10.4 A Generic Drop Down List Application
11. More Rebol Fundamentals
11.1 Data Types
11.2 A Variety of Additional Short Series Operation Examples
11.3 Strings
11.4 File Management and Reading/Writing From/To Varied Data Sources/Protocols
11.5 More About User Defined Functions
11.6 GUI Event Management
11.7 More Various GUI Examples, Techniques, Widgets (some grids), Etc.
11.8 Embedding Files (images, sounds, binary executables, etc.) in Code
11.9 Running Command Line Applications
12. Ports
12.1 Email Ports
12.2 Console Ports
12.3 Sound and other Hardware Ports
12.4 Network Ports
13. Shared Code Files (DLLs, So, Dylib libraries, etc.)
14. Third Party Libraries
14.1 PDF
14.2 Flash (.swf) Builder
14.3 RebGUI
14.4 Cyphre Menu and Tab Panel
14.5 Henrik Listview
14.6 Ensel Menu System
14.7 r3D
14.8 MySQL
14.9 SQLite
14.10 Doc's Captcha
14.11 Irwin/Ensel Screen Capture
14.12 XML and XML-RPC
14.13 Q-Plot Graph and Chart Library
14.14 Rebzip
15. Parse
16. Binding Words Labels to Contexts
17. Multitasking (Rebol Doesn't Have Threads)
18. More about Built-In Help
19. Learning More - Online Resources
19.1 R3 Links
19.2 Rebolforum.com
19.3 Rebol.org
19.4 Forums and The AltME Community

1. Installing and Using Rebol

Download and install the Rebol/View interpreter from:

http://www.rebol.com/download-view.html

On all platforms, this 'R2' interpreter is smaller than 1 Megabyte and takes less than a minute to set up.

Use any text editor to write code (i.e., Notepad, Textmate, Emacs, etc.). The R2 interpreter also has a simple code editor built in.

All Rebol programs must begin with the header "REBOL []". Paste the following code into a blank text document, and save it to a file name ending with ".r":

REBOL []
alert "Hello World"

If you have Rebol installed, any code file with the .r extension will run just like an executable program. For example, in Windows, save the program above to your desktop as "myapp.r", click the myapp.r icon with your mouse to run it.

1.1 Using the Rebol Console

You can paste simple bits of code directly into the Rebol interpreter console (REPL):

  1. Click the Rebol program icon (found by default on your desktop, or in Start -> Programs -> Rebol)
  2. Click the "Console" button in the Rebol program.
  3. Type or paste your code (text) at the flashing command prompt.

When you paste code into the console, there's no need to include the REBOL [] header. A one-liner like the example above can be pasted into the console like this:

alert "Hello World"

TRY THIS: Click the Rebol program icon. On the main Rebol "Viewtop" screen, click "User" -> uncheck "Open Desktop on Startup" -> Click "Save", and Rebol will automatically open to the console every time it starts.

1.2 Rebol's Built In Code Editor

Type the following code into the Rebol console, and a built-in mini text editor program will pop up:

editor none

To edit a specific file:

editor %filename.r

Press the [F5] key on your keyboard, and the code file you're editing will be saved and executed by the Rebol interpreter.

A few useful features can be added to the editor by running this line when the Rebol console is started:

do http://re-bol.com/e

1.3 Setup Options

Save a code file as "rebol.r", in the same folder as the Rebol interpreter, and it will be executed every time Rebol starts. You can create a rebol.r file to initialize options, such as email and other account settings, or to run entire scripts, such as the editor enhancement script above, to automate any common startup routines, or to run any other custom initialization code. To move all your personalized settings to another machine, just copy your rebol.r file.

2. Rebol Language Basics

Rebol is case insensitive ('print and 'PRINT refer to the same function). White space can be entered wherever desired, indentation is not required, and there are no required line endings. Expressions, even math expressions, are evaluated from left to right, top to bottom. Comments are preceded by semicolons. Blocks of code are enclosed in square brackets.

2.1 Functions

Parentheses are not required around function arguments:

print "Hello World"
alert "Hello World"  ; a pop-up message box function
request "Continue?"  ; a pop-up yes/no/cancel requestor
request-text         ; a text requestor function without any arguments
editor none          ; 'none is Rebol's version of 'null

Since there are no white space or line ending requirements, expressions can be placed next to one another as desired, with no extra separating syntax required:

alert "First function on this line" alert "Second function, same line"
alert "No"  alert "line"  alert "endings"  alert "are"  alert "required"
alert "functions are" editor "automatically grouped with their parameters"

In Rebol, some functions have optional "refinements", which allow a variety of potentially useful parameters:

request-text/title  "The /title refinement sets this header text"
request-text/default "Default user text" 
request-text/title/default  "Name:"  "John Smith"     ; 2 options together
request-text/title/offset "/offset repositions the requester" 10x100

request-pass/offset/title 10x100 "title" alert "Processing"  ; 2 functions

request-file/file %temp.txt                            ; default file name
request-file/filter ["*.txt" "*.r"]          ; only list .txt and .r files
request-file/only                         ; limit selection to single file
request-file/save                          ; save dialog (instead of open)
request-file/save/file/only/filter %temp.txt ["*.txt" "*.r"]   ; 4 options

The order of evaluation for all Rebol functions and expressions is always left to right:

print   10  +  12  /  2   ;  22 / 2 = 11    Math is ALWAYS evaluated left
print  (10  +  12) /  2   ;  22 / 2 = 11    to right.  The only exception:
print   10  + (12  /  2)  ;  10 + 6 = 16    Parentheses are evaluated 1st.

2.1.1 Return Values

You can take the return value of any function, and use it as a parameter of another function. Here, the value returned by the 'now function (the current date and time), is used as a parameter of the 'print function:

print now

Similarly, you could take the return value of the 'request-text function and 'alert it right back to the user:

alert request-text

Note that before the 'alert function above can complete its operation, a value first needs to be returned from the 'request-text function. Because of that requirement, when this code runs, the alert function is held up temporarily, and the first thing the user sees is the 'request-text popup.

Here are several functions which each rely on the return value of a following function:

editor request request-text

2.2 Running Complete Programs in Files and in the Console

Rebol code files always start with the "REBOL []" header on the first line. Save your programs to any file name with the ".r" extension, and click the file's icon to run it, or use Rebol's built-in editor and press F5 to run an edited script (or use a comparable 'execute-with' feature in your favorite editor/IDE). Here's a short program example:

REBOL []
print "Hello World!"
print "The current date and time is:"
print now
request-text/title "Enter some text to let me know what you think:"
alert "Thank you for sharing your thoughts!"

When you run simple code examples in the Rebol interpreter console, you'll see all function return values printed out (the 'print function isn't required in the console):

now

To run the code above as a program saved in a code file, you must include the REBOL [] header. And in this case, you should print the return value, and end the program with the 'halt function. The 'halt function stops the interpreter from closing, so that the user can see the output results:

REBOL []
print now
halt

2.3 Data Persistence

Rebol has a simple native ability to store, retrieve, and manipulate data in flat text files. To save any data to a hard drive, thumb drive, web server, etc., use the 'write function. File names are preceded by the percent symbol (%):

write %mydata "This is some text that I want to save for later."

To retrieve the text later, use the 'read function. You can use the 'print function to print formatted data read from a file, or the 'probe function to view the raw data without any formatting:

print read %mydata

probe read %mydata

You can read and write to servers on the Internet the exact same way you read and write to local drives on your computer:

write ftp://user:pass@site.com/www/mydata.txt "text saved to a server"

read http://site.com/mydata.txt

You can read and write binary file data like images, sounds, videos, etc., using the /binary refinement:

read/binary http://rebol.com/bay.jpg

For more complex data formatting, use the 'save function:

save %mycontacts ["John" "Bill" "Jane" "Ron" "Sue"]

save/png %myimage.png logo.gif

To load saved data, use the "load" function:

probe load %mycontacts

probe load %myimage

probe load http://rebol.com/bay.jpg

You'll see that Rebol provides all the required abilities to search, sort, compare, combine, add, remove, change, load, save, and otherwise manipulate persistent data. For many types of simple apps, database systems can be replaced by the tiny, 100% portable Rebol tool set.

2.4 Variables

In Rebol, variable labels are created with the colon symbol:

name: "John"
alert name

You can assign a variable (word label) to data returned by any function:

name: request-text/title "Name:"
alert name

data: read %mydata
alert data

2.5 Conditional Evaluations

Rebol's if/then construct is formatted like this: if (this) is true [do this]. Parentheses are not required around the test expression:

REBOL []
if (now/time = 8:00am) [alert "Time to wake up!"]
if account < $0 [alert "Your account is overdrawn."]

'Either is like if/else evaluations in other languages: if (this) is true [do this] [otherwise do this]. Indentation is not required:

REBOL []
pass: request-text/title "Enter Password:"
either pass = "secret" [
    alert "Welcome back."
] [
    alert "Incorrect password."
]

'Case can be used to choose between a variety of actions, depending on the situation:

REBOL []
name: "john"
case [
    find name "a" [alert {Your name contains the letter "a"}]
    find name "e" [alert {Your name contains the letter "e"}]
    find name "i" [alert {Your name contains the letter "i"}]
    find name "o" [alert {Your name contains the letter "o"}]
    find name "u" [alert {Your name contains the letter "u"}]
    true [alert {Your name doesn't contain any vowels!}]
]

The /all refinement of the 'case function evaluates ALL conditions, without breaking:

REBOL []
name: "brian" 
found: false
case/all [
    find name "a" [print {Your name contains the letter "a"} found: true]
    find name "e" [print {Your name contains the letter "e"} found: true]
    find name "i" [print {Your name contains the letter "i"} found: true]
    find name "o" [print {Your name contains the letter "o"} found: true]
    find name "u" [print {Your name contains the letter "u"} found: true]
    found = false [print {Your name doesn't contain any vowels!}]
]

The 'switch structure is also available:

REBOL []
favorite-day:  request-text/title "What's your favorite day of the week?"
switch/default favorite-day [
    "Monday"    [alert "Monday is the worst!  The work week begins..."]
    "Tuesday"   [alert "Tuesdays and Thursdays are both ok, I guess..."]
    "Wednesday" [alert "The hump day - the week is halfway over!"]
    "Thursday"  [alert "Tuesdays and Thursdays are both ok, I guess..."]
    "Friday"    [alert "Yay!  TGIF!"]
    "Saturday"  [alert "Of course, the weekend!"]
    "Sunday"    [alert "Of course, the weekend!"]
] [alert "You didn't type in the name of a day!"]

'All is a shorcut for multiple 'and evaluations. 'Any is a shorcut for multiple 'or evaluations:

REBOL []

time: 12:30pm
drank-water: true
if ((time > 12:00pm) and (time < 1:00pm) and (drank-water = true)) [
    alert "You should take this opportunity to go to the bathroom"
]
if all [time > 12:00pm  time < 1:00pm  drank-water = true] [
    alert "You should take this opportunity to go to the bathroom"
]

x: 2  y: 3  z: 4
if ((x = 4) or (y = 4) or (z = 4)) [alert "One of the the values is 4"]
if any [x = 4  y = 4  z = 4] [alert "One of the the values is 4"]

2.6 Error Handling

To handle errors, use "if error? try". Put the test code in a block, and follow it with another block of code to execute, when an error occurs:

if error? try [0 / 0] [alert "Divide by 0 error"]

The 'attempt structure runs the following block if there is not an error in the test block:

if attempt [1 / 1] [alert "Successful"]

The 'attempt function can also be used to completely ignore errors. This example attempts to set a variable 'x to the text read from file.txt. If the file doesn't exist, the 'x value remains set to an empty string:

x: copy ""
attempt [x: read %file.txt]

2.7 Concatenation and Multiline Strings

Rebol uses the 'rejoin function to concatenate data:

REBOL []
alert rejoin ["Current Date and Time: " now]

Multiline strings are enclosed in curly brackets:

REBOL []
print rejoin [
    {
        foo 1
        foo 2
    }
    {
        bar 1
        bar 2
    }
]

The 'trim function and it's many refinements can be use to trim extra space:

REBOL []
print trim/auto rejoin [
    {
        foo 1
        foo 2
    }
    {
        bar 1
        bar 2
    }
]

The data type of the first variable is used for the rest of any concatenated data block:

REBOL []
name: request-text
birthday: request-date
clr: request-color
fl: request-file
alert rejoin [
    uppercase name ", your birthday, color and file choices were: "
    birthday ", " clr ", " fl ", AFTERNOON: "
    either now/time > 12:00pm ["yep"]["nope"]
]

2.8 Lists

Rebol's main list structure is called the "series", and there are a number of functions built into Rebol which allow you to add, remove, change, search, sort, compare, combine, load, save, and otherwise manipulate series data. Rebol makes use of the exact same series functions when dealing with lists of nearly any type of data. In Rebol, manipulating characters within strings of text (lists of characters), graphics on screen in a game (list of images and coordinates in a draw block), data coming into and out of a network port, files on a hard drive, emails in an inbox, etc. all require knowledge of only one simple set of functions, and one unified way of thinking. The approach to dealing with lists of data is much more consistent, and uses much simpler syntax than you'll find in other languages (arrays, linked lists, etc.).

The basic code structure used to store series of data items in Rebol is called the "block". Just put square brackets around a list of items, and assign it a variable label. Notice that blocks of code are treated like any other lists:

REBOL []
names: ["John" "Dave" "Jane" "Bob" "Sue"]
codes: [2804 9439 2386 9823 4217]
dates: [21-dec-2013 1-oct-2012 1-jan-2014]
files: [%employees %vendors %contractors %events]
code:  [print "Hello World!"]  ; a list containing 1 function and 1 string

You can pick, find, sort, and otherwise manipulate items from the list using simple functions and constructs:

print pick names 3
print names/3
print find names "Dave"
print sort names
print sort codes
print sort dates
do code

Notice in the sort examples above that each type of data is automatically sorted appropriately (names alphabetically, numbers ordinally, dates chronologically).

Save and load blocks of data using the 'save and 'load functions:

REBOL []
names: ["John" "Dave" "Jane" "Bob" "Sue"]
save %mynames names
loaded-names: load %mynames
probe loaded-names

The most commonly used series functions are: pick, find, at, index?, length?, append, remove, insert, extract, copy, replace, select, sort, reverse, head, next, back, last, tail, skip, change, poke, clear, join, intersect, difference, exclude, union, unique, empty?, write, read, save, load.

Mastering series functions and manipulation concepts is one of the core skills required to become proficient with Rebol.

2.9 Loops

'Foreach is the most commonly used type of loop in Rebol:

REBOL []
names: ["John" "Dave" "Jane" "Bob" "Sue"]
foreach name names [alert name]

As in other languages, variable labels in loops are arbitrary:

REBOL []
n: ["John" "Dave" "Jane" "Bob" "Sue"]
foreach i n [alert i]

You'll often perform conditional evaluations to perform some operation with/to each item in a list. Notice again that Rebol automatically recognizes and knows how to perform appropriate calculations on certain data types. Data types are recognized by a given syntax ($44.92 is a money value, 12-jan-2014 is a date, 19x38 is a coordinate pair, etc.):

REBOL []
names: ["John" "Dave" "Jane" "Bob" "Sue"]
foreach name names [
    if find name "j" [alert name]
]

REBOL []
dates: [21-dec-2013 1-oct-2012 1-jan-2014]
foreach date dates [
    if date > 31-dec-2013 [alert form date]
]

You can structure data in a list as rows and columns. Use 'foreach to iterate over consecutively grouped pairs, or groups of any number of items in a 'row'. In the example below, rows consist of 3 columns of grouped values, labeled name, address, and phone during each iteration of the foreach loop:

REBOL []
mycontacts: [
    "John Smith" "123 Tomline Lane Forest Hills, NJ" "555-1234"
    "Paul Thompson" "234 Georgetown Pl. Peanut Grove, AL" "555-2345"
    "Jim Persee" "345 Pickles Pike Orange Grove, FL" "555-3456"
    "George Jones" "456 Topforge Court Mountain Creek, CO" ""
    "Tim Paulson" "" "555-5678"
]
foreach [name address phone] mycontacts [
    print name
]

You can also use 'repeat and 'for loops to count through the length of items in a list. This example sets a counter variable 'i to count from 1 to the length of the names block, incrementing the variable by 1 each time through the loop:

REBOL []
names: ["John" "Dave" "Jane" "Bob" "Sue"]
repeat i (length? names) [
    alert rejoin ["Name #" i ": " pick names i]
]

REBOL []
names: ["John" "Dave" "Jane" "Bob" "Sue"]
for i 1 length? names 1 [
    alert rejoin [
        "Current Name #" i ": " pick names i
        " Next Name: " pick names (i + 1)
    ]
]

Rebol's recognition of data types can be useful when running 'for loops:

for counter 1 10 1 [print counter] 
for counter 10 1 -2 [print counter]   ; count backwards from 10 to 1 by -2
for counter 10 100 10 [print counter] 
for counter 1 5 .5 [print counter] 
for timer 8:00am 9:00pm 0:05 [print timer] 
for dimes $0.00 $1.00 $0.10 [print dimes] 
for date 1-dec-2005 25-jan-2006 8 [print date]
for alphabet #"a" #"z" 1 [prin alphabet]

A 'forever loop just repeats forever, until a 'break function is encountered:

REBOL []
count: 99
forever [
    print rejoin [count " bottles of beer on the wall"]
    count: count - 1
    if count = 0 [break]
]

'While, 'until, 'loop, 'forall, 'forskip, and 'remove-each structures are also available, and functions such as 'extract help to perform common actions usually performed with loops:

while [now/date < 21-jan-2014] [print "Press [ESC] to quit"]
until [now/date >= 21-jan-2014] [print "It's now January 21, 2014"]
loop 50 [print "REBOL is great!"]
x: [1 2 3 4 5 6 7]
forall x [probe first x]
forskip x 2 [probe first x]
remove-each y x [y > 5]
extract x 2
extract x 3
extract/index x 2 2

2.10 User Interfaces

2.10.1 Faceless Apps

Apps can run in the background without ever displaying a console or user interface (i.e., to perform scheduled file maintenance, update the system clock, etc.).

2.10.2 Console Apps

Use the 'ask and 'print functions to do simple console i/o:

REBOL []
user: ask "Username (iwu83):  "
pass: ask "Password (zqo72):  "
either (user = "iwu83") and (pass = "zqo72") [
    print rejoin [newpage "Welcome back"]
] [
    print rejoin [newpage "Incorrect Password"]
    halt
]
print "You're in..."
halt

2.10.3 GUI Apps

Graphic User Interface windows with buttons, text fields, drop-down selectors, and other "widgets" are easy to create with Rebol. A simple built-in code dialect for creating interactive GUIs uses the words "view layout" to create a window layout. A block holds widget descriptions:

REBOL []
view layout [
    btn
    field
    text "REBOL is really pretty easy to program."
    text-list
    check
]

You can adjust the visual characteristics of any widget in a layout by following each widget with appropriate modifiers:

view layout [
    btn red "Click Me"
    field 400 "Enter some text here"
    text font-size 16 "REBOL is really pretty easy to program." purple
    text-list 400x300 "line 1" "line 2" "another line"
    check yellow
]

Widgets can perform functions or any code when clicked, or otherwise activated. Just put the functions inside another set of brackets after the widget:

view layout [
    btn "click me" [
        alert "You clicked the button."
    ]
]

The word "value" refers to data contained in a currently activated widget:

view layout [
    text "Some action examples.  Try using each widget:"
    button red "Click Me" [
        alert "You clicked the red button."
    ]
    field 400 "Type some text here, then press [Enter] on your keyboard" [
        alert value
    ]
    text-list 400x300 "Select this line" "Then this line" "Now this one" [
        alert value
    ]
    check yellow [alert "You clicked the yellow check box."]
    btn "Quit" [quit]    
]

This tutorial will cover much more about creating GUIs, by example.

2.10.4 CGI Web Apps

Rebol also provides CGI capabilities, so that scripts can run on a web server, accept input from HTML web forms, and print HTML responses to appear in the user's web browser. CGI scripts are covered later in this tutorial.

2.11 User Created Functions

Create user defined functions in Rebol with the "func" code structure. New functions are given a word label using the colon symbol. Variable labels for data parameters are included in a block after the 'func word:

REBOL []

triple: func [x] [
    print 3 * x
]

triple 4
triple 5
triple 6

alert "I just tripled the numbers 4 (12), 5 (15), and 6 (18)"

By default, variables set within functions remain accessible GLOBALLY:

unset 'y
triple-with-global-y: func [x] [
    y: 3 * x
    print y
]
triple-with-global-y 5
print y                         ; here, y = 15

Use the /local refinement to make variables local to the function:

unset 'y
triple-with-local-y: func [x /local y] [
    y: 3 * x
    print y
]
triple-with-local-y 5
print y                         ; here, y has no value

2.11.1 Return Values

The last value in a function definition is its return value. The 'check function below takes a list as its parameter (labeled 'lst here), and checks to see if it contains any bad words (here, words with the characters "--"). It starts out by setting the variable 'answer to "safe", then uses a 'foreach loop and an 'if condition to see if "--" is found in any of the items. If at any point the bad characters are found, the answer variable is set to "unsafe". At the end of the function the 'answer variable is returned. The function is then run on both the names1 and names2 lists, and the user is alerted with the returned results:

REBOL []
check: func [lst] [
    answer: "safe"
    foreach l lst [
        if find l "--" [answer: "unsafe"]
    ]
    answer
]
names1: ["Joe" "Dan" "Sh--" "Bill"]
names2: ["Paul" "Tom" "Mike" "John"]
alert rejoin ["The list " names1 " is " check names1]
alert rejoin ["The list " names2 " is " check names2]

The word 'return can also be used to stop a function and return a value, at any point.

2.11.2 Libraries

Save collections of useful functions to a file, and import them by "do"ing the file. If you save the function code above in a file named "myfunctions.r", the program above would look like this:

REBOL []
do %myfunctions.r
names1: ["Joe" "Dan" "Sh--" "Bill"]
names2: ["Paul" "Tom" "Mike" "John"]
alert rejoin ["The list " names1 " is " check names1]
alert rejoin ["The list " names2 " is " check names2]

Imported files can be read from local files, network URLs, emails, the system clipboard, databases, and other data sources:

do http://site.com/myfunctions.r
do read clipboard://
do first read pop://user:pass@site.com

You can actually include any code you want in imported files (not just function definitions). You could put the entire program above into a file on your web site and run it by "do"ing the URL of the file. Importing a file with 'do is exactly the same as copying and pasting the contents of the file into your code.

2.12 Objects

Object code structures contain collections of variables and functions. Rebol's GUI widget definitions can be found in the built-in 'svv object library. You can inspect the 'svv object with a question mark:

? svv

Rebol uses forward-slash "path" notation to refer to functions and variables in any object (most other languages refer to functions as "methods", and use period characters to refer to items in an object). The following code refers to the 'button object inside the 'vid-styles portion of the 'svv library:

? svv/vid-styles/button

You can use the word "do" to import libraries of object code found in local files, or at URLs:

do %rebgui.r
do http://re-bol.com/rebgui.r
? ctx-rebgui

If you try running the last line above before importing the rebgui library, you'll see that the 'ctx-rebgui object doesn't otherwise exist. (The rebgui library contains an entire GUI language with some additional features not found in Rebol's built-in 'svv library).

2.12.1 Encapsulating Code in Objects

The code below defines 2 new objects, labeled 'money and 'place. The 'make function is used to create the objects:

REBOL []
money: make object! [
    var: 1234.56
    bank: func [] [
        print ""
        print rejoin ["Your bank account balance is:  $" var]
        print ""
    ]
]
place: make object! [
    var: "Wabash"
    bank: func [] [
        print ""
        print rejoin [
            "Your favorite place is on the bank of the:  " var]
        print ""
    ]
]

The variables labeled 'var, and the functions labeled 'bank, in each object above exist and operate only within the context (namespace) of each object. Look at the result of these lines:

money/bank
money/var
place/bank
place/var

2.12.2 Inheritance

Here's an example of a new object blueprint, meant to store user account information. It contains several variable labels set to 'none, and 2 function definitions labeled 'email-address and 'display:

REBOL []
account: make object! [
    first-name: last-name: address: phone: none
    email-address: func [] [
        to-email rejoin [
            first-name "_" last-name "@website.com"
        ]
    ]
    display: func [] [
        print ""
        print rejoin ["Name:     " first-name " " last-name]
        print rejoin ["Address:  " address]
        print rejoin ["Phone:    " phone]
        print rejoin ["Email:    " email-address]
        print ""
    ]
]

Note that the variable label "email-address" is initially assigned to a function, which builds a default email address by concatenating the object's first-name and last-name variables with "@website.com". You can choose to use either that inherited default email definition, or override it by assigning an actual specific email address value. If you override the default value of the 'email-address label, the 'email-address function no longer exists in that particular object - it is overwritten by the specified email value. Below are some instances of the above object. Each instance creates a new labeled instantiation of the 'account object definition above, filled with default and/or specified data values:

Dave: make account []

John: make account [
    first-name: "John"
    last-name: "Smith"
    phone:  "555-4321"
]

Bob: make account [
    first-name: "Bob"
    last-name: "Jones"
    address: "4321 Street Place Cityville, USA 54321"
    phone:  "555-1234"
    email-address: "bob@mysite.net"
]

You can print out all the data contained in each object, by running each object's 'display function. Pay special attention to the 'email-address value in each object:

Dave/display
John/display
Bob/display

Take a look through the code of the default 'btn object in 'svv. You'll see that it's basically just a big collection of variable labels, functions, and other object definitions:

editor svv/vid-styles/btn

Here's a red button object built from the generic svv/vid-styles/btn object above. Everything about this button is exactly the same as the generic 'btn object in 'svv, except that the 'color property has been changed to red. Notice that the 'make function is used to create the 'redbtn object from the parent 'btn object in svv/vid-styles:

REBOL []
redbtn: make svv/vid-styles/btn [color: red]
editor redbtn

Here's some example code which creates the 'redbtn object above, adds it to 'svv (along with a 'redbtn label), and then uses the new 'redbtn object in a window layout:

REBOL []
append svv/vid-styles 'redbtn
redbtn: make svv/vid-styles/btn [color: red]
append svv/vid-styles redbtn
view layout [redbtn]

Here's a new tan button object which extends the 'redbtn object so that it performs one additional action, not found in the original red button definition. Override the color value and extend its definition by adding the new function to it, using the same code format as above:

tanbtn: make svv/vid-styles/redbtn [
    color: tan
    size: 400x50
    action: func [] [alert "I've been clicked"]
]

Here's an example program which creates both the 'redbtn and 'bluebtn objects above, adds them to 'svv, and then uses them in a window layout:

REBOL []

append svv/vid-styles 'redbtn
redbtn: make svv/vid-styles/btn [
    color: red
    size: 200x50
]
append svv/vid-styles redbtn

append svv/vid-styles 'tanbtn
tanbtn: make svv/vid-styles/redbtn [
    color: tan
    size: 400x50
    action: func [] [alert "I've been clicked"]
]
append svv/vid-styles tanbtn

view layout [
    redbtn "Click me and nothing happens"
    redbtn "I'm just another red button object"
    tanbtn "Click me and my default message appears"
    tanbtn "I'm just another tan button object"
]

The code which creates the 'redbtn and 'tanbtn objects could be saved in a separate file named, for example, newbtns.r:

REBOL []
append svv/vid-styles 'redbtn
redbtn: make svv/vid-styles/btn [
    color: red
    size: 200x50
]
append svv/vid-styles redbtn
append svv/vid-styles 'tanbtn
tanbtn: make svv/vid-styles/redbtn [
    color: tan
    size: 400x50
    action: func [] [alert "I've been clicked"]
]
append svv/vid-styles tanbtn

Then, anyone who wants to use the red and tan button objects can simply import and use the definitions like this:

REBOL []
do %newbtns.r
view layout [
    redbtn "Click me and nothing happens"
    redbtn "I'm just another red button object"
    tanbtn "Click me and my default message appears"
    tanbtn "I'm just another tan button object"
]

3. Rebol Dialects and Other Features

Rebol has a special built-in ability to create "dialects" to SIMPLIFY just about any sort of coding activity. For example, the code you saw earlier to create a little GUI layout is a Rebol dialect. It makes use of a specially designed syntax which hides most of the complexity of the objects, drawing routines, event management functions (actions that occur when a mouse is clicked, when a screen is closed, etc.), in an easy to read/write mini-language designed specifically for creating GUIs. You don't really need to understand anything about the object and function structure in Rebol's 'svv object to create useful screen displays. The GUI dialect parser converts simple layout code into complex lower level code comprised of objects and other more complex code structures. For example, one feature of the GUI dialect lets users create new widget objects using the 'style word. In Rebol's GUI dialect, here's how you create and use a 400x50 pixel red button which always alerts the user that it's been clicked (absolutely no formal understanding of objects is required to use this code):

REBOL []
view layout [
    style redbtn btn red 400x50 [alert "clicked"]
    redbtn "button 1"
    redbtn "button 2"
]

That code is really as concise as could possibly be imagined - much simpler than the mess of object definitions you saw in the previous section. Internally, the dialect code above is converted into a more complex lower level object, function, and variable constructs, which the user of the dialect never needs to see.

Rebol was designed with dialect creation in mind as a fundamental approach to writing code. It provides a 'parse function and a set of key language features which make it simple to create dialects that reduce code complexity to the lowest possible levels of simplicity. Designers of dialects can create specialized code structures which translate into much more complex internal operations under the hood, and perform enormous amounts of computing activity, using the shortest, easiest to use, highest level syntax possible. That's one of the main reasons why Rebol code always seems to be so dramatically short and simple, compared to examples in other languages. The built in 'parse function allows for the creation of complete mini languages which drastically reduce the complexity of coding syntax patterns needed to solve domain specific problems. Dialects have been created to provide solutions to a variety of common programming situations and routines, and Rebol is ripe for creating dialects related to any new problems which may come in the future.

Other Rebol features include:

  1. The ubiquitous series approach used to manage lists of all types: files, strings, email accounts, data in network ports, etc., all using the same functions and way of thinking.
  2. Simple data persistence features such as read, write, save, and load which, along with all the standard series functions, eliminate the need for 3rd party database systems.
  3. Lots of built in data types which are automatically recognized and handled appropriately. Not just numbers and text, but dates, times, money values, coordinates, IP addresses, etc. can all be calculated, sorted, evaluated, etc. without any special formatting. This recognition of complex data types is also important in designing dialect rules.
  4. A complete lack of all unnecessary syntax (semicolons, commas, indentation, quotes, etc.), so that code can read much more naturally, simply, and without clutter.
  5. Native ability to draw graphics, display images, and build GUI elements with events, timers, etc.
  6. Native access to network ports, files, and other data sources.
  7. Native access to Internet protocols such as HTTP, FTP, email, etc.
  8. Native ability to parse formats such as .csv, HTML, XML, etc.
  9. Native access to database systems.
  10. Natural, native interactivity between all these fundamental computing capabilities, and the ability to create new data types (such as JSON, PDF, Flash, etc.), and put them to use with simple series functions and dialects that are constructed using syntax patterns which are as simple and straightforward as possible.

4. Some Practical GUI Dialect, File, and List Examples in Rebol

Create a window with a button:

rebol []
view layout [
    btn "Click Me"
]

When the button is clicked, do something:

rebol []
view layout [
    btn "Click Me" [alert "Hello World"]
]

A window with a button and a text entry field labeled 'f:

rebol []
view layout [
    btn "Click Me"
    f: field
]

When the button is clicked, alert the user with the text currently in the 'f field. Try typing something in the field, then click the button:

rebol []
view layout [
    f: field "Type here, then click the button"
    btn "Click Me" [alert f/text]
]

When the button is clicked, write the contents of the field to the file mytext.txt, then alert the user that the file has been saved:

rebol []
view layout [
    f: field "Type here, then click the button"
    btn "Click Me" [
        write %mytext.txt f/text
        alert "Saved"
    ]
]

Add another button to read the file contents back into the field. Now you can close the program, run it again, and retrieve the saved text. Note that any time you change anything in a GUI, you must update the display with the 'show function:

rebol []
view layout [
    f: field 
    btn "Save" [
        write %mytext.txt f/text
        alert "Saved"
    ]
    btn "Load" [
        f/text: read %mytext.txt
        show f
    ]
]

The same exact program, except with a text area widget, instead of a one-line field. A simple text editor:

rebol []
view layout [
    a: area
    btn "Save" [
        write %mytext.txt a/text
        alert "Saved"
    ]
    btn "Load" [
        a/text: read %mytext.txt
        show a
    ]
]

Here the text is appended to mylist.txt. Instead of overwriting the file contents each time, each new write operation adds an additional line to the file. The 'rejoin function joins together the text and a newline (carriage return), so that each entry appears on a different line in the saved file:

rebol []
view layout [
    f: field "Enter some lines here..."
    btn "Save" [
        write/append %mylist.txt rejoin [f/text newline]
        alert "Saved"
    ]
    a: area "The contents of the file will appear here when loaded..."
    btn "Load" [
        a/text: read %mylist.txt
        show a
    ]
]

Display a list in a GUI layout, with a 'text-list widget:

rebol []
mylist: ["John" 2804 "Dave" 9439 "Jane" 2386 "Bob" 9823 "Sue" 4217]
view layout [
    text-list data mylist
]

Create a blank list using "copy []". Add items to a list using 'append - this creates a new list containing just the names from the original list:

rebol []
mylist: ["John" 2804 "Dave" 9439 "Jane" 2386 "Bob" 9823 "Sue" 4217]
names: copy []
foreach [n c] mylist [append names n]
view layout [
    t: text-list data names
]

The clicked item in a text list item is referred to with "/picked":

rebol []
mylist: ["John" 2804 "Dave" 9439 "Jane" 2386 "Bob" 9823 "Sue" 4217]
names: copy []
foreach [n c] mylist [append names n]
view layout [
    t: text-list data names [alert t/picked]
]

The "index" is the numerical location of an item in a list:

rebol []
mylist: ["John" 2804 "Dave" 9439 "Jane" 2386 "Bob" 9823 "Sue" 4217]
names: copy []
foreach [n c] mylist [append names n]
view layout [
    t: text-list data names [alert form index? find mylist t/picked]
]

Select the next item in a list (in this case, the number for the name selected). In the example list below, each number value is always found at the next index location after the name (i.e., "Jane" is at position 5 in the example list, her number 2386 is at position 6). In code, that's thought of as 1 + (Jane's position):

rebol []
mylist: ["John" 2804 "Dave" 9439 "Jane" 2386 "Bob" 9823 "Sue" 4217]
names: copy []
foreach [n c] mylist [append names n]
view layout [
    t: text-list data names [
        alert form pick mylist (1 + index? find mylist t/picked)
    ]
]

4.1 Building a Basic CRUD (create, read, update, delete) Contacts App

Here's the same idea as above, but with more data fields for each name. Name, Address, and Phone values can be found at Name, Name + 1, and Name + 2 positions:

rebol []
mycontacts: [
    "John Smith" "123 Tomline Lane Forest Hills, NJ" "555-1234"
    "Paul Thompson" "234 Georgetown Pl. Peanut Grove, AL" "555-2345"
    "Jim Persee" "345 Pickles Pike Orange Grove, FL" "555-3456"
    "George Jones" "456 Topforge Court Mountain Creek, CO" ""
    "Tim Paulson" "" "555-5678"
]
names: copy []
foreach [name address phone] mycontacts [append names name]
view layout [
    t: text-list data names [
        alert rejoin [
            "Name: "
            t/picked
            " Address: "
            pick mycontacts (1 + index? find mycontacts t/picked)
            " Phone: "
            pick mycontacts (2 + index? find mycontacts t/picked)
        ]
    ]
]

Instead of alerting some rejoined text, display each data item in a text entry field:

rebol []
mycontacts: [
    "John Smith" "123 Tomline Lane Forest Hills, NJ" "555-1234"
    "Paul Thompson" "234 Georgetown Pl. Peanut Grove, AL" "555-2345"
    "Jim Persee" "345 Pickles Pike Orange Grove, FL" "555-3456"
    "George Jones" "456 Topforge Court Mountain Creek, CO" ""
    "Tim Paulson" "" "555-5678"
]
names: copy []
foreach [name address phone] mycontacts [append names name]
view g: layout [
    t: text-list data names [
        n/text: t/picked
        a/text: pick mycontacts (1 + index? find mycontacts t/picked)
        p/text: pick mycontacts (2 + index? find mycontacts t/picked)
        show g
    ]
    text "Name:"
    n: field
    text "Address:"
    a: field
    text "Phone:"
    p: field
]

Add items to a block (data displayed in a text-list) by entering text into field widgets:

rebol []
contacts: copy []
names: copy []
foreach [name address phone] contacts [append names name]
view g: layout [
    t: text-list data [] [
        n/text: copy t/picked
        a/text: copy pick contacts (1 + index? find contacts t/picked)
        p/text: copy pick contacts (2 + index? find contacts t/picked)
        show g
    ]
    text "Name:"
    n: field
    text "Address:"
    a: field
    text "Phone:"
    p: field
    btn "Add" [
        append contacts reduce [copy n/text copy a/text copy p/text]
        names: copy []
        foreach [name address phone] contacts [append names name]
        t/data: copy names
        show t
    ]
]

Use a function to encapsulate duplicated code:

rebol []
extract-names: func [] [
    names: copy []
    foreach [name address phone] contacts [append names name]
    copy names
]
contacts: copy []
view g: layout [
    t: text-list data [] [
        n/text: copy t/picked
        a/text: copy pick contacts (1 + index? find contacts t/picked)
        p/text: copy pick contacts (2 + index? find contacts t/picked)
        show g
    ]
    text "Name:"
    n: field
    text "Address:"
    a: field
    text "Phone:"
    p: field
    btn "Add" [
        append contacts reduce [copy n/text copy a/text copy p/text]
        t/data: extract-names
        show t
    ]
]

Load a list from file using 'load , and assign it a variable label. Save a list to file using 'save . Create an empty file using write/append "" (if the file already exists, nothing gets written):

rebol [title: "Contacts"]
write/append %contacts ""
contacts: load %contacts
extract-names: func [] [
    names: copy []
    foreach [name address phone] contacts [append names name]
    copy names
]
extract-names
view g: layout [
    t: text-list data names [
        n/text: copy t/picked
        a/text: copy pick contacts (1 + index? find contacts t/picked)
        p/text: copy pick contacts (2 + index? find contacts t/picked)
        show g
    ]
    text "Name:"
    n: field
    text "Address:"
    a: field
    text "Phone:"
    p: field
    across
    btn "Add" [
        append contacts reduce [copy n/text copy a/text copy p/text]
        t/data: extract-names
        show t
    ]
    btn "Save" [save %contacts contacts]
]

Notice that the title "Contacts" was added to the program header above, which is displayed in the title bar of the GUI window.

So far, this program actually only creates and reads, but we'll add more to it shortly.

5. A Few More Complete App Examples

5.1 Tip Calculator

This app calculates the total amount, including tip, to pay for a restaurant meal:

  1. A window layout is created. It contains 3 text field widgets, labeled 'f,'t, and 'x.
  2. The 'f and 't fields contain some default money and tip rate values ($9 and .2 (20 percent)).
  3. When a value is entered into the 't field by the user, the 'x field's text is set to the value computed by multiplying the 'f field's money value, times the 't field's decimal value + 1 (i.e., the total to pay for a $9 bill is ($9 times 1.2)). The display of the 'x widget is updated with the 'show function:
REBOL [title: "Tip Calculator"]
view layout [
    f: field "$9"
    t: field ".2" [
        x/text: (to-money f/text) * (1 + (to-decimal t/text))
        show x
    ]
    x: field
]

5.2 Days Between 2 Dates

This app displays the number of days between any 2 selected dates:

  1. A window layout is created. It contains 2 buttons, a text widget displaying the text "Days Between:", and a text entry field labeled 'f.
  2. When the first button is clicked, it's face text is set to a date requested from the user. The selected date value is also assigned the variable label 's.
  3. When the second button is clicked, it's face text is set to another date requested from the user. That value is also assigned the variable label 'e. The text of the 'f field is then set to the difference between the 2 dates (e - s), and the display is updated with the 'show function.
REBOL [title: "Days Between"]
view layout [
    btn 200 "Start" [face/text: s: request-date]
    btn 200 "End" [
        face/text: e: request-date
        f/text: e - s
        show f
    ]
    text "Days Between:"
    f: field
]

5.3 Image Effects

This app allows users to apply selected effects to an image:

  1. A window layout is created. It contains an image widget labeled 'pic, and a text-list.
  2. The data in the text list is a variety of built-in effects which Rebol natively knows how to apply to images.
  3. When a value is selected from the text list, the /effect property of the 'pic image is set to that selected value, and the image is updated with the 'show function.
REBOL [title: "Image Effects"]
view layout [
    pic: image load http://re-bol.com/palms.jpg
    text-list data [
        "Invert" "Grayscale" "Emboss" "Blur" "Sharpen" "Flip 1x1"
        "Rotate 90" "Tint 83" "Contrast 66" "Luma 150" "None"
    ][
        pic/effect: to-block value
        show pic
    ]
]

5.4 Send Emails

This program allows users to send email messages.

  1. The program starts out by setting 5 required system variables: the user's POP and SMTP mail server settings, account username and password, and email address. These values must be edited to contain proper email account settings, in order for emails to actually be sent by the program.
  2. A window layout is created. It contains a text entry field labeled 'f, a multiline text area labeled 'a, a "Send" button, and 2 text widgets to tell the user what to type in the field and the area widgets ("Email:" and "Message:").
  3. Users type in the recipient's email address, and a message to send. When the "Send" button is clicked, the 'send function is executed. The parameters of the send function are the email address gotten from the text typed into the 'f field, and the multiline text typed into the 'a area. After the email has been sent, the user is alerted with the text "Sent".
REBOL [title: "Send Email"]
system/schemes/pop/host: "pop.server1" 
system/schemes/default/host: "smtp.server1" 
system/schemes/default/user: "username"
system/schemes/default/pass: "password1" 
system/user/email: you@site.com
view layout [
    text "Email:"
    f: field "joe@site.com" 
    text "Message:"
    a: area
    btn "Send" [
        send to-email f/text a/text
        alert "Sent"
    ]
]

Try editing this program so that it allows the user to set the default email settings without having to edit any program code (you can find a few such working examples at http://re-bol.com/examples.txt)

5.5 Tile Game

The code below creates a playable tile game. Click any tile piece to move it into the empty space. Rearrange the tiles in ascending order:

  1. A GUI window layout is created.
  2. The 'style word, in Rebol's GUI dialect, is used to create a new red 'box object labeled 't. Whenever a 't widget is clicked by the user, three actions occur: the label 'x is set to the box's current 'offset (coordinate position), the box's coordinate position is set to the 'e widget's position, and the 'e widget's position is changed to the 'x position. These three actions together effectively swap the positions of the clicked box and the 'e widget (as you'll see below, the widget labeled 'e is just a plain box widget, which appears as a blank space in the window layout).
  3. The 'across word, in Rebol's GUI dialect, is used to place each consecutive widget in the window across the screen (instead of one below another, each on new lines, which is the default behavior you've seen so far in GUI layouts).
  4. The 'return word, in Rebol's GUI dialect, is used to start a new "line" of widgets (similar to how a carriage return is used to start a new line in a text document).
  5. A bunch of 't box widgets are added to lines in the window layout, along with a final box widget labeled 'e. Any time you click a red box, its position is swapped with the empty box.
REBOL [title: "Tile Game"] 
view layout [ 
     style t box red [
         x: face/offset
         face/offset: e/offset 
         e/offset: x
     ] 
     across 
     t "8"  t "7"  t "6"  return 
     t "5"  t "4"  t "3"  return 
     t "2"  t" 1"  e: box 
]

5.6 Generic Calculator

Below is a calculator app. Most of the code should make sense.

  1. A GUI window is created.
  2. The 'across word is used to place each consecutive widget in the window across the screen.
  3. The window layout includes a field labeled 'f. It is 225x50 pixels in size, and the font size of text displayed in it is 25.
  4. The 'style word is used to create a new button object called 'b, which is 50x50 pixels in size, and which appends the current button's face text to the 'f field widget's text, whenever one of the 'b buttons is clicked.
  5. A bunch of 'b buttons are added to the window layout, each displaying a different number or mathematical operator. The 'return word is used to start new lines of buttons.
  6. The "=" button attempts to set the 'f field's text to whatever results in "do"ing (evaluating) the current mathematical expression displayed in the 'f field.
REBOL [title: "Calculator"]
view layout [ 
     across 
     f: field 225x50 font-size 25  return 
     style b btn 50x50 [append f/text face/text show f] 
     b "1"  b "2"  b "3"  b " + "  return 
     b "4"  b "5"  b "6"  b " - "  return 
     b "7"  b "8"  b "9"  b " * "  return 
     b "0"  b "."  b " / "  b "=" [ 
         attempt [f/text: form do f/text show f] 
     ] 
]

Try building this same app in any other programming language, and you'll see that there is absolutely nothing simpler than Rebol's dialect approach to building windowed programs. The idea of using derived GUI widget objects, variable labels, series functions, and evaluated expressions, however, is universal to creating a similar calculator application using any programming language/tool.

5.7 Coin Flip

Clicking the button in this app randomly flips between a heads and tails coin image:

  1. Two images are loaded from specified URLs on the Internet. The label 'h is assigned to the loaded heads image. The label 't is assigned to the loaded tails image.
  2. The 'random function with the /seed refinement is used to initiate Rebol's random number generator, using the current time (a generally unique value), so that unique random values can be generated with the 'random function. Any time you want to generate random numbers, text, or other random values, this stock line of code should be included in your program.
  3. A window layout is created and labeled 'g. It contains an image widget labeled 'i which initially displays the 'h image (the coin head image), a text field entry widget labeled 'f, and a button displaying the text "Flip".
  4. When the button is clicked, it sets the 'f field text to the first item in a randomly ordered list of the 2 text strings "Heads" and "Tails".
  5. Next, an 'either conditional evaluation is performed. If the text in the 'f field is "Heads" (that random state was determined in the previous step), then the image value of the 'i widget is set to the 'h image (the heads image). Otherwise (i.e., if the text in the 'f field is "Tails"), then the image in the 'i widget is set to 't (the tails image).
  6. The display (labeled 'g) is updated using the 'show function.
REBOL [title: "Coin Flip"]
h: load http://re-bol.com/heads.jpg
t: load http://re-bol.com/tails.jpg
random/seed now
view g: layout [
    i: image h
    f: field
    btn "Flip" [
        f/text: first random ["Heads" "Tails"]
        either f/text = "Heads" [i/image: h] [i/image: t] 
        show g
    ]
]

5.8 Additional Example Apps to Study

Here are a few more short app examples. Pay attention to which pieces of code are variable labels, GUI layout widgets, conditional evaluations, etc.

A web page editor:

REBOL [title: "Web Page Editor"]
view layout [
    f: field 600 "ftp://user:pass@site.com/public_html/page.html"
    a: area 600x350 
    across 
    btn "Load" [a/text: read to-url f/text  show a]
    btn "Save" [write to-url f/text a/text  alert "Saved"]
]

Here's an app to quiz users on basic addition math facts. Try changing it to quiz subtraction, multiplication, and division on selected ranges of random numbers (i.e., subtraction on numbers 1 - 100 or multiplication on numbers 1 - 12):

REBOL [title: "Math Test"]
random/seed now
x: func [] [rejoin [random 10 " + " random 20]]
view layout [
    f1: field x
    text "Answer:"
    f2: field [
        alert either f2/text = form do f1/text ["Yes!"]["No"]
        f1/text: x  show f1  focus f2
    ]
]

Try changing this one to generate insults:

REBOL [title: "Compliment Generator"]
random/seed now
view layout [
    x: area "brilliant rare unique talented exceptional"
    y: area "genius champion winner success achiever"
    btn "Compliment" [
        alert rejoin [
            "You're a "
            first random parse x/text none " "
            first random parse y/text none "!"
        ]
    ] 
]

A useful tool for web developers, which allows you to select and edit files in a folder on your FTP server:

REBOL [title: "FTP Tool"]
view  layout [
    f: field 600 "ftp://user:pass@site.com/public_html/" [
        either dir? to-url value [
            t/data: sort read to-url value 
            show t
        ][
            editor to-url value
        ]
    ]
    t: text-list 600x400 [editor to-url join f/text value]
]

6. Graphics

6.1 Rebol "Draw" Basics

To draw graphics, put a box widget into a GUI layout, followed by an 'effect block containing 1 or more 'draw commands:

rebol []
view layout [
    box 400x400 effect [
        draw [
            line 10x39 322x211
        ]
    ]
]

The draw commands are contained inside a BLOCK:

rebol []
view layout [
    box 400x400 black effect [
        draw [
            line 0x400 400x50
            circle 250x250 100
            box 100x20 300x380
            curve 50x50 300x50 50x300 300x300
            spline closed 3 20x20 200x70 150x200
            polygon 20x20 200x70 150x200 50x300
        ]
    ]
]

So you could, for example, use a foreach loop to add items to the screen. We'll append draw commands to the /effect/draw block of the object labeled 'b (in this case, the box widget being drawn upon):

rebol [title: "Drawing Animation"]
shapes: [
    [line 0x400 400x50]
    [circle 250x250 100]
    [box 100x20 300x380]
    [curve 50x50 300x50 50x300 300x300]
    [spline closed 3 20x20 200x70 150x200]
    [polygon 20x20 200x70 150x200 50x300]
]
view layout [
    b: box 400x400 black effect [draw []]
    btn "Animate Drawing" [
        foreach g shapes [
            append b/effect/draw g
            show b
            wait 1
        ]
    ]
]

Or use widgets to make changes to the data in the draw block. This code changes the 'x and 'y values of the 'pos variable (the circle's position), then updates the 'b display, using the 'show function:

rebol [title: "Animated Circle"]
pos: 200x200
view layout [
    b: box 400x400 black effect [
        draw [circle pos 20]
    ]
    across
    btn "Up"    [pos/y: pos/y - 10 show b]
    btn "Down"  [pos/y: pos/y + 10 show b]
    btn "Right" [pos/x: pos/x + 10 show b]
    btn "Left"  [pos/x: pos/x - 10 show b]
]

Here's the same example again using cursor key controls:

rebol [title: "Keyboard Controlled Animated Circle"]
pos: 200x200
view layout [
    b: box 400x400 black effect [
        draw [circle pos 20]
    ]
    across
    key keycode [up]    [pos/y: pos/y - 10 show b]
    key keycode [down]  [pos/y: pos/y + 10 show b]
    key keycode [right] [pos/x: pos/x + 10 show b]
    key keycode [left]  [pos/x: pos/x - 10 show b]
]

Color can be added to shapes in the draw block using the "pen" command. Shapes can be filled with color, with images, and with other graphic elements using the "fill-pen" command. The thickness of drawn lines is set with the "line-width" command:

rebol []
view layout [
    box 400x400 black effect [
        draw [
            pen red
            line 0x400 400x50
            pen white
            box 100x20 300x380
            fill-pen green
            circle 250x250 100
            pen blue
            fill-pen orange
            line-width 5
            spline closed 3 20x20 200x70 150x200
            polygon 20x20 200x70 150x200 50x300
        ]
    ]
]

6.2 A Few Short Paint Apps

Making a paint program is as simple as adding items to a draw block, using any sort of controlled input from a user:

rebol [title: "Paint Lines"]
view layout [
    b: box black 400x400 effect [draw []]
    text "line start point:"
    f1: field "0x400"
    text "line end point:"
    f2: field "400x50"
    btn "Add line" [
        append b/effect/draw compose [
            line (to-pair f1/text) (to-pair f2/text)
        ]
        show b
    ]
]

Here's a colorful paint example that uses key controls:

rebol [title: "Colorful Etch a Painting"]
pos: 200x200
paint: func [] [
    append b/effect/draw compose [
        pen (random 255.255.255)
        circle (pos) 2
    ]
    show b
]
view layout [
    b: box 400x400 black effect [
        draw [circle pos 10]
    ]
    across
    key keycode [up]    [pos/y: pos/y - 4  paint]
    key keycode [down]  [pos/y: pos/y + 4  paint]
    key keycode [right] [pos/x: pos/x + 4  paint]
    key keycode [left]  [pos/x: pos/x - 4  paint]
]

As you can see, drawing graphics in Rebol is all about managing that draw block by using series functions, adjusting variable values, etc. In that way, graphics programming is a lot like creating CRUD apps similar to the contacts example you saw earlier.

7. More About Series - Adding More Features to the Contacts App

Learning how to do anything more in Rebol requires understanding more about how to use blocks ("series") to store and manipulate data. Let's take the contacts example from earlier:

rebol []
write/append %contacts ""
contacts: load %contacts
extract-names: func [] [
    names: copy []
    foreach [name address phone] contacts [append names name]
]
extract-names
view g: layout [
    t: text-list data names [
        n/text: copy t/picked
        a/text: copy pick contacts (1 + index? find contacts t/picked)
        p/text: copy pick contacts (2 + index? find contacts t/picked)
        show g
    ]
    text "Name:"
    n: field
    text "Address:"
    a: field
    text "Phone:"
    p: field
    btn "Add" [
        append contacts reduce [copy n/text copy a/text copy p/text]
        extract-names
        t/data: copy names
        show t
    ]
    btn "Save" [save %contacts contacts]
]

If you want to remove a contact, you'll need a function to remove 3 data items from the saved list (name, address, and phone). Let's add a button which does that - it uses the 'remove/part function:

rebol []
write/append %contacts ""
contacts: load %contacts
extract-names: func [] [
    names: copy []
    foreach [name address phone] contacts [append names name]
    copy names
]
view g: layout [
    t: text-list data extract-names [
        n/text: copy t/picked
        a/text: copy pick contacts (1 + index? find contacts t/picked)
        p/text: copy pick contacts (2 + index? find contacts t/picked)
        show g
    ]
    text "Name:"
    n: field
    text "Address:"
    a: field
    text "Phone:"
    p: field
    across
    btn "Add" [
        append contacts reduce [copy n/text copy a/text copy p/text]
        t/data: extract-names
        show t
    ]
    btn "Remove" [
        remove/part (find contacts t/picked) 3
        t/data: extract-names
        show t
    ]
    btn "Save" [save %contacts contacts]
]

Now let's add a button to sort the entries by name. You can use the 'sort/skip function/refinement to perform that operation:

rebol []
write/append %contacts ""
contacts: load %contacts
extract-names: func [] [
    names: copy []
    foreach [name address phone] contacts [append names name]
    copy names
]
view g: layout [
    t: text-list data extract-names [
        n/text: copy t/picked
        a/text: copy pick contacts (1 + index? find contacts t/picked)
        p/text: copy pick contacts (2 + index? find contacts t/picked)
        show g
    ]
    text "Name:"
    n: field
    text "Address:"
    a: field
    text "Phone:"
    p: field
    across
    btn "Add" [
        append contacts reduce [copy n/text copy a/text copy p/text]
        t/data: extract-names  show t
    ]
    btn "Remove" [
        remove/part (find contacts t/picked) 3
        t/data: extract-names  show t
    ]
    btn "Sort" [
        sort/skip contacts 3
        t/data: extract-names  show t
    ]
    btn "Save" [save %contacts contacts]
]

We can sort in the opposite order using the 'sort function with the /skip and /reverse refinements. Notice that in each one of our new buttons, we're using the extract-names function. If we hadn't created that function, there certainly would have been a lot of duplicate code in this program:

rebol []
write/append %contacts ""
contacts: load %contacts
extract-names: func [] [
    names: copy []
    foreach [name address phone] contacts [append names name]
    copy names
]
view g: layout [
    t: text-list data extract-names [
        n/text: copy t/picked
        a/text: copy pick contacts (1 + index? find contacts t/picked)
        p/text: copy pick contacts (2 + index? find contacts t/picked)
        show g
    ]
    text "Name:"
    n: field
    text "Address:"
    a: field
    text "Phone:"
    p: field
    btn 100 "Add" [
        append contacts reduce [copy n/text copy a/text copy p/text]
        t/data: extract-names  show t
    ]
    btn 100 "Remove" [
        remove/part (find contacts t/picked) 3
        t/data: extract-names  show t
    ]
    btn 100 "Sort Ascending" [
        sort/skip contacts 3
        t/data: extract-names  show t
    ]
    btn 100 "Sort Descending" [
        sort/skip/reverse contacts 3
        t/data: extract-names  show t
    ]
    btn 100 "Save" [save %contacts contacts]
]

7.1 Built in Help

So where do you learn about all these useful series operations? Well, Rebol can provide information about using any function, with the 'help function, and you can get a list of all available functions with the 'what function. To get a start, try running this script:

REBOL []
help pick
help find
help at
help index?
help length?
help append
help remove
help insert
help extract
help copy
help replace
help select
help sort
help reverse
help head
help next
help back
help last
help tail
help skip
help change
help poke
help clear
help join
help intersect
help difference
help exclude
help union
help unique
help empty?
help write
help read
help save
help load
halt

Rebolers use the 'help function regularly when writing code. Information about using Rebol's built in GUI dialect (called "VID") can be found by examining the objects in SVV:

REBOL []
? svv
probe extract svv/vid-styles 2
? svv/vid-styles/button
? svv/vid-words
? svv/facet-words

You can view the source of all "mezzanine" (non compiled) functions, using the 'source function, or by using the 'mold function with any function's "get-word" as its argument (the "get-word" syntax is a colon preceding a word - the opposite of a set word). Exploring the source of built in functions is one of the best ways to learn Rebol:

source layout
editor mold :layout

Run the program below to create a Rebol word reference manual. The results are saved in a file named help.txt, and displayed using Rebol's built in text editor. Using the editor, you can rename the file and save it anywhere on your hard drive, make notes and write in your own examples, etc., for future reference:

REBOL [Title: "Quick Manual"]
print "This will take a minute..."  wait 2
echo %words.txt what echo off   ; "echo" saves console activity to a file
echo %help.txt
foreach line read/lines %words.txt [
    word: first to-block line
    print "___________________________________________________________^/"
    print rejoin ["word:  " uppercase to-string word]  print "" 
    do compose [help (to-word word)]
]
echo off
x: read %help.txt
write %help.txt "VID STYLES (GUI WIDGETS):^/^/"
foreach i extract svv/vid-styles 2 [write/append %help.txt join i newline]
write/append %help.txt "^/^/LAYOUT WORDS:^/^/" 
foreach i svv/vid-words [write/append %help.txt join i newline]
b: copy [] 
foreach i svv/facet-words [
    if (not function? :i) [append b join to-string i "^/"]
]
write/append %help.txt rejoin [
    "^/^/STYLE FACETS (ATTRIBUTES):^/^/" b "^/^/SPECIAL STYLE FACETS:^/^/"
]
y: copy ""
foreach i (extract svv/vid-styles 2) [
    z: select svv/vid-styles i
    ; additional facets are held in a "words" block:
    if z/words [
        append y join i ": "
        foreach q z/words [if not (function? :q) [append y join q " "]]
        append y newline
    ]
]
write/append %help.txt rejoin [
    y "^/^/CORE FUNCTIONS:^/^/" at x 4
]
editor %help.txt

More about built in help will be demonstrated later in this text. It's critical to understand how to access all the help information and resources available directly in the interpreter. Those resources are essential not only as instructional materials while learning, but also while working regularly with Rebol code. The availability to use help search, auto completion, system object introspection, and other documentation features, all built directly into the interpreter, enable it to operate as a fully self-documented system. No IDE or external resources are required to provide Rebol coding support.

7.1.1 The Word-Broswer.r Script

The program below presents nicely formatted help text with clickable examples that clarify how all Rebol's functions work. It covers the overwhelming majority of the Rebol language, and everything is organized into helpful categories, and cross referenced with other related functions, so you can quickly find and use the code you need. It's an indispensable tool which will help you learn all about the core language functions. Combine it with a thorough exploration of the 'system object (especially, the 'svv object for GUI programming), and you will know most of what you need to be productive with Rebol:

do http://re-bol.com/wordbrowser.r

8. A Few More Short Apps Using Series and Loop Constructs

8.1 Image Slideshow

This app displays all the images in the current folder, with a 3 second pause between each:

  1. The list of files in the current directory is read, and assigned the variable label 'files.
  2. A 'forever loop is started.
  3. Inside each repeat of the 'forever loop, a 'foreach loop is executed. For each file in the folder, if the suffix of the file is found in the given list of file extensions (png, jpg, gif, bmp), then any currently displayed window layout is unviewed, and a new window layout displaying the current file image is displayed. If the user clicks the image with the mouse, the program is quit. Otherwise the program waits 3 seconds and then continues with the loops.
REBOL [title: "Image Slideshow"]
files: read %./
forever [
    foreach file files [
        if find [%.png %.jpg %.gif %.bmp] suffix? file [
            unview 
            view/new center-face layout [
                image file [quit]
            ]
            wait 3
        ]
    ]
]

8.2 Bar Chart

This little app demonstrates how to visually display a list of numerical values in a graphic bar chart:

  1. In this example, a block (list) of months and numerical values is created, labeled 'd.
  2. An empty block labeled 'gui is created.
  3. A 'foreach loop is used to go through each month and value pair in the 'd list. For each pair of month and value items in the 'd list, some GUI layout code is appended to the 'gui block. The appended code consists of a button, sized according to the computation (value * 10), and displaying the month text. The 'compose function is used to evaluate the parenthesized size and month values. For example, if the selected month and value items are "March" and 13, the items added to the 'gui block are [btn 130 "March"].
  4. The constructed 'gui layout block is displayed. The 'center-face word is used to center the window layout on screen.
REBOL [title: "Bar Chart"] 
d: ["March" 13 "April" 9 "May" 21 "June" 29 "July" 10] 
gui: copy []
foreach [month value] d [
    append gui compose [btn (value * 10) (month)]
]
view center-face layout gui

8.3 Paypal Report

This example downloads a Paypal account file from the web, and computes a sum of all sales in the "Gross Sales" column:

  1. A variable label 'sum is set to represent an initial value of $0.
  2. A variable label 'x is set to represent a block of line data read from the given URL.
  3. The first line in the 'x data block is a bunch of column headers (titles for each column of data). We don't want to use that line in our computations, so a new variable 'y is set to represent all but the first line of 'x values (i.e., 'y is set to start "at" the second line in 'x).
  4. A foreach loop is started, to run through each 'line in the 'y block.
  5. The data on each line of the Paypal file is in "CSV" or "comma separated value" format, which means that each value on each line of the file is separated by a comma character. The 'parse function separates each line into a block of values, labeled 'values here.
  6. The "Gross Sales" value is found in column number 8 on each line, so we'll add the current 'sum value to the money value picked from item 8 in the 'values block. By doing this for each line, the final 'sum value will be the total of all individual Gross Sales values.
  7. Alert the sum to the user.
REBOL [title: "Paypal Reports"]    
sum: $0
x: read/lines http://re-bol.com/Download.csv
y: at x 2
foreach line y [
    values: parse/all line ","
    sum: sum + to-money pick values 8
]
alert join "Total Gross Sales: " sum

8.4 Tric Trac Game

The point of the Tric-Trac board game is to roll two dice, and match the rolled number with any combination of available numbers on the board. For example, if you roll a 6 with the dice, you can match with the numbers 5 + 1, or 4 + 2, or 3 + 2 + 1, etc., on the board. Once a number on the board has been selected, it cannot be used again for the rest of the game. The game is over when you have no possible combinations of number buttons to match the rolled value. To determine your final score, sum the remaining unselected numbers on the board. The goal is to get the lowest final score (you can play against yourself or take turns playing against others). Here's how the code works in this simple Tric-Trac game simulation:

  1. The "random/seed" function is used to seed Rebol's random number generator with the current time, so that unique random numbers can be generated with the 'random function.
  2. A new block labeled 'g is created. To start out, that block contains 2 lines of GUI dialect code: the 'across layout word, and a button which alerts the user with a random value between 1 and 12 (to simulate a roll of the dice).
  3. A 'repeat loop is used to add GUI code to the 'g block. That code consists of a text widget displaying a number 1 - 12, and a check widget.
  4. The 'g block layout code is displayed in a centered GUI layout window.
REBOL [title: "Tric Trac"]
random/seed now
g: [
    across 
    btn "Roll" [alert form 1 + random 11]
] 
repeat i 12 [
    append g compose [text (mold i) check]
]
view center-face layout g

A version of this game with many more features is available at http://www.rebol.org/view-script.r?script=trictrac.r

8.5 Shopping List and To-Do List

This app keeps a shopping list of items to buy. It checks to make sure that you're not adding duplicate items, and allows you to save and load your list.

  1. A window layout is created. It contains five widgets: a text list 500x400 pixels in size, labeled 't, a text entry field 500 pixels wide, labeled 'f, and three buttons.
  2. When the user enters an item in the 'f field, the program checks to see if the entered text is already found in the list. If a duplicate entry is not found in the list, the item is added to the data block displayed by the 't widget (t/data). The display is updated with the 'show function, and the cursor is placed back in 'f field, with the 'focus function.
  3. If the user clicks the "Remove Selected" button, the picked item is removed from the text list, and the display is updated.
  4. If the user clicks the "Save List" button, the data in the text list is saved to the file %shopping.txt, and the user is alerted with the text "Saved".
  5. If the user clicks the "Load List" button, the text list is set to display the data loaded from the %shopping.txt file, and the display is updated.
REBOL [title: "Shopping List"]
view center-face layout [
    t: text-list 500x400
    f: field 500 "(Enter Items Here)" [
        if not find t/data f/text [
            append t/data copy f/text
        ] 
        show t 
        focus f
    ]
    across
    btn "Remove Selected" [remove find t/data t/picked  show t]
    btn "Save List" [save %shopping.txt t/data  alert "Saved"]
    btn "Load List" [t/data: load %shopping.txt  show t]
]

Here's a slightly more complicated "To-Do List" variation of the program above, which adds some features. It provides the option to save and load lists with different file names, the ability to move items up or down in the list order, and the ability to save a screenshot of the program to a .png image, and view that image in a web browser. Try to pick out the series functions and variable labels, and see if you can follow the general program flow:

REBOL [title: "To Do"]
view/new gui: layout [
    t: text-list 500x400
    across
    f: field [if not find t/data v: value [append t/data copy v] focus f]
    btn "Del"  [remove find t/data t/picked]
    btn "Up"   [attempt [move/to z: find t/data t/picked (index? z) - 1]]
    btn "Down" [attempt [move/to z: find t/data t/picked (index? z) + 1]]
    btn "Load" [attempt [t/data: load request-file/file/only %todo.txt]]
    btn "Save" [attempt [
        save request-file/save/file/only %todo.txt t/data
        alert "Saved"
    ]]
    btn "Print" [save/png %todo.png to-image gui  browse %todo.png]
] 
forever [wait .1  show gui  if not viewed? gui [quit]]

8.6 Calendar

Here's a little personal calendar app which allows you to save events for specific days:

  1. A new file "mycal" is created to save the calendar event data. Remember, the /append refinement of the 'write function adds data to a file (instead of erasing the existing contents). If the file doesn't already exist, the 'write function creates a new empty file (i.e., with nothing ("") added to it). If the file already exists, nothing ("") is added to it, so it stays the same.
  2. A 'forever loop is started.
  3. A 'request-date function is used to get a date selected by the user. The result of that function is labeled 'd. If the user selects no date ('none), then the program quits.
  4. A window layout is created and centered on screen (with the 'center-face GUI dialect word).
  5. The selected date is displayed in bold text at the top of the window.
  6. A text area widget labeled 'a is added to the layout. It contains the data "select"ed (found after) the 'd date value, in the "mycal" file (if the selected date isn't found in the mycal file, nothing is displayed).
  7. A button widget is added to the layout. When it's clicked, the date ('d) and text contained in the area labeled 'a, are saved to the mycal file, and then the current layout window is unviewed.
  8. The 'forever loop starts over.
REBOL [title: "Calendar"]    
write/append %mycal "" 
forever [ 
     if none = d: request-date [quit] 
     view center-face layout [ 
         text bold form d 
         a: area form select (l: load %mycal) d 
         btn "Save" [ 
             save %mycal head insert l reduce [d a/text] 
             unview 
         ] 
     ] 
]

8.7 Pig Latin Generator

This console program allows the user to enter a phrase, which is converted to Pig Latin:

  1. The user is asked for a string of text from the user. The ^/ characters at the end of the quoted text are used to print a carriage return (they're the same thing as a "newline"). The label 'p is assigned to the text entered by the user.
  2. The 'parse function is used to split apart strings at specified characters. Here, the phrase labeled 'p above, is split apart at each space. The result of the 'parse function is a block of text strings (here, the individual words of the phrase entered by the user). That list of words is assigned the label 's.
  3. A foreach loop is used to go through each word in the 's block of words. During each repeat of the foreach loop, the individual words from the list are labeled 'w.
  4. During each repeat of the foreach loop, each individual word is split apart at the vowels. That block of characters is labeled 'x.
  5. The Pig Latin output is created by concatenating several pieces of text: the part of the word before the first vowel, and the part of the word beginning with the first vowel. A conditional 'either evaluation is used to determine which portion of the given word should be used in each one of the Pig Latin halves of the word. Another 'either evaluation is used to determine which ending characters should be used to form the second word in the Pig Latin text (based on whether or not non-vowel characters exist before the first part of the orginal word). For example, if the word starts with a consonant letter, the second half of the Pig Latin pair ends in "ay". Otherwise, it ends in "hay". Note that the 'prin function is used to print each Pig Latin word. The 'prin function does the same thing as 'print, except it doesn't attach a newline character to the end of each printed item (in this case, the allows each of the output words to be printed next to one another, instead of on separate lines).
  6. A blank line is printed, and the 'halt function is used to keep the program from closing, so that the user can read the printed results.
REBOL [title: "Pig Latin"]
p: ask "Type some words to convert to Pig Latin:  ^/^/"
s: parse p " "
foreach w s [
    x: parse w "aeiou"
    prin rejoin [
        either q: find/match w x/1 [q] [w] 
        x/1 
        either q ["ay"]["hay"]
        " "
    ]
]
print newline
halt

8.8 Marching Alien Logo Army

Here's an example that moves an army of Rebol logo graphics across the screen:

  1. A block of starting position coordinates is defined, labeled 'poss.
  2. A 'build-block function is defined. It builds a block of draw commands from the block of existing position coordinates, moving each image 8 pixels from the existing position. In this function, the 'poss block values are first copied over to the variable label 'existing-poss, and a new blank 'poss block is created. A foreach loop is used to fill the new 'poss block with coordinates values that are moved 8 pixels to the right (+ 8x0) from their locations in the existing poss block. Then a new 'logos block is created, and a foreach loop is used to fill it with draw commands composed from the generated coordinates in the new 'poss block.
  3. The 'build-block function is run once before showing the window display, to create a 'logos block of draw commands. The function definition above just defines what the 'build-block function does. In order to run that code, you must actually execute the 'build-block function (just like any other function (request-text, now, etc.), the function is executed by just typing its name).
  4. A window layout is created. It contains a draw block displaying all the graphics commands in the 'logos block.
  5. The window layout also contains a button widget. When the user clicks the button, a block of code is looped 10 times. Inside the loop, the 'build-block function is executed, the display of the 'b box is updated with the 'show function, so that it displays the new graphics commands in the generated 'logos block, with the images shifted over 8 pixels to the right. The loop is paused for .2 seconds during each of the 10 iterations.
REBOL [title: "Marching Alien Logo Army"]
poss: [
    10x30 120x30 230x30 340x30 450x30 560x30
    10x60 120x60 230x60 340x60 450x60 560x60 
]
build-block: func [] [
    existing-poss: copy poss
    poss: copy []
    foreach pos existing-poss [append poss pos + 8x0]
    logos: copy []
    foreach pos poss [append logos compose [image (pos) logo.gif]]
]
build-block
view layout [
    b: box 800x300 aqua effect [draw logos]
    btn "Move 10 clicks"[
        loop 10 [
            build-block
            show b
            wait .2
        ]
    ]
]

8.9 Catch Game

In this game the player tries to catch falling Rebol logo images, using cursor keys to move. Speed increases as the game progresses. Game play ends when a falling logo hits the floor. Final score is the amount of time the game was played. Scores are saved along with each player's name, sorted from lowest to highest score, and displayed at the end of the game. Take a look at the GUI window elements, conditional evaluations of computed variable values inside a forever loop, score list management operations, persistent data storage, etc.:

  1. The "random/seed" function is used to seed Rebol's random number generator with the current time, so that unique random numbers can be generated with the 'random function.
  2. The variable label 'start is set to hold the time when the program was started.
  3. The variable 'speed is set to an initial value of 2.
  4. A vindow layout is created. The /new refinement of the 'view function is used to create a window, without showing it yet. The layout is labeled 'g. The layout is 600x440 pixels in size. An image widget containing the built in 'logo.gif image is placed at coordinate 280x20. That widget is labeled 'a. A blue button widget sized 50x20 pixels is placed at coordinate 280x420. That widget is labeled 'b. Keyboard handlers for the left and right arrow keys are set up. If the left key is pressed, the position of the blue 'b button is shifted 10 pixels to the left. If the right key is pressed, the 'b button's coordinate is shifted 10 pixels to the right.
  5. A forever loop is started.
  6. The position of the 'a image is shifted down. The distance of the shift is determined by the 'speed variable. The 'as-pair function is used to create a coordinate in which the y (vertical) value is the rounded 'speed value. That incremented coordinate value is added to the current coordinate of the 'a widget. The end result of this calculation is that the widget is moved down 'speed number of pixels.
  7. A conditional 'if evaluation is used to check whether the 'a and 'b widgets are overlapping (i.e., the player piece and the falling image are touching/coliding - if so, the falling piece is being caught). If so, the position of the 'a widget is set to a random x coordinate at the top of the screen. This starts dropping a "new" logo image, once the previous falling image has been caught (there's really only one logo image in the entire game - it's just repositioned). The x value of the random coordinate is somewhere between 1-500 pixels from the left side of the window, and the y value is 20 pixels from the top of the window. The speed value is also incremented by .1 every time a falling logo is caught. This means that every 10 catches, the speed increases by one full rounded pixel value.
  8. An 'if evaluation is used to check whether the y value of the 'a widget's coordinate is greater than 440 (i.e., the image is touching the floor). If so, game play is over, so the 'break function is used to end the 'forever loop.
  9. If game play hasn't been stopped above, the program waits .01 seconds, shows all updates to the window layout (labeled 'g), and continues with another repeat of the 'forever loop.
  10. When game play ends, the score is saved, and high scores are displayed in sorted order from lowest to highest. To do this, first a "scores" file is created, if it doesn't already exist. Next, any existing scores are loaded from the file. Then the score list is appended with 2 values: the current score (which is the current time minus the start time), and the player's name (gotten using the 'request-text function). The score list is sorted by score, using the 'skip refinement (every 2 items in the list contains a score and a name (score, name, score, name, etc.)), and that sorted list is saved to the scores file. The 'request-list function is used to display all the sorted scores.
REBOL [title: "Catch Game"]
random/seed now   
start: now/time
speed: 2
view/new center-face g: layout [
    size 600x440 
    at 280x20   a: image logo.gif
    at 280x420  b: btn 50x20 blue
    key keycode [left]  [b/offset: b/offset - 10x0]
    key keycode [right] [b/offset: b/offset + 10x0]
]
forever [
    a/offset: a/offset + (as-pair 0 round speed)
    if overlap? a b [
        a/offset: as-pair (random 500) 20
        speed: speed + .1
    ]
    if a/offset/y > 440 [break]
    wait .01
    show g
]
write/append %scores ""
scores: load %scores
append scores now/time - start 
append scores request-text/title "Name:"
save %scores sort/skip scores 2 
request-list "High Scores:" scores

8.10 Guitar Chord and Scale Diagrammer

This program allows the user to quickly create and save images of guitar fretboard diagrams for chords and/or scales. When the program starts, enter the number of frets you want in your diagram (the default is 5 frets, but you could use 3 or 4 for smaller diagrams, or more for full fretboard scale diagrams). Click any fret on any string to add a dot (finger position). Click any added dot to remove it (change it back to an empty position). Right-Click any fret on any string to add a character of your choice (you could use this feature to add finger numbers, root note labels, interval labels, etc.). Click the title text ("Chord Name") to give the chord or scale a name. Right-Click the title to save the diagram to a .png image (the default image file name is the title text entered above).

  1. The program starts by getting a number of frets from the user. The 'request-text function is used, with the refinements /title and /default (a default number of 5 is presented to the user). The user's answer is labeled 'f.
  2. A block of GUI dialect code is labeled 'g. The word 'backdrop sets the background color of the window layout to white. The word 'across is used arrange the widgets across the screen. The word 'space is used to set the space between widgets to 0 in both the horizontal and vertical direction (there's usually a default space buffer between widgets - here it's set to 0x0). The word 'origin is used to set the position of the first widget in the layout to 0x0.
  3. The 'style word is use to create a new widget labeled 's, which is a 20x20 pixel box, displaying the character "|" on its face, with a font size of 20, a font color of black, and a font shadow offset of 0x0 (no shadow). There are two actions set up for the 's widget. The first block runs whenever the widget is left-clicked with the mouse (just like the normal action block you've seen in all the other examples so far). The second block runs whenever the widget is right-clicked with the mouse. When any 's widget is left-clicked, the face text is set to either "O" or "|". The 'either conditional evaluation is used to determine if the current face text is "|". If so, the face text is set to "O", if not the face text is set to "|". If any 's widget is right-clicked, the face text is set to some text requested from the user.
  4. A text widget labeled 't is added to the layout. It is 120x24 pixels in size, with bold, centered text, and a font size of 18. It is assigned 2 alternate action blocks, to react to left and right clicks of the mouse. If the text widget is left clicked, the face text is set to some text requested from the user. The 'request-text function is used, with the /default refinement, and the default text that appears in the requester is the current face text on the widget. If the text widget is right clicked, an attempt is made to save the 'p layout to an image file. The 'save function with the /png refinement takes 2 parameters: the name of the file to save to, and the data to save. In this case, the name of the file is gotten from the user, with the 'request-file function. The /save /only and /file refinements of the 'request-text function bring up a save dialogue which allows for only one file selection, with a default file name (in this case, the text of the 't widget joined with the characters ".png"). The image data is created using the 'to-image function, with the current layout (labeled 'p) as it its parameter.
  5. The 'loop function is used to run a block of code 'f number of times. Remember, the 'f label was set at the beginning of the program - it's the number of frets to include in the layout. Here, the text answer gotten from the user needs to be converted to an integer number. That's accomplished using the 'to-integer function. The looped block appends a number of 's widgets and a 'return word (newline), to the 'g layout block above.
  6. The 'g layout code block is viewed, and that layout is labeled 'p. To be clear, the label 'g refers to the GUI dialect code block used to create the layout. The label 'p refers to the actual current layout (which internally consists of lots of low level objects, functions, draw commands, etc.). That low level, currently drawn layout is what's required as a parameter of the 'to-image function.
REBOL [title: "Guitar Chord and Scale Diagrammer"]
f: request-text/title/default "Number of frets in each diagram:" "5"
g: [
    backdrop white
    across  space 0x0  origin 0x0
    style s box 20x20 "|" font-size 20 font-color black shadow 0x0 [
        face/text: either "|" = face/text ["O"] ["|"] 
    ] [
        face/text: request-text
    ]
    t: text 120x24 bold center font-size 18 "Chord Name" [
        face/text: request-text/default face/text
    ] [ attempt [
        save/png request-file/save/only/file join t/text ".png" to-image p
    ] ]
    return
]
loop to-integer f [append g [s s s s s s return]]
view center-face p: layout g

Here's a version of the program above, with an added HTML layout feature. Press the "p" key on your keyboard to create an HTML layout of any selected diagrams you've created. The HTML layout can be printed, emailed, uploaded to a web site, etc.:

REBOL [title: "Guitar Chord and Scale Diagrammer"]
f: request-text/title/default "Number of frets in each diagram:" "5"
layout-html: func [] [
    filelist: sort request-file/filter/title 
        ["*.png"] "Select image(s) to print:" ""
    html: copy "<html><body>"
    foreach file filelist [
        append html rejoin [
            {<img src="file:///} to-local-file file {"> &nbsp; &nbsp; }
        ]
    ]
    append html [</body></html>]
    write %chords.html trim/auto html
    browse %chords.html 
]
g: [
    at 100x100 key #"p" [layout-html]
    backdrop white
    across  origin 0x0  space 0x0
    style s box 20x20 "|" font-size 20 font-color black shadow 0x0 [
        face/text: either "|" = face/text ["O"] ["|"] 
    ] [
        face/text: request-text
    ]
    t: text 120x24 bold center font-size 18 "Chord Name" [
        face/text: request-text/default face/text
    ] [ attempt [
        save/png request-file/save/only/file join t/text ".png" to-image p
    ] ]
    return
]
loop to-integer f [append g [s s s s s s return]]
view center-face p: layout g

The apps in this section clearly demonstrate that lists and loop code structures are important additions to concepts such as variables, functions, persistent data storage, and other basic coding principles.

8.11 Additional Series Apps to Study

Here are a few more short app examples which make use of series constructs. See if you can follow the basic flow of code in each program.

A full screen presentation (try to gues which system variable holds the size of the screen). Note that the slide layouts are stored in a nested block of blocks:

REBOL [title: "Simple Presentation"]
slides: [
    [
        at 0x0 box system/view/screen-face/size white [unview]
        at 20x20 h1 blue "Slide 1"
        box black 2000x2
        text "This slide takes up the full screen."
        text "Adding images is easy:"
        image logo.gif
        image stop.gif
        image info.gif
        image exclamation.gif
        text "Click anywhere on the screen for next slide..."
        box black 2000x2
    ]
    [
        at 0x0 box system/view/screen-face/size effect [
            gradient 1x1 tan brown
        ] [unview]
        at 20x20 h1 blue "Slide 2"
        box black 2000x2
        text "Gradients and color effects are easy in REBOL:"
        box effect [gradient 123.23.56 254.0.12]
        box effect [gradient blue gold/2]
        text "Click anywhere on the screen to close..."
        box black 2000x2
    ]
]
foreach slide slides [
    view/options center-face layout slide 'no-title
]

A simple but fully functional retail cash register app:

REBOL [title: "Minimal Cash Register"]
view gui: layout [
    style fld field 80
    across
    text "Cashier:"   cashier: fld 
    text "Item:"      item: fld 
    text "Price:"     price: fld [
        if error? try [to-money price/text] [alert "Price error" return]
        append a/text reduce [mold item/text tab price/text newline]
        item/text: copy "" price/text: copy ""
        sum: 0
        foreach [item price] load a/text [sum: sum + to-money price]
        subtotal/text: form sum
        tax/text: form sum * .06
        total/text: form sum * 1.06 
        focus item
        show gui
    ]
    return
    a: area 600x300
    return
    text "Subtotal:"   subtotal: fld 
    text "Tax:"        tax: fld 
    text "Total:"      total: fld
    btn "Save" [
        items: replace/all (mold load a/text) newline " "
        write/append %sales.txt rejoin [
            items newline cashier/text newline now/date newline
        ]
        clear-fields gui
        a/text: copy ""             
        show gui             
    ]
]

A report program which calculates the daily sales total for the program above:

REBOL [title: "Daily Sales Total"]
sales: read/lines %sales.txt
sum: $0
foreach [items cashier date] sales [
    if now/date = to-date date [
        foreach [item price] load items [
            sum: sum + to-money price
        ]
    ]
]
alert rejoin ["Total sales today: " sum]

Below are two different version of the same group chat program. The first runs in the text console. The second uses a GUI window to allow the user to input text and view messages. The messages are just stored in a text file on a web server (to use this program, you need to know a valid web site FTP url, and use correct username and password info to write the file):

REBOL [title: "Group Chat (Console)"]
url: ftp://user:pass@site.com/public_html/chat
write/append url ""
name: copy ask "^LName:  "
forever [
    notes: copy read url
    message: ask rejoin [newpage notes "Message:  "]
    if message = "erase" [write url ""]
    if message <> "" [
        write/append url rejoin [
            now " (" name "):  " message "^/^/"
        ]
    ]
]

REBOL [title: "Group Chat (GUI)"]
url: ftp://user:pass@site.com/public_html/chat
write/append url ""
name: request-text/title "Name:"
view gui: layout [
    a: area 600x300
    text "New Message:"
    message: field 600 [
        if message/text = "erase" [write url ""]
        if message/text <> "" [
            write/append url rejoin [
                now " (" name "):  " message/text "^/^/"
            ]
        ]
        a/text: copy read url  focus message  show gui 
    ]
]

Here's a game which manipulates a list of images in a draw block:

REBOL [title: "Ski Game"]
tree:  load to-binary decompress 64#{
eJzt18sNwjAQBFDTBSVw5EQBnLjQE1XRngmBQEj8Wa/3M4oYOZKBKHkaWwTO1/sh
jDkNx3N6HI7LcOzCfnz/9v5cMnEai7lj4mokT9C7XczUsrhvGSku6RkgDIbHAEP0
2EiIMBdMDuaOWZCSL91bQvCsSY4MHE9umXz7ydVi3xgltYvEKboexzVSlpTa614d
NonpUauIv176dX0ZTRgJlVgzNl25A3gkGwld1bkrNFqqedQfEI02AU9PjDeMpac/
ShKeTXylROqCImlXRFd9zkQoh4tp+GpqlSTnLnum4HTEzK/gjpmTpDxSASlHFqYU
EE/8nddG9n+9LIm8t9OeIEra2JZWDRSG4VEioa0UFCZFqv/aMQh2Rf790EnGgcJU
SVAer0Bhcp7/epVJvkHzBHjPfz+XSe6BwryC5gmQno3mAY3tpba2KAAA
}
skier-left: load to-binary decompress 64#{
eJyN0U8og2EcB/DvNrz+E5fJZSmRf9Ej76h3Ne1AIspyMQflpJDFU/KO1cQmSnGa
A3PYkvInB3kvuyzlgJolh+fCRUq5iBvP8+5lTvKrX33ep+/zp9/b2Tthhl6zvGt5
W3nX8TYhS1//MOGnSjNEa/AUxd0UVQ3raL9IYbBvA2OBI9Q0DqB6fAujl08Yi97D
Hr3F5EQYSss2OrrWEFo5xB+VO5Vx/skvnxmQbDCFvxcjMJ/b0s6LAZXGA3O0ZtTt
pW3WbJmDeMC8a1gE9o3bTBFI9YvGhrOKSueyEQpu9ri60vQFXFqPMx1K+sNWrdOh
73Y/uMr85fKdcIrJ0z6vxSfsYV5KCU2JEPNIlD9dFZ65AfXwD+HsKdAZiiLdqtvt
Hh65E5ZklTGmDvWLgxxKkjAivwt7XxhJEvIsrCY8ikLs0Tj3yGeCKaQtdsX9fv3G
N1jCJdyv84lHJkNriiM7Li29OIDV0jcU8kuIHaiPLEDEsG9DQYxiQTi0A8sBpEvh
OT65GmBYH9Jx5nf8TFFUFf5ZX2hFdG1uAgAA
}
skier-right: load to-binary decompress 64#{
eJxz8s1jYgCDMiDWAGIJINYCYkYGFrD4D0YGOBBAMBn4++Yz6HjVMSgY1oP5gWdu
M/gHTmCwNutlKJ26l6F03VUGp3XnGGo+/mGILVnMoFkwhaHm7GcGz4m7GbABFwST
eQWSNXMQbM+3DAwlULbmEgaWXih75QUGzvkQJstMBwbPRRA2L1D5yS8QNudioNQF
qNYPDExAZRCtDg78c6Fa7wZK3Ycq940O3L1fAcLWigpctUsZzHTSj5Jd+l7NAKS6
3HnXk6jHSiBF7sUmxi7Gl9VAZrqVOxsZuTirg8TTS0qAQs5FIPF0BhYXFkgog/zg
7gJlq5SXpaWVF4O9lZKuXl6eVl4AZLIfKS82LzYuB2nlOFxWXl5ubA6ytm1KWU65
cXExkMl09lNNR3q5eTFQPYfHE7YT6cXlJgcYGI7cPMAOMtKhgcH9wE8FBuPycgOG
BoYKtl8ODL4gjccY2HSAfr4BVMvgAwyazwwsXSA7ORgY2BQYeH+Cw+sAKPo5wEHj
kQAO/GZwIIHDgc0AaxQSBAAFOXD7bgIAAA==
}
random/seed now
the-score: 0
board: reduce ['image 300x20 skier-right black]
for i 1 20 1 [
    pos: random 600x540
    pos: pos + 0x300
    append board reduce ['image pos tree black]
]
view center-face layout/tight [
    scrn: box white 600x440 effect [draw board] rate 0 feel [
        engage: func [f a e] [
            if a = 'key [
                if e/key = 'right [
                    board/2: board/2 + 5x0
                    board/3: skier-right
                ]
                if e/key = 'left [
                    board/2: board/2 - 5x0
                    board/3: skier-left
                ]
                show scrn
            ]
            if a = 'time [
                new-board: copy []
                foreach item board [
                    either all [
                        ((type? item) = pair!) 
                        ((length? new-board) > 4)
                    ] [ 
                        append new-board (item - 0x5) 
                    ] [
                        append new-board item
                    ]
                    coord: first back back (tail new-board)
                    if ((type? coord) = pair!) [
                        if ((second coord) < -60) [
                            remove back tail new-board
                            remove back tail new-board
                            remove back tail new-board
                            remove back tail new-board
                        ]
                    ]
                ]
                board: copy new-board
                if (length? new-board) < 84 [
                    column: random 600
                    pos: to-pair rejoin [column "x" 440]
                    append board reduce ['image pos tree black]
                ]
                collision-board: remove/part (copy board) 4
                foreach item collision-board [
                    if (type? item) = pair! [
                        if all [
                          ((item/1 - board/2/1) < 15)
                          ((item/1 - board/2/1) > -40)
                          ((board/2/2 - item/2) < 30)
                          ((board/2/2 - item/2) > 5)
                        ] [
                            alert "Ouch - you hit a tree!"
                            alert rejoin ["Final Score: " the-score]
                            quit
                        ]
                    ]
                ]
                the-score: the-score + 1 
                score/text: to-string the-score
                show scrn
            ]
        ]
    ]
    origin across h2 "Score:" 
    score: h2 bold "000000"
    do [focus scrn]
]

9. Creating Web Applications using REBOL CGI

In order to create CGI applications for a web site, you need an available web server, and the REBOL interpreter must be uploaded and installed on your web server. This project contains the Uniform miniserver with Rebol all set up to test CGI scripts on Windows.

9.1 HTML Forms and Server Scripts - the Basic CGI Model

The following HTML contains a form with a text entry field and a button, and an "action" pointing to a specified URL:

<HTML>
    <HEAD><TITLE>Data Entry Form</TITLE></HEAD>
    <BODY>
        <FORM ACTION="http://yourwebserver.com/your_rebol_script.cgi">
            <INPUT TYPE="TEXT" NAME="username" SIZE="25">
            <INPUT TYPE="SUBMIT" NAME="Submit" VALUE="Submit">
        </FORM>
    </BODY>
</HTML>

Save the following script at the action URL specified above. The script decodes the submitted data, assigns that data the label 'submitted, and prints output directly to the user's web browser. Remember that curly brackets are used in Rebol for multiline string content:

#!/home/your_user_path/rebol/rebol -cs
REBOL []
print {content-type: text/html^/}  
submitted: decode-cgi read-cgi

print rejoin [
    {<HTML><HEAD><TITLE>Page title</TITLE></HEAD><BODY>}
    {Hello } second submitted {!}
    {</BODY></HTML>}
]

For the above code to run on your web server, a REBOL interpreter must be found in the path specified by "/home/your_user_path/rebol/rebol -cs" (i.e., if the interpreter is in the same path as the script, that line should read "#!./rebol -cs"). The decode-cgi read-cgi line is the key to retrieving data submitted by HTML forms (above, that data block is assigned the label 'submitted).

9.2 A Standard CGI Template to Memorize

Most CGI programs typically examine the submitted data, and if it's empty (i.e., no data has been submitted), the program prints an initial HTML form to retrieve input from the user. Otherwise, the submitted data is manipulated as needed, and output is printed to the user's web browser in the form of a new HTML page. Here's a basic example of that whole process, all in one script:

#!/home/your_user_path/rebol/rebol -cs
REBOL []
print {content-type: text/html^/}
submitted: decode-cgi read-cgi

; The 4 lines above are the standard REBOL CGI headers.
; The line below prints the standard HTML, head and body
; tags to the visitor's browser:

print {<HTML><HEAD><TITLE>Page title</TITLE></HEAD><BODY>}

; Next, determine if any data has been submitted.
; Print the initial form if empty.  Otherwise, process 
; and print out some HTML using the submitted data.  
; Finally, print the standard closing "body" and "html"
; tags, which were opened above:

either empty? submitted [
    print {
        <FORM METHOD="POST">
        <INPUT TYPE="TEXT" NAME="username" SIZE="25">
        <INPUT TYPE="SUBMIT" NAME="Submit" VALUE="Submit">
        </FORM>
        </BODY></HTML>
    }
] [ 
    print rejoin [{Hello } submitted/2 {!}]
    print {</BODY></HTML>}
]

10. Example CGI Applications

10.1 Generic CGI App, With HTML Form

Here is a basic CGI template that prints a form for user data entry. A demo of this script is available at http://guitarz.org/rebol3:

#!./rebol -cs
REBOL [title: "Generic CGI Application, With HTML Form"]
print {content-type: text/html^/}
print {<HTML><HEAD><TITLE>Page title</TITLE></HEAD><BODY>}
submitted: decode-cgi read-cgi
either empty? submitted [
    print {
        <FORM METHOD="POST">
        <INPUT TYPE="TEXT" NAME="username" SIZE="25">
        <INPUT TYPE="SUBMIT" NAME="Submit" VALUE="Submit">
        </FORM>
        </BODY></HTML>
    }
] [ 
    print rejoin [{Hello } submitted/2 {!}]
    print {</BODY></HTML>}
]

10.2 CGI Photo Album

Here's a simple CGI program that displays all photos in the current folder on a web site, using a foreach loop. A demo of this script is available at http://guitarz.org/rebol3/photos.cgi:

#!./rebol -cs
REBOL [title: "CGI Photo Album"]
print {content-type: text/html^/}
print {<HTML><HEAD><TITLE>Photos</TITLE></HEAD><BODY>}
folder: read %./
count: 0
foreach file folder [
    foreach ext [".jpg" ".gif" ".png" ".bmp"] [
        if find file ext [
            print [<BR> <CENTER>]
            print rejoin [{<img src="} file {">}]
            print [</CENTER>]
            count: count + 1
        ]
    ]
]
print join {<BR>Total Images: } count
print {</BODY></HTML>}

10.3 CGI Text Chat

Here's a short web chat app. Users can type the word "erase" in the name field to delete all former texts. A demo of this script is available at http://guitarz.org/rebol3/chat.cgi:

#!./rebol -cs
REBOL [title: "Group Chat"]
print {content-type: text/html^/}
url: %./chat.txt
write/append url ""
submitted: decode-cgi read-cgi
if submitted/2 = "erase" [write url ""]
if submitted/2 <> none [
    write/append url rejoin [
        now " (" submitted/2 "):  " submitted/4 "^/^/"
    ]
]
notes: copy read url
print rejoin [
    "<pre>" notes "</pre>"
    {<FORM METHOD="POST">
        Name:<br>
        <input type=text size="65" name="username"><br>
        Message:<br>
        <textarea name=message rows=5 cols=50></textarea><br>
        <input type="submit" name="submit" value="Submit">
    </FORM>}
]

10.4 A Generic Drop Down List Application

The following example demonstrates how to automatically build lists of days, months, times, and data read from a file, using dynamic loops (foreach, for, etc.). The items are selectable from drop down lists in the printed HTML form. A demo of this script is available at http://guitarz.org/rebol3/drop.cgi::

#!./rebol3 -cs
REBOL [title: "Dropdown Lists"]
print {content-type: text/html^/}
print {<HTML><HEAD><TITLE>Dropdown Lists</TITLE></HEAD><BODY>}
submitted: decode-cgi read-cgi
if not empty? submitted [
    print rejoin [{NAME SELECTED: } submitted/2 {<BR><BR>}]
    selected: rejoin [
        {TIME/DATE SELECTED: }
        submitted/4 { } submitted/6 {, } submitted/8
    ]
    print selected
    quit
]
; If no data has been submitted, print the initial form:
print {<FORM METHOD="POST">SELECT A NAME:  <BR> <BR>}
names: read/lines %users.txt
print {<select NAME="names">}
foreach name names [prin rejoin [{<option>} name]]
print {</option> </select> <br> <br>}
print { SELECT A DATE AND TIME: }
print rejoin [{(today's date is } now/date {)} <BR><BR>]
print {<select NAME="month">}
foreach m system/locale/months [prin rejoin [{<option>} m]]
print {</option> </select>}
print {<select NAME="date">} 
for daysinmonth 1 31 1 [prin rejoin [{<option>} daysinmonth]]
print {</option> </select>}
print {<select NAME="time">}
times: ["10am" "11am" "12pm" "1pm" "1:30pm" "4:15pm" "7:30pm"]
foreach time times [prin rejoin [{<option>} time]]
print {</option> </select><br> <br>}
print {<INPUT TYPE="SUBMIT" NAME="Submit" VALUE="Submit"></FORM>}

The "users.txt" file used in the above example may look something like this:

nick
john
jim
bob

11. More Rebol Fundamentals

11.1 Data Types

Rebol recognizes more that 40 native data types, and new ones can be defined by the user. The 'type function determines a value's data type. Here are a few examples:

a-string: "Hello World"                    type? a-string
an-integer: 3874904                        type? an-integer
a-decimal: 7348.39                         type? a-decimal    
web-site: http://musiclessonz.com          type? web-site 
email-address: user@website.com            type? email-address      
the-file: %/c/myfile.txt                   type? the-file 
money-amount: $343.56                      type? money-amount
color-tuple: 123.54.212                    type? color-tuple
a-character: #"z"                          type? a-character
a-word: 'asdf                              type? a-word                   
html-tag: <br>                             type? html-tag    
binary-info:  #{ddeedd}                    type? binary-info    
image: load http://rebol.com/view/bay.jpg  type? image
a-sound: load %/c/windows/media/tada.wav   a-sound/type

Here are some casting examples, which convert data from one type to another:

to-decimal an-integer     ; Convert values TO different types ("cast")
to-string web-site        ; now the web site URL is surrounded by quotes
form web-site             ; "form" also converts various values to string
form $29.99
alert form $29.99         ; the alert function REQUIRES a string parameter
alert $29.99              ; (this throws an error)
5 + 6                     ; you can perform math operations with integers
"5" + "6"                 ; (error) you can't perform math with strings
(to-integer "5") + (to-integer "6")    ; this eliminates the math problem
to-pair [12 43]           ; creates a coordinate pair
as-pair 12 43             ; a better way to create a coordinate pair
to-binary 123.54.212      ; convert a REBOL color value to hex color value
to-binary request-color   ; convert the color chosen by the user, to hex
to-tuple #{00CD00}        ; convert a hex color value to REBOL color value
form to-tuple #{00CD00}   ; covert the hex color value to a string
write/binary %floorplan8.pdf debase read clipboard://  ; email attachment

REBOL computes values appropriately for type:

6:30am + 00:37:19
now  
now + 0:0:59  
now - 10   
23x54 + 19x31
22x66 * 2
22x66 * 2x3
192.168.1.1 + 0.0.0.37
11.22.33.44 * 9        ; note that each IP segment value is limited to 255
0.250.0 / 2            ; an easy way to adjust color values
$29.99 * 5
x: 12  y: 33  q: 18  p: 7
(as-pair x y) + (as-pair q p)  ; very common in graphics apps using coords
remove form to-money 1 / 233
remove/part form to-time (1 / 233) 6

Random data values can be generated, appropriately according to type:

random/seed now/time   ; always use this line to get real random values
random 50              ; a random number between 0 and 50
random 50x100          ; left side is limited to 50, right limited to 100
random 222.222.222     ; each segment is limited to #s between 0 and 222
random $500
random "asdfqwerty"    ; a random mix of the given characters
random [1 2 3 4 5]     ; a randomly sorted list of values

11.2 A Variety of Additional Short Series Operation Examples

Here are a variety of series operation examples:

new-block: copy []  ; a new, empty block
some-names: ["John" "Bill" "Tom" "Mike"]   ; a list of text strings
more-names: copy some-names   ; a copy of the above list
probe more-names
same? more-names some-names   ; NOT the exact same list, but a copy
same-names: some-names   ; THESE labels now refer to the EXACT SAME list
probe same-names
same? same-names some-names ; change some-names and same-names CHANGES TOO
sortednames: sort copy some-names  ; "copy" keeps some-names from changing
sortednames: sort some-names  ; here, the some-names block has been sorted
print first sortednames  ; here are 3 different ways to pick the 1st item:
print sortednames/1 
print pick sortednames 1 
print second sortednames   ; 3 different ways to pick the 2nd item:
print sortednames/2
print pick sortednames 2
find some-names "John"
first find some-names "John"
find/last some-names "John" 
select some-names "John"   ; use series like dictionaries
reverse sortednames 
length? sortednames 
head sortednames 
next sortednames 
back sortednames 
last sortednames 
tail sortednames  
at sortednames 2
skip sortednames 1
extract sortednames 3  ; every third item
index? sortednames 
insert (at sortednames 3) "Lee" 
append sortednames "George" 
remove sortednames 
remove find sortednames "Mike" 
change sortednames "Phil" 
change third sortednames "Phil"
poke sortednames 3 "Phil" 
copy/part sortednames 2 
replace/all sortednames "Lee" "Al"
probe form sortednames
probe mold sortednames
join some-names sortednames 
intersect sortednames more-names  
difference sortednames more-names 
exclude sortednames more-names 
union sortednames more-names 
unique sortednames 
clear sortednames
empty? sortednames
probe same-names
probe more-names

index-num: length? more-names
print pick more-names index-num
print pick more-names (index-num - 1)
print pick more-names (random length? more-names)

index-num: ((index? (find more-names "Tom")) - 1)
print pick more-names index-num ; 4 ways to pick items at a variable index
print more-names/:index-num
print compose [more-names/(index-num)]
print reduce [more-names/(index-num)]

"READ" reads data byte-for-byte from file, "LOAD" performs a CONVERSION. "WRITE" writes series byte-for-byte to file, "SAVE" performs CONVERSION:

save %names.txt more-names         ; series data saved to a text file
loaded-names: load %names.txt      ; use "load" to read it into a variable
write %names2.txt mold more-names  ; series saved but WITH SQUARE BRACKETS
loaded-names2: load %names2.txt    ; "load" also correctly loads that file
read-names: to-block read %names.txt  ; "read" requires "to-block" convert
read-names2: to-block read %names2.txt  ; block within a block
probe read-names2                       ; [["John" "Phil" "Tom" "Mike"]]
first read-names2                       ; ["John" "Phil" "Tom" "Mike"]

Working with compressed blocks of data:

write/binary %compressed.dat compress mold more-names  ; compress and save
probe load decompress read/binary %compressed.dat  ; read and decompress
save %compressed.dat compress mold more-names  ; another way to save
probe load decompress load %compressed.dat  ; and load compressed series

Incrementing a counter (produces similar functionality to 'repeat and 'for):

count: 0
foreach item items [
    count: count + 1
    print rejoin ["^/Item #" count ": " item]
]

A nested block:

big-block: [
    [may june july] 
    [ 
        [1 2 3] 
        [
            [yes no]
            [monday tuesday friday]
        ]
    ]
]

Indentation makes the block easier to read, but is not required:

big-block: [[may june july][[1 2 3][[yes no][monday tuesday friday]]]]

probe first big-block          ; 3 ways to get the first item in the block
probe big-block/1
probe pick big-block 1
probe second big-block        ; 3 ways to get the second item in the block
probe big-block/2
probe pick big-block 2 
probe first second big-block     ; 1st block in the 2nd block in big-block
probe big-block/2/1
probe second second big-block    ; 2nd block in the 2nd block in big-block
probe big-block/2/2
probe first second second big-block
probe big-block/2/2/1
probe second second second big-block
probe big-block/2/2/2
probe big-block/2/2/2/1
probe big-block/2/2/2/2
probe big-block/2/2/2/3

A block of contact info:

users: [
    "John Smith" "123 Tomline Lane Forest Hills, NJ" "555-1234"
    "Paul Thompson" "234 Georgetown Pl. Peanut Grove, AL" "555-2345"
    "Jim Persee" "345 Pickles Pike Orange Grove, FL" "555-3456"
    "George Jones" "456 Topforge Court Mountain Creek, CO" ""
    "Tim Paulson" "" "555-5678"
]

Sort a copy of the block by the first column (groups of every 3 consecutive items), Without the 'copy, the original list is permanently changed:

probe sort/skip copy users 3

To sort by any other column, first convert the block to a nested block of blocks (rows of column data):

new-users: copy []
foreach [name address phone] users [
    append/only new-users reduce [name address phone]
] ; append/only inserts blocks as blocks, instead of as individual items
editor new-users

Sort ascending by 2nd field (by address):

field: 2  sort/compare new-users func [a b] [(at a field) < (at b field)]
editor new-users

Sort descending:

field: 2  sort/compare new-users func [a b] [(at a field) > (at b field)]
editor new-users

"Flatten" the block back to it's orginal state (no nested blocks of blocks):

users: copy []
foreach block new-users [append users reduce block]
editor users

Extract columns:

probe extract users 3           ; the name column (every 3rd item)
probe extract/index users 3 2   ; address column (skip 3, starting on 2)
probe extract/index users 3 3   ; phone column (skip 3, starting on 3)

Convert entire block to a string:

probe form users

Get groups of 3 consecutive items:

foreach [name address phone] users [ 
    print rejoin [
        "^/Name:     " name 
        "^/Address:  " address 
        "^/Phone:    " phone 
    ]
]

Print phone numbers for all names that contain the letter "a":

foreach name (extract users 3) [
    if find name "a" [
        print pick users ((index? find users name) + 2)
    ]
]

Append to end of list:

append users ["Joe Thomas" "" "555-321-7654"]

Append variable values ('repend is 'reduce + 'append)

name: "Alex Sharp"  address: "937 Boll Rd"  phone: "555-294-2834"
repend users [name address phone]

Insert at a specific index:

insert (at users 4) [
    "Tom Adams" "321 Way Lane Villageville, AZ" "555-987-6543"
]

Remove single and multiple items:

remove (at users 4)  ; remove 1 item
remove/part (at users 4) 2  ; remove 2 items
change (at users 1) "Jonathan Smith"
remove (at users 1) insert (at users 1) "Jonathan Smith"
foreach item users [
    replace item "John Smith" "Jonathan Smith"
]

Retrieve single and multiple items:

copy/part users 3
copy/part (at users 4) 3
copy at tail users -3
copy/part (at users 7) 3
copy/part (find users "Jim Persee") -3
copy/part (skip (find users "Jim Persee") -6) 3
alert form (copy/part users 3)

chosen: request-list "Choose a person: " (extract users 3)
alert form reduce [copy/part find users chosen 3]
alert reform [copy/part find users chosen 3]
chosen: request-list "Choose an address: " (extract/index users 3 2)
alert reform [copy/part at (find users chosen) -1 3]

Move item positions:

x: ["one" "two" "three" "four" "five" "six"]
move/to (find x "five") 2
print x               ; item position changed

x: ["asdf" "qwer" "zxcv" "uiop" "hjkl" "vbnm"]
y: head clear skip tail x -2
probe y               ; last 2 items removed

Nested blocks:

data: [
    1 2 3 [4 5 6]
    7 8 9 [0 9 8 7 6 5 4 3 2 1]
    3 4 5 [6 3 1 7 8 0] 
]
counter: 1
foreach [col1 col2 col3 col4] data [
    print rejoin [
        "Row: " counter newline
        "Column1: " col1 newline
        "Column2: " col2 newline
        "Column3: " col3 newline
        "Column4 (sorted): " (sort col4) newline newline
    ]
    counter: counter + 1
]

Directories are just lists of files - all series operations work:

foreach file read %. [
    if (suffix? file) = %.tester [
        rename file to-file (replace to-string file ".tester" ".test")
    ]
]
list-dir

foreach line reverse copy system/console/history [print line]

Here's a useful unique string generator demonstrating the 'replace function

replace/all replace/all replace/all replace/all form now/precise trim {
    /} "" ":" "x" "-" "q" "." ""

11.3 Strings

Strings of text work just like blocks of data items - they're just a list of characters. Use all the same functions used to manipulate items in a block, to manipulate the list of characters in a string of text:

the-string: "abcdefghijklmnopqrstuvwxyz"

; 3 different ways to get just the 7th character:

pick the-string 7
the-string/7 
seventh the-string

; get the left 7 characters of the string:

copy/part the-string 7

; get the right 7 characters of the string:

copy at tail the-string -7

; get 7 characters from the middle of the string,
; starting with the 12th character:

copy/part (at the-string 12) 7

; get 7 characters from the middle of the string,
; starting 7 characters back from the letter "m":

copy/part (find the-string "m") -7

; get 7 characters from the middle of the string,
; starting 12 characters back from the letter "t":

copy/part (skip (find the-string "t") -12) 7

; Change "cde" to "123"

replace the-string "cde" "123"

; Several ways to change the 7th character to "7"

change (at the-string 7) "7"
poke the-string 7 #"7"  ; the pound symbol refers to a single character
poke the-string 7 (to-char "7")  ; another way to use single characters
print the-string

; Remove 15 characters, starting at the 3rd position:

remove/part (at the-string 3) 15
print the-string

; Insert 15 characters, starting at the 3rd position:

insert (at the-string 3) "cdefghijklmnopq"
print the-string

; Insert 3 instances of "-+" at the beginning of the string:

insert/dup head the-string "-+ " 3
print the-string

; Replace every instance of "-+ " with " ":

replace/all the-string "-+ "  " "
print the-string

; Get every third character from the string:

extract the-string 3

11.4 File Management and Reading/Writing From/To Varied Data Sources/Protocols

Here are some function examples which demonstrate file management operations:

rename %temp.txt %temp2.txt       ; change file name
write %temp.txt read %temp2.txt   ; copy file
write/append %temp2.txt ""   ; create file (or if it exists, do nothing)
delete %temp2.txt
change-dir %../
what-dir 
list-dir
make-dir %./temp
print read %./
osfile: to-local-file filename   ; REBOL uses its own multiplatform syntax
to-rebol-file osfile  ; Convert from native OS file notation back to REBOL
the-url: http://website.com/subfolder
split-path the-url   ; "split-path" breaks any file or URL into 2 parts

Here are some examples of reading and writing data to and from various data sources and protocols:

print read http://rebol.com  ; "read" retrieves the data from many sources
editor http://rebol.com   ; the built in editor can also read many sources
print read %./   ; the % symbol is used for local files and folders
editor %./
write %temp.txt "test"   ; write takes TWO parameters (file name and data)
editor %temp.txt
editor request-file/only   ; "only" refinement limits choice to 1 file
write clipboard:// (read http://rebol.com)  ; 2nd parameter in parentheses
editor clipboard://
print read dns://msn.com   ; REBOL can read many built in protocols
print read nntp://public.teranews.com 
write/binary %/c/bay.jpg (read/binary http://rebol.com/view/bay.jpg)
write/binary %tada.wav (read/binary %/c/windows/media/tada.wav)
write/binary %temp.dat (compress read http://rebol.com)  ; COMPRESS DATA
print decompress read/binary %temp.dat                   ; DECOMPRESS DATA
print read ftp://user:pass@website.com/name.txt       ; user/pass required
write ftp://user:pass@website.com/name.txt "text"     ; user/pass required
editor ftp://user:pass@website.com/name.txt  ; can save changes to server!
editor pop://user:pass@website.com    ; read all emails in this account
send user@website.com "Hello"   ; send email
send user@website.com (read %file.txt)   ; email the text from this file
send/attach user@website.com "My photos" [%pic1.jpg %pic2.jpg pic3.jpg]

11.5 More About User Defined Functions

Any string or block of data can be treated like a function, using 'do:

some-actions: [
    alert "Here is one action." 
    print "Here's a second action."
    write %/c/anotheraction.txt "Here's a third action."
]
do some-actions

Copy, paste and run any code using 'do:

write clipboard:// {alert "This code was run from the clipboard"}
do read clipboard://

write %some-code.r {
    REBOL [] ; executable code saved to a file must begin with this header
    print rejoin [newpage "The code in %some-code.r just ran." newline]
}
do %some-code.r

write ftp://user:pass@site.com/public_html/some-code.r {
    REBOL []
    print "The code in http://site.com/some-code.r just ran."
}
do http://site.com/some-code.r

You can 'do any rejoined text:

the-word: to-word request-list "choose a word:" (first system/words)
do rejoin ["help " the-word]

'Does is a 'func shortcut, when no parameter list is required:

cls: does [prin "^(1B)[J"]          ; same as "prin newpage"
cls

By default, variables in functions are GLOBAL:

x: 10
change-x-globally: func [y z] [x: y + z]
change-x-globally 10 20
print x

You can change that default behavior with the /local option:

x: 10
change-x-locally: func [y z /local x] [x: y + z]
change-x-locally 10 20
print x

Here's how to include refinement options in your functions:

compute: func [x y /multiply /divide /subtract] [
    if multiply [return x * y]
    if divide   [return x / y]
    if subtract [return x - y]
    return x + y
]
compute/multiply 10 20
compute/divide 10 20
compute/subtract 10 20
compute 10 20

Here's how to include function documentation (used by 'help), and require data types for parameters:

concatenate-string-or-num: func [
    "This function will only concatenate strings or integers."
    val1 [string! integer!] "First string or integer"
    val2 [string! integer!] "Second string or integer"
] [
    join val1 val2
]
help concatenate-string-or-num
concatenate-string-or-num "Hello " "there."  ; this works correctly
concatenate-string-or-num 10 20              ; this works correctly
concatenate-string-or-num 10.1 20.3          ; this creates an error

Using 'do for metaprogramming:

do [
    print "^/This example builds a line of code, and then executes it.^/"
    function: ask "Enter a function, such as 'print' or 'editor':  "
    parameter: ask "Enter a parameter, such as some random text:  "
    print rejoin [function { "} parameter {"}]
    do rejoin [function { "} parameter {"}]
    do compose [(to-word function) (parameter)]
    print "That's a very simple way to accomplish metaprogramming tasks."
]

Save functions to library files. Import the libraries with 'do:

write %imported-func.r {
    REBOL [title: "play-sound"]
    play-sound: func [sound-file] [
        wait 0
        insert sound-port: open sound:// load sound-file
        wait sound-port
        close sound-port
    ]
}
do %imported-func.r
play-sound %/C/WINDOWS/Media/chimes.wav

Get the code of any function, as a text string:

editor mold :read-cgi

Redefine a built-in (mezzanine) function:

request: do replace/all mold :request "bold" ""
request/ok/type "An alert without bold text" 'alert

11.6 GUI Event Management

"Feel" and "Engage" together detect events:

view layout [
    text "Mouse me." feel [
        engage: func [face action event] [
            if action = 'up [print "You just released the mouse."]
        ]
    ]
]

The variables 'f, 'a, and 'e are shortcuts used to refer to face, action, and event. For example, e/offset gets the coordinate position of a mouse click:

print "Click anywhere in the window, then click the text."
view center-face layout [
    size 400x200
    box 400x200 feel [
        engage: func [f a e] [
            print rejoin ["Mouse " a " at " e/offset]
        ]
    ]
    origin 
    text "Click me" [print "Text clicked"] [print "Text right-clicked"]
    box blue [print "Box clicked"]
]

Here's some generic click and drag code:

movestyle: [
    engage: func [f a e] [
        if a = 'down [initial-position: e/offset]
        if find [over away] a [
            f/offset: f/offset + e/offset - initial-position
            show f
        ]
    ]
]
view layout [
    size 600x400
    text "This is some text" feel movestyle
]

This drag code ensures that the dragged item is layered as the top face in the z-order of the layout:

movestyle: [
    engage: func [f a e] [
        if a = 'down [
            initial-position: e/offset
            remove find f/parent-face/pane f
            append f/parent-face/pane f
        ]
        if find [over away] a [
            f/offset: f/offset + (e/offset - initial-position)
        ]
        show f
    ]
]
view layout/size [
    style moveable-object box 20x20 feel movestyle
    at random 600x400 moveable-object (random 255.255.255)
    at random 600x400 moveable-object (random 255.255.255)
    at random 600x400 moveable-object (random 255.255.255)
    at random 600x400 moveable-object (random 255.255.255)
    at random 600x400 moveable-object (random 255.255.255)
    text "This text and all the boxes are movable" feel movestyle
] 600x440

This example follows all mouse movements:

view center-face layout [
    size 600x440
    at 270x209 b: btn "Click Me!" feel [
        detect: func [f e] [
            if e/type = 'move [
                if (within? e/offset b/offset 59x22) [
                    b/offset: b/offset + ((random 50x50) - (random 50x50))
                    if not within? b/offset -59x-22 659x462 [
                        b/offset: 270x209
                    ]
                    show b
                ]
            ]
            e
        ]
    ]
]

To trap other events, use 'insert-event-func. This example traps and responds to close events:

insert-event-func [
    either event/type = 'close [
        really: request "Really close the program?"
        if really = true [unview]
    ] [event]  ; always return other events
]
view center-face layout [size 600x400]

This example traps resize events:

insert-event-func [
    either event/type = 'resize [
        fs: t1/parent-face/size
        t1/offset: fs / 2x2
        t2/offset: t1/offset - 50x25
        t3/offset: t1/offset - 25x50
        show gui  none
    ] [event]
]
svv/vid-face/color: white
view/options gui: layout [
    across
    t1: text "50x50"
    t2: text "- 50x25"
    t3: text "- 25x50"
] [resize]

11.7 More Various GUI Examples, Techniques, Widgets (some grids), Etc.

Some more built in widget styles:

print "GUI Output:^/"
view layout [
    h1 "Some More GUI Widgets:"
    box red 500x2
    drop-down 200 data system/locale/months [
        a/text: join "Month:  " value show a
    ]
    a: field
    slider 200x18 [bar1/data: value show bar1]
    bar1: progress
    scroller 200x16 [bar2/data: value show bar2]
    bar2: progress
    across 
    toggle "Click here" "Click again" [print value]
    rotary "Click" "Again" "And Again" [print value]
    choice "Choose" "Item 1" "Item 2" "Item 3" [print value]
    return
    x: radio y: radio z: radio
    btn "Get Radio States" [print [x/data y/data z/data]]
    return
    led
    arrow
    below
    code "Code text"
    tt "Typewriter text"
    text "Little Text" font-size 8
    title "Centered title" 500
]

You can fit images on most widgets:

view layout [
    area 400x400 load http://rebol.com/view/bay.jpg effect [
        Fit Flip Emboss 
    ]
]

And there are many more effects:

effects:  [
  invert contrast 40 colorize 0.0.200 gradcol 1x1 0.0.255 255.0.0 tint 100
  luma -80 multiply 80.0.200 grayscale emboss flip 0x1 flip 1x0 rotate 90
  reflect 1x1 blur sharpen aspect tile tile-view
]

Gradients (color fades) can be applied as an effect:

view layout [area effect [gradient red blue]]

view layout [
    size 500x400
    backdrop effect [gradient 1x1 tan brown]
    box effect [gradient 123.23.56 254.0.12]
    box effect [gradient blue gold/2]
]

Switching between separate panes on a single window layout:

gui: layout [
    across
    btn "Fields"      [window/pane: pane1 show window]
    btn "Text List"   [window/pane: pane2 show window]
    return
    window: box 400x200
]
pane1: layout/tight [field 400 field 400 area 400]
pane2: layout/tight [text-list 400x200 data system/locale/days]
window/pane: pane1
view center-face gui

Changing the global face color property:

svv/vid-face/color: white
alert "New global background color is now white."

Responding to keys:

view center-face layout [
    size 600x440 
    h3 "Press the left or right arrow key"
    key keycode [left]  [alert "You pressed the LEFT arrow key"]
    key keycode [right] [alert "You pressed the RIGHT arrow key"]
    btn #"a" "Click Me or Press the 'a' Key" [alert "clicked or pressed"]
]

Here's a useful scipt to show all key codes:

insert-event-func func [f e] [if e/type = 'key [print mold e/key] e]
view layout [text "Type keys to see their character/keycode"]

How to refer to the main layout window:

view gui: layout [
    btn1: btn "Button 1"
    btn2: btn "Remove all widgets from window" [
        foreach item system/view/screen-face/pane/1/pane [
            remove find system/view/screen-face/pane/1/pane item
        ]
        show gui
    ]
]

Graphic collision detection using the 'within function:

view center-face layout [
    size 400x400
    btn1: btn red
    at 175x175 btn2: btn green
    box 0x0 rate 0 feel [engage: func [f a e] [if a = 'time [
        btn1/offset: btn1/offset + 5x5
        show btn1
        if within? btn1/offset btn2/offset 1x1 [alert "Collision" unview]
    ]]]
]

A simpler collision example using the 'overlap function:

gui: view/new center-face layout [
    size 400x400
    btn1: btn red
    at 175x175 btn2: btn green
]
forever [
    wait .02
    btn1/offset: btn1/offset + 5x5  show gui 
    if overlap? btn1 btn2 [alert "Collision"]
    if btn1/offset/1 > 300 [alert "done" quit]
]

Use "to-image" to create a screen shot of any layout:

picture: to-image layout [
    page-to-read: field "http://rebol.com"
    btn "Display HTML"
]
save/png %layout.png picture     ; save the image to a file
browse %layout.png

A simple info screen:

flash "Waiting..."  wait 3  unview
inform layout [btn "Click Me" [flash "Waiting..." wait 3 unview]]

Here are all the main GUI words that you should get to know:

VID-STYLES--GUI-WIDGETS: [
    face blank-face IMAGE BACKDROP BACKTILE BOX BAR SENSOR KEY BASE-TEXT
    VTEXT TEXT BODY TXT BANNER VH1 VH2 VH3 VH4 LABEL VLAB LBL LAB TITLE
    H1 H2 H3 H4 H5 TT CODE BUTTON CHECK CHECK-MARK RADIO CHECK-LINE
    RADIO-LINE LED ARROW TOGGLE ROTARY CHOICE DROP-DOWN ICON FIELD INFO
    AREA SLIDER SCROLLER PROGRESS PANEL LIST TEXT-LIST ANIM BTN BTN-ENTER
    BTN-CANCEL BTN-HELP LOGO-BAR TOG
]
LAYOUT-WORDS: [
    return at space pad across below origin guide tab tabs indent style
    styles size backcolor backeffect do
]
STYLE-FACETS--ATTRIBUTES: [
    edge font para doc feel effect effects keycode rate colors texts help
    user-data with bold italic underline left center right top middle
    bottom plain of font-size font-name font-color wrap no-wrap as-is
    shadow frame bevel ibevel
]
SPECIAL-STYLE-FACETS: [
    ARROW: [up right down left]  ROTARY: data  CHOICE: data  DROP-DOWN: 
    [data rows]  FIELD: hide  INFO: hide  AREA: hide  LIST: [supply map
    data]  TEXT-LIST: data  ANIM: [frames rate]
]

The especially powerful 'list widget can create data grids, not just from text fields, but from ANY types of widgets:

y: read %.   c: 0   x: copy []
foreach i y [append/only x reduce [(c: c + 1) i (size? to-file i)]]
slider-pos: 0
view center-face layout [
    across space 0
    the-list: list 400x400 [
        across  space 0x0
        text 50 purple
        text 250 bold [editor read to-file face/text]
        text 100 red italic
        return box green 400x1
    ] supply [
        count: count + slider-pos
        if none? q: pick x count [face/text: none exit]
        face/text: pick q index
    ]
    scroller 16x400 [
        slider-pos: (length? x) * value
        show the-list
    ]
]

A more powerful grid example implemented using the 'list widget:

REBOL [title: "Table/Grid/Listview Example"]

headers: ["Numbers" "Text" "Dates"]

x: [
    [1   "1"   1-1-2012]
    [11  "11"  1-2-2012]
    [2   "2"   1-3-2012]
]

do decompress #{
789CC518C98EDB36F4AEAF78500FB11228B2074D0AA8B3A01F904BAF860EB245
D9CAD0A2225133720CFF7B1F77D24BE324456A60C626F9F69D1C5B4A86015E4A
3A922778B3199B7445D9FA1996F6670E8763118D21E004CB298735EBF6B02CCE
4EC9AEE3FB74E8CA3581A5B7C8615E447B8D3741B46674DCB5430E94B41BBE7D
822D292BD20F91FA36FC25F8E118F5A423258706341E2C23C04FD975A4ADC0C7
819E7C664D0BCB781B237C9C713271888BA83821CD9946F0B7A355734AEFCDB0
1F38D945949595DA22550EF5D8AE615995BC4CEB8612C87AF26524032FB4644D
0D7AC783CACD9E5C65ACA5FB4C625B8842A9C5B9309D26253ED672FA5B08E3B0
2CD8B065AF401BB9161A0FE50BF945320B56C2A6E1194C2885B45C4D4BFECBCD
56A3BDAE98CAC5303870D663C82C606682B29E929388331FC12908D265119C0B
529F610E33839EC22241D2211517C28E1E740DFEAB279835F00E3E271060845C
14B2B40826554FAA11D3CE91B2B09E8E3692FE3D68FE37775D720A29D75BE851
CAE9C47EDA78B5D51D819CD24E43119B4E8B7A72BAB29EA7CA4546DBBA21B432
0A92866F490F128CF55814726819F7D69E4062335BB35D57F618F79A5A092BA4
35C3DA5582A49CC02388E54A2F9594C50FD0B9BF484719CEF8B4889E0956E075
CF28350AAA555AEED8D85A4F0E69C7B01ACB2F0CBB00C6B85A1D3EBAF4C0EC58
6A44BB5584D0F798030666AE2B05CD842F0CB32CA01788AF7F0C14F5E8C9D07C
25E9A66F6C25EB48BF262D2F37C468213A970043DAB268672F0D79CD501942DA
B4C6369475654BB2452680E04F9194F3692E516983012BB6B345EE2FE02D383E
279077B9E598DD2131AFDF4948AE85E9D18A15AAE951F5B4F6BB4D92681369CC
126D54363D2C3EFA6C25CCD84AE564EEA314A0561D6F18561B212FD2142A032D
F76CE4E0357865CBC259B56E30D92B46861FB1A3C4F0DC03336B14A1A7AF750A
EF1709B22DAB2AA54D4B0CD3B014600B9E647F6E50890DE698A927A29567BCE1
58552A529723C5C6FE5755A9E163C082228BC46F79EC178F9D4F89BDF4E9DA45
B4E0040F22447BC2C7BE5501DA92D754910C6BFB597B083C278ABBAE478E80C2
8F639D15ED4030B7A5CB44EE4E5280C48777992B9CB363588AFF2343FD8D7439
0345348FE1E76C8407BC1F09DC3FDA0E6007AFBF150B88552B535A42FC141721
0D254A60095FFD33E52512A355FA5D7A7F123CEA9EEDBE2B38844B6E60A3C90B
BB7AC4D138B399A1206C970086CECC882E7792DBEDF929B4A625736E5161B20C
855126358089D5C66F0C3BAC5463D7511C08825E475E48FB84718B851F9BA1C8
75EC8F0C1BDFEBB6E1A2D2FA7B77BFCFDFCBBF0F1F9404122FD7E8EF5489370A
B6ACC52BC297DC28E2F31026CD4DB600991A6EEDA2DD8256BB20D4DD1C99CF3D
01E4A1EA2F6BDBBC3C1692F717116E44CCA6036D2A34F65E8C2DDA0CED062BBD
9D06B0DF12BF3B3762E701DE8C1DF635373D288238318AFB831E46826B442DF9
BB99643DAAF29CDF88E90B6070E1F1012ED5A230287CA94BCAD38ABD62540DBC
94C2E31493034E69753D104F3C214D8AF1A0653C0463573F7CC5D140F8B96E7A
8CD4966C4A8E898CB54F51C442EFD14F02648C3BE90ED36BC335621AEA01165A
7966D44E2CEEA5DD5B2860642E12ADF3299DF0EC766A9764F24FF0F7454ADBAB
525D3BB926D329BC91E8F2FE35796485D04E399DC44E288939C1E21EED2FBCAC
530112C4101C9CA78E36826FC691FC8E10AE2F86B748CC4B0187C11A6CDB6075
E11F5EB590A8854173DD9FB640F3A95828F5C905448C59A46A785064B04EF7AE
C4B093023363508A81D1543E6C0AB5AC69A26BE8CD5C6F190F19664AB59B4B58
7869D53532E873A691AAC3E0B6182AF9EDA1514EDD29CCA70FF3C88E856E86C6
A39919783FCEFD693A89BE3511FFD0A86DEE25105CF0641F066B66D1B5223DD8
B9195A154429B5151F4ABC350D628693CF5FE666211DAF59C36AE49CB5560B25
2476315861F83FAB1E0B2BECDCD1B71EBD9C2C6650B03E39887FDB23621E72C3
F888C301354F5B4396E319D48450701DF028E604317059979EB1928C0C79FBB6
2173F1DC44B9D156E916F7312CFE9850D72014A5B91FECD483D32366E2536C1F
19F797225CE5A1BE8EA9FB9A0B07FF2E2D6DBF2694828C5E0D2015B74969A1CF
FDF7B34E101F44D4AE90721C3D35507DF3702AE799C15E211C6139A5AA53F45A
7199BB71DC05971DF4C3801AF6C08E7DFAA299EBBBBE78E5A0DA7C3A2FFC7BF9
5B252884CED06E385EE3FA4CF6E26FCD2AA43A765884DC7304A40BF35A118089
F12404BC0CD761894BCF68DECDAF039F5356D0C7C87BF1B6CFC2764FBD093BD5
CC83F03FD2FC2B1641170000
}
view center-face gui: layout gui-block

Other options such as coloring rows, and specifying column data types, using the compressed grid code above, are demonstrated in the following example. This is a critically useful tool for data entry, visualization, and demonstration:

REBOL [title: "Table/Grid/Listview Example With Expanded Features"]

headers: ["Numbers" "TEXT (Note Sort)" "Dates"] ; REQUIRED COLUMN LABELS

; ALL THESE ADDITIONAL SETUP PARAMETERS ARE *** OPTIONAL ***:

x: [[1 "1" 1-1-2012][11"11"1-2-2012][2"2"1-3-2012]]  ; some default data

colors: [blue black red]    ; specify column colors like this
empty-space: 235            ; size of blank GUI area to appear below grid
svv/vid-face/color: white   ; default GUI face color

; Here's how to include GUI layout code to appear above the grid: 

gui-block: {
    h3 "RIGHT-CLICK/DRAG HEADERS TO RESIZE COLUMNS.  RESIZE WINDOW..."
    text "Click headers to sort (note that sort is DATA-TYPE SPECIFIC)."
    text "Notice Arrow Keys, PgUp/PgDn Keys, Scroll Bar, and highliting"
    text "Click any cell to edit data. Buttons load and save data to HD."
}

; The following line automatically fits grid to resized GUI window:

insert-event-func [either event/type = 'resize [resize-fit none] [event]]

do decompress #{
    789CC518C98EDB36F4AEAF78500FB11228B2074D0AA8B3A01F904BAF860EB245
    D9CAD0A2225133720CFF7B1F77D24BE324456A60C626F9F69D1C5B4A86015E4A
    3A922778B3199B7445D9FA1996F6670E8763118D21E004CB298735EBF6B02CCE
    4EC9AEE3FB74E8CA3581A5B7C8615E447B8D3741B46674DCB5430E94B41BBE7D
    822D292BD20F91FA36FC25F8E118F5A423258706341E2C23C04FD975A4ADC0C7
    819E7C664D0BCB781B237C9C713271888BA83821CD9946F0B7A355734AEFCDB0
    1F38D945949595DA22550EF5D8AE615995BC4CEB8612C87AF26524032FB4644D
    0D7AC783CACD9E5C65ACA5FB4C625B8842A9C5B9309D26253ED672FA5B08E3B0
    2CD8B065AF401BB9161A0FE50BF945320B56C2A6E1194C2885B45C4D4BFECBCD
    56A3BDAE98CAC5303870D663C82C606682B29E929388331FC12908D265119C0B
    529F610E33839EC22241D2211517C28E1E740DFEAB279835F00E3E271060845C
    14B2B40826554FAA11D3CE91B2B09E8E3692FE3D68FE37775D720A29D75BE851
    CAE9C47EDA78B5D51D819CD24E43119B4E8B7A72BAB29EA7CA4546DBBA21B432
    0A92866F490F128CF55814726819F7D69E4062335BB35D57F618F79A5A092BA4
    35C3DA5582A49CC02388E54A2F9594C50FD0B9BF484719CEF8B4889E0956E075
    CF28350AAA555AEED8D85A4F0E69C7B01ACB2F0CBB00C6B85A1D3EBAF4C0EC58
    6A44BB5584D0F798030666AE2B05CD842F0CB32CA01788AF7F0C14F5E8C9D07C
    25E9A66F6C25EB48BF262D2F37C468213A970043DAB268672F0D79CD501942DA
    B4C6369475654BB2452680E04F9194F3692E516983012BB6B345EE2FE02D383E
    279077B9E598DD2131AFDF4948AE85E9D18A15AAE951F5B4F6BB4D92681369CC
    126D54363D2C3EFA6C25CCD84AE564EEA314A0561D6F18561B212FD2142A032D
    F76CE4E0357865CBC259B56E30D92B46861FB1A3C4F0DC03336B14A1A7AF750A
    EF1709B22DAB2AA54D4B0CD3B014600B9E647F6E50890DE698A927A29567BCE1
    58552A529723C5C6FE5755A9E163C082228BC46F79EC178F9D4F89BDF4E9DA45
    B4E0040F22447BC2C7BE5501DA92D754910C6BFB597B083C278ABBAE478E80C2
    8F639D15ED4030B7A5CB44EE4E5280C48777992B9CB363588AFF2343FD8D7439
    0345348FE1E76C8407BC1F09DC3FDA0E6007AFBF150B88552B535A42FC141721
    0D254A60095FFD33E52512A355FA5D7A7F123CEA9EEDBE2B38844B6E60A3C90B
    BB7AC4D138B399A1206C970086CECC882E7792DBEDF929B4A625736E5161B20C
    855126358089D5C66F0C3BAC5463D7511C08825E475E48FB84718B851F9BA1C8
    75EC8F0C1BDFEBB6E1A2D2FA7B77BFCFDFCBBF0F1F9404122FD7E8EF5489370A
    B6ACC52BC297DC28E2F31026CD4DB600991A6EEDA2DD8256BB20D4DD1C99CF3D
    01E4A1EA2F6BDBBC3C1692F717116E44CCA6036D2A34F65E8C2DDA0CED062BBD
    9D06B0DF12BF3B3762E701DE8C1DF635373D288238318AFB831E46826B442DF9
    BB99643DAAF29CDF88E90B6070E1F1012ED5A230287CA94BCAD38ABD62540DBC
    94C2E31493034E69753D104F3C214D8AF1A0653C0463573F7CC5D140F8B96E7A
    8CD4966C4A8E898CB54F51C442EFD14F02648C3BE90ED36BC335621AEA01165A
    7966D44E2CEEA5DD5B2860642E12ADF3299DF0EC766A9764F24FF0F7454ADBAB
    525D3BB926D329BC91E8F2FE35796485D04E399DC44E288939C1E21EED2FBCAC
    530112C4101C9CA78E36826FC691FC8E10AE2F86B748CC4B0187C11A6CDB6075
    E11F5EB590A8854173DD9FB640F3A95828F5C905448C59A46A785064B04EF7AE
    C4B093023363508A81D1543E6C0AB5AC69A26BE8CD5C6F190F19664AB59B4B58
    7869D53532E873A691AAC3E0B6182AF9EDA1514EDD29CCA70FF3C88E856E86C6
    A39919783FCEFD693A89BE3511FFD0A86DEE25105CF0641F066B66D1B5223DD8
    B9195A154429B5151F4ABC350D628693CF5FE666211DAF59C36AE49CB5560B25
    2476315861F83FAB1E0B2BECDCD1B71EBD9C2C6650B03E39887FDB23621E72C3
    F888C301354F5B4396E319D48450701DF028E604317059979EB1928C0C79FBB6
    2173F1DC44B9D156E916F7312CFE9850D72014A5B91FECD483D32366E2536C1F
    19F797225CE5A1BE8EA9FB9A0B07FF2E2D6DBF2694828C5E0D2015B74969A1CF
    FDF7B34E101F44D4AE90721C3D35507DF3702AE799C15E211C6139A5AA53F45A
    7199BB71DC05971DF4C3801AF6C08E7DFAA299EBBBBE78E5A0DA7C3A2FFC7BF9
    5B252884CED06E385EE3FA4CF6E26FCD2AA43A765884DC7304A40BF35A118089
    F12404BC0CD761894BCF68DECDAF039F5356D0C7C87BF1B6CFC2764FBD093BD5
    CC83F03FD2FC2B1641170000
}

; APPEND ANY WIDGETS AND/OR GUI CODE TO APPEAR BELOW THE GRID, HERE:

append gui-block [

    ; REPLACE 'BTN' WITH 'KEY' TO HIDE BTNS AND STILL USE KEY SHORTCUTS.
    ; CHANGE/REMOVE BUTTONS AND/OR KEYBOARD SHORTCUTS AS NEEDED:

    text "" return
    btn "Insert (Ins)" keycode [insert] [add-line] 
    btn "Remove (Del)" #"^~" [remove-line]  
    btn "Move (CTRL+M)" #"^M" [move-line]
    btn "Grow (+)" #"+" [resize-grid 1.333]
    btn "Shrink (-)" #"-" [resize-grid .75]
    btn "Fit (CTRL+R)" #"^R" [resize-fit]
    return
    btn "Load Blocked (CTRL+O)" #"^O" [load-blocked/request %blocked.txt]
    btn "Save Blocked (CTRL+S)" #"^S" [save-blocked/request %blocked.txt]
    btn "Load Flat (CTRL+U)" #"^U" [load-flat/request %flat.txt]
    btn "Save Flat (CTRL+F)" #"^F" [save-flat/request %flat.txt]

; LOAD A DEFAULT DATA *FILE HERE (instead of specifying it in code above):
; Note that the "load-flat" and "save-flat" functions load "flat" blocks,
; which are simply long sequences of data values.  The "load-blocked" and
; "save-blocked" functions load block which have rows enclosed inside
; deliniated blocks.  All of those functions provide an optional
; "/request" refinement that allows the user to select a file:

    ;  do [load-blocked %blocked.txt] 

]
view/options center-face gui: layout gui-block [resize]

11.8 Embedding Files (images, sounds, binary executables, etc.) in Code

Use this script to embed files:

system/options/binary-base: 64
editor picture: compress to-string read/binary to-file request-file/only

Here's how you can use the data above:

view layout [image load (to-binary decompress picture)]

Here's an example of some image data created using the script above:

logo-pic: load to-binary decompress #{
789C018A0375FC89504E470D0A1A0A0000000D49484452000000640000001808
020000008360CFB90000001374455874536F667477617265005245424F4C2F56
6965778FD916780000033249444154789CD599217402310C86F7CE6227B1481C
1637874362B1382C1687C4A15168240A89C5A2B058ECDEBE47DFFA429276DCEE
10FDCD582F97267FD33F2D7CF47ABDCF32D1ED76E7F3F9ED76FB4EE0743A8D46
A3B6A683A80FFE540562381C1E8FC7144D12DBEDB6951C3B9D4E91648DC7E34C
41B925465D349C14A2CA230BA65EA729E27C3E37CCB43CB228905A3525B1DBED
9A4CED93851C7C193088A0667C0D0603FB5640BFDFB7F648C0D0836B1C41C22E
11D7EBF57038F074BFDF534429BE2693891B4626CE1C59BC7CB95CDC99EEF7FB
66B349F922D65A4B4A8DE0D0B547B9DD85212B6B4CB4D3E994B055FEE8943566
30134626BBDA64052C974BD757A637B1DA2E599959A05EE61F4032D62C55EFBC
6EED01878954188DC80AE714C07126D24F91BBBE6265A129B3D96C2A4085BB64
459FEBF51A1B2692E5A9FA17A428B562EBE595A1F29650AD5C6B9525FD4621E0
A95D73491606F9046C94101A06178B4518E19122023655DA184B03ECA15BE98E
6D9D302E536E8D2C96A5FF0061458FEE9EAA045958720EDCFC82CF145A9E2C7C
52BC6CF0503B8C2B2200DAACD24698A4B710361E6421930E05A85E9484BE51B3
0885AE9727CB22A5591981B73D1AC6A58D2ABD5892DF46C5993DCFF25BC8828E
14538AACEB3390A43C59D890213B5D2AA3D2AC3C59ABD54ACE2E85C29E36DE42
162B8C0AC47F0942B512972CCCF0D91170ED6594ECC130288549ED44744DE52C
771381C571D5AFEDB14B2E79CB022F13C834A056049EFCE35C2A7449877A2B00
2D872635082FEA2D267D8BC047AD910D3875CE9247078A826259FC8234F264E1
9FAD4AAC52015465D973193B3755B611B417FB562A0C66C77EF7001F5463FD83
2CF20F83B2B8E0C22DAE760FA556B32AAF87B86A18C18259CFAA3567C250C7C3
1AE72CD95350531BD93FAE3B6CEADB33188174FCBBD77B7B7A0841DAB6C3EBEE
F13DE8696B6455E222ADCE23F162ECF644064709A47AA8FD3632BFAD78EA5E92
D947500C3BB04CAD419F3D5B05580DC127118E3D2866CAFB8AC6CAFCEB68F895
56796455CF47AAD741F5B957D4D751245980BD569729B723D742A964558FFB4D
EAB6A440BF6ACE54157EB028F7A730B695BDF749D05EA9C1B612C4CF0F396EDC
8E943F5C020000000049454E44AE426082CAEBA2D78A030000
}  
view layout [image logo-pic]

11.9 Running Command Line Applications

You can run executable functions using the 'call function. See "help call" to see the many available refinements of 'call:

call/show "notepad.exe c:\config.sys"   ; run OS shell command

12. Ports

Rebol "ports" are created with the 'open function:

help open

Ports enable fine grained access to data sources such as files, email, network connections, etc. This line:

write/append %temp.txt "1234"

Is the same as:

temp: open %temp.txt
append temp "1234"
close temp

See all the properties of a port object using "probe":

temp: open %temp.txt
probe temp
close temp
temp: open %temp.txt
print temp/date
print temp/path
print temp/size
close temp

Just one of the properties enabled by opening a file port includes setting read, write, and execute permissions for the file:

my-file: open %temp.txt
set-modes my-file [
    world-read: true
    world-write: true
    world-execute: true
]
close my-file

Be sure to examine all the other properties of a port to see how data is handled under the hood:

write %temp.txt ""
print read %temp.txt
temp: open %temp.txt
append temp "1234"
print temp/state/inBuffer
print read %temp.txt
update temp
print read %temp.txt
temp: head temp
insert temp "abcd"
print temp/state/inBuffer
print read %temp.txt
close temp
print read %temp.txt
append temp "1q2w3e4r"  ; (error)

Fine grained access, for example, to read very large files can be managed with the port structure:

x: open/direct/lines %input.txt
while [not empty? data: copy/part x 10] [
   probe data
   ask "continue..."
]
close file

12.1 Email Ports

Ports allow you to deal with data sources in ways which aren't possible using other functions. For example, this example requires download of ALL mail in the account, just to read the second message:

print second read pop://user:pass/site.com

The port version actually only downloads a single message from the account port. Notice that ports respond to series functions:

my-email: open pop://user:pass/site.com
print second my-email

12.2 Console Ports

Access to the console can also be manipulated with a port structure:

print ""
q: open/binary/no-wait console://
forever [if wait q [print join "You typed:  " to-integer copy q]]

q: open/binary/no-wait console:// 
while [120 <> x: to-integer copy wait q] [print join "You typed:  " x]
print "Goodbye!" wait 2

12.3 Sound and other Hardware Ports

The port structure is used to access not only data structures, but also hardware and other features. The sound system is set up as a port in Rebol. Play sounds by opening a sound port and inserting the data:

the-sound: load %/c/windows/media/tada.wav
insert s: open sound:// the-sound wait s close s
insert s: open sound:// load %/c/windows/media/tada.wav wait s close s
insert s: open sound:// load request-file/only wait s close s

That data can be a .wav file, or it can be generated by code:

REBOL [title: "Sine Wave Example, by Carl Sassenrath"]
tone: #{}
for phase 1 360 6 [
    val: 128 + to-integer 127 * sine phase
    append tone to-char val
]
q: make sound [rate: 44100  channels: 1  bits: 8  volume: 0.5  data: #{}]
loop 1000 [append q/data tone]
insert s: open sound:// q wait s close s

This example opens a serial port:

port: open serial://port1/9600/8/none/1

12.4 Network Ports

This section demonstrates how to send text and binary data across network ports:

A basic server (run this script first):

REBOL [title: "Server"]
port: first wait open/lines tcp://:55555
print join "Received: " first wait port

A basic client (run this script second, in a separate instance of REBOL):

REBOL [title: "Client"]
port: open/lines tcp://localhost:55555
insert port ask "Send:  "

This app combines a server and a client:

REBOL [title: "Network Text Messenger"]
view layout [ across
    q: btn "Serve" [
        focus g 
        p: first wait open/lines tcp://:8 
        z: 1
    ]
    text"OR"
    k: btn "Connect" [
        focus g 
        p: open/lines rejoin[tcp:// i/text ":8"]
        z: 1
    ]
    i: field form read join dns:// read dns://  
    return
    r: area rate 4 feel [
        engage: func [f a e] [
            if a = 'time and value? 'z [
                if error? try [x: first wait p] [quit]
                r/text: rejoin [x "^/" r/text] 
                show r
            ]
        ]
    ]
    return
    g: field "Type message here [ENTER]" [
        insert p value
        focus face
    ]
]

This app demonstrates how to send binary files across a network port:

REBOL [title: "Network Binary File Transfer"]
; server/receiver - run first:
if error? try [port: first wait open/binary/no-wait tcp://:8] [quit]
mark: find file: copy wait port #""
length: to-integer to-string copy/part file mark
while [length > length? remove/part file next mark] [append file port]
view layout [image load file]
; client/sender - run after server (change IP address if using on 2 pcs):
save/png %image.png to-image layout [box blue "I traveled through ports!"]
port: open/binary/no-wait tcp://127.0.0.1:8  ; adjust this IP address
insert file: read/binary %image.png join l: length? file #""
insert port file

Here's a VOIP intercom:

REBOL [title: "VOIP Intercom"] do [write %ireceive.r {REBOL []
if error? try [port: first wait open/binary/no-wait tcp://:8] [quit]
wait 0  speakers: open sound://
forever [
    if error? try [mark: find wav: copy wait port #""] [quit]
    i: to-integer to-string copy/part wav mark
    while [i > length? remove/part wav next mark] [append wav port]
    insert speakers load to-binary decompress wav
]} launch %ireceive.r
lib: load/library %winmm.dll
mci: make routine! [c [string!] return: [logic!]] lib "mciExecute"
if (ip: ask "Connect to IP (none = localhost):  ") = "" [ip: "localhost"]
if error? try [port: open/binary/no-wait rejoin [tcp:// ip ":8"]] [quit]
mci "open new type waveaudio alias wav"
forever [
    mci "record wav"  wait 2  mci "save wav r"  mci "delete wav from 0"
    insert wav: compress to-string read/binary %r join l: length? wav #""
    if l > 4000 [insert port wav]  ; squelch (don't send) if too quiet
]]

This app demonstrates how to send and receive data via UDP:

REBOL [Title: "UDP Group Chat"]
net-in: open udp://:9905  ; This is UDP, so NO known IP addresses required
net-out: open/lines udp://255.255.255.255:9905
set-modes net-out [broadcast: on]
name: request-text/title "Your name:"
gui: view/new layout [
    a1: area wrap rejoin [name ", you are logged in."] 
    f1: field 
    k1: at 0x0 key #"^M" [
        if f1/text = "" [return]
        insert net-out rejoin [name ":  " f1/text]
    ]
]
forever [
    focus f1
    received: wait [net-in]
    if not viewed? gui [quit]
    insert (at a1/text 1) copy received  show a1
]

13. Shared Code Files (DLLs, So, Dylib libraries, etc.)

To load shared libraries, used the 'load/library function. Give the loaded library a label. This example loads the Windows kernel32.dll library, and labels it 'lib:

lib: load/library %kernel32.dll

Define a Rebol function using the signature of any exported function in the shared library, with "make routine!". Give the exported function a label, define the return value types, and all the required parameter types. The new Rebol function below is labeled 'play-sound. It wraps the "Beep" function from the kernel32.dll library loaded above (labeled 'lib here). It returns an integer value, and requires 2 integer parameters:

play-sound: make routine! [
    return: [integer!] 
    pitch [integer!] 
    duration [integer!]
] lib "Beep"

Here's an example of the 'play-sound function in use. It uses a 'for loop to play every frequecy from 37 to 3987, in steps of 50 hertz, for 50 milliseconds each. Close the library with the 'free function when you're done:

for hertz 37 3987 50 [
    print rejoin ["The pitch is now " hertz " hertz."]
    play-sound hertz 50
]
free lib

Here's a Windows API example which plays an AVI file:

REBOL [title: "AVI example"]
lib: load/library %winmm.dll
mciExecute: make routine! [c [string!] return: [logic!]] lib "mciExecute"
if not exists? %test.avi [
    flash "Downloading test video..."
    write/binary %test.avi read/binary http://re-bol.com/test.avi
    unview
]
video: to-local-file %test.avi
mciExecute rejoin ["OPEN " video " TYPE AVIVIDEO ALIAS thevideo"]
mciExecute "PLAY thevideo WAIT"
mciExecute "CLOSE thevideo"
free lib
quit

A Windows API sound recorder example:

REBOL [title: "Sound Recorder"]
lib: load/library %winmm.dll
mciExecute: make routine! [ 
    command [string!]
    return: [logic!] 
] lib "mciExecute"
file: to-local-file to-file request-file/save/title/file "Save as:" {
    } %rebol-recording.wav
mciExecute "open new type waveaudio alias buffer1 buffer 6"
mciExecute "record buffer1"
ask "RECORDING STARTED (press [ENTER] when done)...^/"
mciExecute "stop buffer1"
mciExecute join "save buffer1 " file
free lib
print "Recording complete.  Here's how it sounds:^/"
insert snd: open sound:// load to-rebol-file file wait snd close snd
print "DONE.^/"

AutoIT DLL example which moves the mouse around the screen:

REBOL [title: "AutoIT DLL example"]
if not exists? %AutoItDLL.dll [
    write/binary %AutoItDLL.dll
    read/binary http://musiclessonz.com/rebol_tutorial/AutoItDLL.dll
]
lib: load/library %AutoItDLL.dll
move-mouse: make routine! [
    return: [integer!] x [integer!] y [integer!] z [integer!]
] lib "AUTOIT_MouseMove"
print "Press the [Enter] key, and your mouse will move to the top"
ask "corner of your screen, and then down diagonally to 200x200:  " 
for position 0 200 5 [
    move-mouse position position 10 
]
free lib
print "^/Done.^/"
halt

Use the Windows API to capture a webcam image:

REBOL [title: "Webcam"]
avicap32.dll: load/library %avicap32.dll
user32.dll: load/library %user32.dll
find-window-by-class: make routine! [
    ClassName [string!] WindowName [integer!] return: [integer!]
] user32.dll "FindWindowA"
sendmessage: make routine! [
    hWnd [integer!] val1 [integer!] val2 [integer!] val3 [integer!]
    return: [integer!]
] user32.dll "SendMessageA"
sendmessage-file: make routine! [
    hWnd [integer!] val1 [integer!] val2 [integer!] val3 [string!]
    return: [integer!]
] user32.dll  "SendMessageA"
cap: make routine! [
    cap [string!] child-val1 [integer!] val2 [integer!] val3 [integer!]
    width [integer!] height [integer!] handle [integer!] 
    val4 [integer!] return: [integer!]
] avicap32.dll "capCreateCaptureWindowA"
view/new center-face layout/tight [
    image 320x240
    across
    btn "Take Snapshot" [
        sendmessage cap-result 1085 0 0
        sendmessage-file cap-result 1049 0 "scrshot.bmp"
        browse %scrshot.bmp
    ]
    btn "Exit" [
        sendmessage cap-result 1205 0 0
        sendmessage cap-result 1035 0 0
        free user32.dll   quit
    ]
]
hwnd: find-window-by-class "REBOLWind" 0
cap-result: cap "cap" 1342177280 0 0 320 240 hwnd 0
sendmessage cap-result 1034 0 0
sendmessage cap-result 1077 1 0
sendmessage cap-result 1075 1 0
sendmessage cap-result 1074 1 0
sendmessage cap-result 1076 1 0
do-events

Use the Windows API to change Rebol's default window title:

tt: "Your Title"
user32.dll: load/library %user32.dll
gf: make routine![return:[int]]user32.dll"GetFocus"
sc: make routine![hw[int]a[string!]return:[int]]user32.dll"SetWindowTextA"
so: :show show: func[face][so[face]hw: gf sc hw tt]
view layout [text 400x400 "No 'REBOL -' in the title bar!"]

Using the usps4cb DLL:

REBOL [title: "USPS Intelligent Mail Encoder"]
unless exists? %usps4cb.dll [
    write/binary %usps4cb.dll read/binary http://re-bol.com/usps4cb.dll
]
GEN-CODESTRING: make routine! [
    t [string!]  r[string!]  c [string!]  return: [integer!]
]  load/library request-file/only/file %usps4cb.dll "USPS4CB"
t: request-text/title/default "Tracking #:" "00700901032403000000"
r: request-text/title/default "Routing #:" "55431308099"
GEN-CODESTRING t r (make string! 65)
alert first second first :GEN-CODESTRING

An MP3 player using libwmp3.dll:

REBOL [Title: "MP3 Jukebox"]
if not exists? %libwmp3.dll [
    write/binary %libwmp3.dll
    read/binary http://musiclessonz.com/rebol_tutorial/libwmp3.dll
]
lib: load/library %libwmp3.dll
Mp3_Initialize: make routine! [
    return: [integer!]
] lib "Mp3_Initialize"
Mp3_OpenFile: make routine! [
    return: [integer!] 
    class [integer!] 
    filename [string!]
    nWaveBufferLengthMs [integer!]
    nSeekFromStart [integer!] 
    nFileSize [integer!]
] lib "Mp3_OpenFile"
Mp3_Play: make routine! [
    return: [integer!] 
    initialized [integer!]
] lib "Mp3_Play"
Mp3_Stop: make routine! [
    return: [integer!] 
    initialized [integer!]
] lib "Mp3_Stop"
Mp3_Destroy: make routine! [
    return: [integer!] 
    initialized [integer!]
] lib "Mp3_Destroy"
Mp3_GetStatus: make routine! [
    return: [integer!] 
    initialized [integer!] 
    status [struct! []]
] lib "Mp3_GetStatus"
status: make struct! [
    fPlay [integer!] 
    fPause [integer!] 
    fStop [integer!] 
    fEcho [integer!] 
    nSfxMode [integer!] 
    fExternalEQ [integer!] 
    fInternalEQ [integer!] 
    fVocalCut [integer!] 
    fChannelMix [integer!] 
    fFadeIn [integer!] 
    fFadeOut [integer!] 
    fInternalVolume [integer!] 
    fLoop [integer!] 
    fReverse [integer!] 
] none
Mp3_Time: make struct! [
    ms [integer!] 
    sec [integer!]
    bytes [integer!] 
    frames [integer!] 
    hms_hour [integer!] 
    hms_minute [integer!] 
    hms_second [integer!] 
    hms_millisecond [integer!] 
] none
TIME_FORMAT_SEC: 2
SONG_BEGIN: 1
SONG_CURRENT_FORWARD: 4
Mp3_Seek: make routine! [
    return: [integer!] 
    initialized [integer!]
    fFormat [integer!]
    pTime [struct! []]
    nMoveMethod [integer!]
] lib "Mp3_Seek"
Mp3_PlayLoop: make routine! [
    return: [integer!] 
    initialized [integer!]
    fFormatStartTime [integer!]
    pStartTime [struct! []]
    fFormatEndTime [integer!] 
    pEndTime [struct! []]
    nNumOfRepeat [integer!] 
] lib "Mp3_PlayLoop"
Mp3_GetSongLength: make routine! [
    return: [integer!]
    initialized [integer!]
    pLength [struct! []]
] lib "Mp3_GetSongLength"
Mp3_GetPosition: make routine! [
    return: [integer!] 
    initialized [integer!]
    pTime [struct! []]
] lib "Mp3_GetPosition"
Mp3_SetVolume: make routine! [
    return: [integer!] 
    initialized [integer!]
    nLeftVolume [integer!]
    nRightVolume [integer!]
] lib "Mp3_SetVolume"
Mp3_GetVolume: [
    initialized [integer!]
    pnLeftVolume [integer!]
    pnRightVolume [integer!]
    return: [integer!]
] lib "Mp3_GetVolume"
Mp3_VocalCut: make routine! [
    return: [integer!] 
    initialized [integer!]
    fEnable [integer!]
] lib "Mp3_VocalCut"
Mp3_ReverseMode: make routine! [
    return: [integer!] 
    initialized [integer!]
    fEnable [integer!]
] lib "Mp3_ReverseMode"
Mp3_Close: make routine! [
    return: [integer!] 
    initialized [integer!]
] lib "Mp3_Close"
waves: []
foreach file read %. [
    if (%.mp3 = suffix? file) [append waves file]
]
append waves "(CHANGE FOLDER...)"
initialized: Mp3_Initialize
view center-face layout [
    vh2 "Click a File to Play:"
    file-list: text-list data waves [
        if value = "(CHANGE FOLDER...)" [
            new-dir: request-dir
            if new-dir = none [break]
            change-dir new-dir
            waves: copy []
            foreach file read %. [
                if (%.mp3 = suffix? file) [append waves file]
            ]
            append waves "(CHANGE FOLDER...)"
            file-list/data: waves
            show file-list
            break
        ]
        Mp3_GetStatus initialized status
        if (status/fPlay = 0) [
            file: rejoin [to-local-file what-dir "\" value]
            Mp3_OpenFile initialized file 1000 0 0
            Mp3_Play initialized
        ]
    ]
    across
    tabs 40
    text "Seek:   " 
    tab slider 140x15 [
        plength: make struct! Mp3_Time compose [0 0 0 0 0 0 0 0]
        Mp3_GetSongLength initialized plength
        location: to-integer (value * plength/sec)
        ptime: make struct! Mp3_Time compose [0 (location) 0 0 0 0 0 0]
        Mp3_Seek initialized TIME_FORMAT_SEC ptime SONG_BEGIN
        Mp3_Play initialized
    ]
    return
    text "Volume: " 
    tab slider 140x15 [
        volume: to-integer value * 100
        Mp3_SetVolume initialized volume volume
    ]
    return
    btn "Reverse" [
        Mp3_GetStatus initialized status
        either (status/fReverse > 0) [
            Mp3_ReverseMode initialized 0
        ] [
            Mp3_ReverseMode initialized 1
        ]
    ]
    btn "Vocal-Cut" [
        Mp3_GetStatus initialized status
        either (status/fVocalCut > 0) [
            Mp3_VocalCut initialized 0
        ] [
            Mp3_VocalCut initialized 1
        ]
    ]
    return
    tabs 50
    text "Loop Start:" 
    tab start-slider: slider 120x15 []
    return
    text "Loop End:  " 
    tab end-slider: slider 120x15 []
    return
    btn "Play Loop" [
        plength: make struct! Mp3_Time compose [0 0 0 0 0 0 0 0]
        Mp3_GetSongLength initialized plength
        s-loc: to-integer (start-slider/data * plength/sec)
        pStartTime: make struct! Mp3_Time compose [0 (s-loc) 0 0 0 0 0 0]
        end-loc: to-integer (end-slider/data * plength/sec)
        pEndTime: make struct! Mp3_Time compose [0 (end-loc) 0 0 0 0 0 0]
        ; TIME_FORMAT_SEC: 2
        Mp3_PlayLoop initialized 2 pStartTime 2 pEndTime 1000  ; 1000x
    ]
    btn 58 "Stop" [
        Mp3_GetStatus initialized status
        if (status/fPlay > 0) [Mp3_Stop initialized]
    ]
]
Mp3_Destroy initialized
free lib

14. Third Party Libraries

This section contains examples which demonstrate how to use a variety of third party Rebol libraries. SQLite and MySQL databases, a little 3D module based on Draw graphics, dialects to create PDF and Flash (.swf) output, XML-RPC, additional GUI layout tools, and other functionality are demonstrated:

14.1 PDF

PDF Maker by Gabriele Santilli:

REBOL [title: "PDF example"]
do http://www.colellachiara.com/soft/Misc/pdf-maker.r
write/binary %example.pdf layout-pdf [[textbox ["Hello PDF world!"]]]
call %example.pdf

REBOL [title: "PDF example 2"]
do http://www.colellachiara.com/soft/Misc/pdf-maker.r
write/binary %example.pdf layout-pdf compose/deep [
    [
        page size 215.9 279.4  ; American Letter Size
        textbox ["Here is page 1.  It just contains this text."]
    ] 
    [
        textbox 55 55 90 100 [
            {Here's page 2.  This text box contains a starting
             XxY position and an XxY size.  Coordinates are in
             millimeters and extend from the BOTTOM LEFT of the
             page (this box extends from starting point 55x55
             and is 90 mm wide, 100 mm tall).
             All the following page sizes are the default ISO A4,
             or 211297 mm.  That is SLIGHTLY SMALLER than the
             standard American Letter page size.}
        ]
    ]
    [
        textbox 0 200 200 50 [
            center font Helvetica 10.5
            {This is page 3.  The text inside this box is centered
             and formatted using Helvetica font, with a character
             size of 10.5 mm.}
        ]
    ]
    [
        apply rotation 20 translation 35 150 [
            textbox 4 4 200 20 [
                {This is page 4.  The textbox is rotated 20 degrees
                 and translated (moved over) 35x150 mm.  Graphics
                 and images can also be rotated and translated.}
            ]
        ]
    ]
    [
        textbox 5 200 200 40 [
            {Here's page 5.  It contains this textbox and several
             images.  The first image is placed at starting point
             50x150 and is 10mm wide by 2.4mm tall.  The second
             image is 2x bigger and rotated 90 degrees.  The last
             image is placed at starting point 100x150 and is
             streched to 10x its size.  Notice that this ENTIRE
             layout block has been evaluated with compose/deep to
             evaluate the images in the following parentheses.}
        ]
        image 50 150 10 2.4 (system/view/vid/image-stock/logo)
        image 50 100 20 4.8 rotated 90 
            (system/view/vid/image-stock/logo)
        image 100 150 100 24 (system/view/vid/image-stock/logo)
    ]
    [
        textbox [
            {This page contains this textbox and several generated
             graphics:  a line, a colored and filled box with a
             colored edge, and a circle.}
        ]
        line width 3  20 20 100 50   ; starting and ending XxY positions
        solid box edge width 0.2 edge 44.235.77 150.0.136 100 67 26 16
        circle 75 50 40   ; starting point 75x50, radius 40mm
    ]
]
call %example.pdf
; see http://www.colellachiara.com/soft/Misc/pdf-maker-doc.pdf

14.2 Flash (.swf) Builder

By Oldes:

REBOL [title: "REBOL/Flash example"]
do http://box.lebeda.ws/~hmm/rswf/rswf_latest.r
make-swf/save/html http://tinyurl.com/yhex2cf 
browse %starfield1.html
; see http://box.lebeda.ws/~hmm/rswf/ 
; and http://re-bol.com/rebol.html#section-9.17

14.3 RebGUI

A GUI dialect by Ashley Truter, with features not in native VID:

REBOL [title: "RebGUI Example"]
do http://re-bol.com/rebgui.r
display/close "Tab Panel" [
    main-screen: tab-panel data [
        "Spreadsheet" [
            x: sheet data [
                A1 32 A2 12 A3 "=a1 + a2" A4 "=1.06 * to decimal! a3"
            ]
            a: area
            reverse
            button -1 " Show Data " [x/save-data set-text a x/data]
            button -1 " Quit! " [if true = question "Quit?" [quit]]
        ]
        "Pie Chart" [
            pie-chart data ["VID" yellow 19 "RebGUI" red 81]
                tip "Pie Chart!"
        ]
        "Menu" [
            menu #LHW data [
                "File" [
                     "Open" [x/text: read to-file request-file show x]
                     "Save" [write to-file request-file/save x/text]
                 ]
                 "About" [
                     "Info" [alert "RebGUI is great!"]
                 ]
            ] 
            return
            x: area #LHW "[CTRL-Z: Undo CTRL-Y: Redo CTRL-S: Spell Check]"
        ]
        "VID style"  [
            style -1 data [text bold "Back to Spreadsheet" [
                main-screen/select-tab 1
            ]]
        ]
        action [wait .2 face/color: 230.230.230]  "Text" [
            text "Tabs are a great way to maximize screen real estate."
        ]
        action [wait .2 set-focus z]  "Fields" [
            y: field
            z: field "Edit me"
        ]
    ]
] [question "Really Close?"]
do-events

A little app example:

REBOL [title: "RebGUI Table Grid Example"]
do load-thru http://re-bol.com/rebgui.r
alert {Default username/password is "user1/pass1"}
unless exists? %snappmx.txt [
    save %snappmx.txt [
        "user1" "pass1" "Bill Jones" "bill@site.com" "Bill LLC" 
        "user2" "pass2" "John Smith" "john@mail.com" "John LLC"
    ]
]
database: load %snappmx.txt
login: request-password
found: false
foreach [userid password name email company] database [
    either (login/1 = userid) and (login/2 = password) [found: true] []
]
if found = false [alert "Incorrect Login." quit]
add-record: does [
    display/dialog "User Info" [
        text 20 "User:" f1: field return
        text 20 "Pass:" f2: field return
        text 20 "Name:" f3: field return
        text 20 "Email:" f4: field return
        text 20 "Company:" f5: field reverse
        button -1 #XY " Clear " [clear-fields]
        button -1 #XY " Add " [add-fields]
    ]
]
edit-record: does [
    display/dialog "User Info" [
        text 20 "User:" f1: field (form pick t/selected 1) return
        text 20 "Pass:" f2: field (form pick t/selected 2) return
        text 20 "Name:" f3: field (form pick t/selected 3) return
        text 20 "Email:" f4: field (form pick t/selected 4) return
        text 20 "Company:" f5: field (form pick t/selected 5) reverse
        button -1 #XY " Delete " [
            t/remove-row t/picked
            save %snappmx.txt t/data
            hide-popup
        ]
        button -1 #XY " Save " [
            t/remove-row t/picked
            add-fields
            save %snappmx.txt t/data
            hide-popup
        ]
    ]
]
add-fields: does [
    t/add-row reduce [
        copy f1/text copy f2/text copy f3/text copy f4/text copy f5/text
    ]
    save %snappmx.txt copy t/data
]
clear-fields: does [
    foreach item [f1 f2 f3 f4 f5] [do rejoin [{set-text } item {""}]]
]
table-size: system/view/screen-face/size/1 / ctx-rebgui/sizes/cell
display/maximize "Users" [
    t: table table-size #LWH options [
        "" left .0  "" left .0   ; don't show the first 2 fields
        "Name" center .33  "Email" center .34  "Company" center .34
    ] data database [edit-record]
    reverse
    button -1 #XY " Add " [add-record]
]
do-events

14.4 Cyphre Menu and Tab Panel

REBOL [title: "Cyphre Menu and Tab Panel Example"]
do load-thru http://re-bol.com/cyphre_menu_and_tab_panel.r
insert-event-func [ 
    either event/type = 'resize [
        mn/size/1: system/view/screen-face/pane/1/size/1 
        my-tabs/size: system/view/screen-face/pane/1/size - 15x30
        show [mn my-tabs]  none
    ] [event]
]
view/options center-face layout  [
    across space 0x0 origin 0x0
    mn: menu with [ 
        size: 470x20 
        data: compose/deep [
            " File " [
                "Open" # "Ctrl+O" [request-file]
                "Save" # "Ctrl+S" [request-file/save]
                bar
                "Exit" [quit]
            ]
            " Options " [
                "Preferences" sub [
                    "Colors" [alert form request-color]
                    "Settings" [request-text/title "Enter new setting:"]
                ]
                "About" [alert "Menu Widget by Cyphre"]
            ]
        ]
    ]
    below
    at 10x25 my-tabs: tab-panel data [
        "Fields"   [
            h1 "Tab Panel by Cyphre" field field area area btn "Ok"
         ]
        "Data List"  [
            t1: text-list 400x430 data system/locale/months [alert value]
        ]
    ]
] [resize]

14.5 Henrik Listview

REBOL [title: "Listview Example"]
evt-close: func [face event] [
    either event/type = 'close [
        inform layout [
            across
            Button "Save Changes" [
                backup-file: to-file rejoin ["backup_" now/date]
                write backup-file read %database.db
                save %database.db theview/data quit
            ]
            Button "Lose Changes" [quit]
            Button "CANCEL" [hide-popup]
        ] none ] [ 
        event
    ]
]
insert-event-func :evt-close
if not exists? %list-view.r [write %list-view.r read
    http://www.hmkdesign.dk/rebol/list-view/list-view.r
]
do %list-view.r
if not exists? %database.db [write %database.db {[][]}]
database: load %database.db
view center-face gui: layout [
    h3 {To enter data, double-click any row, and type directly 
        into the listview.  Click column headers to sort:}
    theview: list-view 775x200 with [
        data-columns: [
            Student Teacher Day Time Phone Parent Age Payments
            Reschedule Notes
        ]
        data: copy database
        tri-state-sort: false
        editable?: true
    ]
    across
    button "add row" [theview/insert-row]
    button "remove row" [
        if (to-string request-list "Are you sure?" 
                [yes no]) = "yes" [
            theview/remove-row
        ]
    ]
    button "filter data" [
        filter-text: request-text/title trim {
            Filter Text (leave blank to refresh all data):}
        if filter-text <> none [
            theview/filter-string: filter-text
            theview/update
        ]
    ]
    button "save db" [
        backup-file: to-file rejoin ["backup_" now/date]
        write backup-file read %database.db
        save %database.db theview/data
    ]
]

14.6 Ensel Menu System

REBOL [title: "Another Menu Module Example"]
if not exists? %menu-system.r [write %menu-system.r (
        read http://www.rebol.org/library/scripts/menu-system.r)]
do %menu-system.r
menu-data: [
    file: item "File" menu [item "Open" item "Save" item "Quit"]
    edit: item "Edit" menu [ 
        item "Item 1"
        item "Item 2" <ctrl-q>
        ---
        item "Submenu..." menu [
            item "Submenu Item 1" 
            item "Submenu Item 2"
            item "Submenu Item 3" menu [
                item "Sub-Submenu Item 1"
                item "Sub-Submenu Item 2"
            ]
        ]
        ---
        item "Item 3"       
    ]
    icons: item "Icons" menu [
        item "Icon Item 1" icons [help.gif stop.gif]
        item "Icon Item 2" icons [info.gif exclamation.gif]
    ]
]
basic-style: [item style action [
    switch item/body/text [
        case "Open" [
            the-file: request-file
            alert rejoin ["You opened: " the-file]
        ] 
        case "Save" [
            the-file: request-file/save
            alert rejoin ["You saved to: " the-file]
        ]
        case "Quit" [
            if (request/confirm "Really Quit?") = true [quit]
        ]   
        case "Item 1" [alert "Item 1 selected"]
        case "Item 2" [alert "Item 2 selected"]
        case "Item 3" [alert "Item 3 selected"]
        case "Submenu Item 1" [alert "Submenu Item 1 selected"]
        case "Submenu Item 2" [alert "Submenu Item 2 selected"]
        case "Submenu Item 3" [alert "Submenu Item 3 selected"]
        case "Sub-Submenu Item 1" [alert "Sub-Submenu Item 1 selected"]
        case "Sub-Submenu Item 2" [alert "Sub-Submenu Item 2 selected"]
        case "Icon Item 1" [alert "Icon Item 1 selected"]
        case "Icon Item 2" [alert "Icon Item 2 selected"]
    ]
]]
evt-close: func [face event] [either event/type = 'close [quit] [event]]
insert-event-func :evt-close
view center-face layout [
    size 400x500
    at 2x2 menu-bar menu menu-data menu-style basic-style
]

14.7 r3D

A 3D dialect based on native Rebol draw functions, by Andrew Hoadley:

REBOL [title: "r3D Example"]
do http://re-bol.com/r3d.r
Transx:   Transy:   Transz:   300.0
Lookatx:  Lookaty:  Lookatz:  100.0 
do update: does [ 
    world: copy [] 
    append world reduce [
        reduce [cube-model (r3d-scale 100.0 150.0 125.0) red]
    ]
    camera: r3d-position-object   
        reduce [Transx Transy Transz]
        reduce [Lookatx Lookaty Lookatz]
        [0.0 0.0 1.0] 
    RenderTriangles: render world camera r3d-perspective 250.0 400x360
    probe RenderTriangles    ; This line can be removed
] 
view layout [
    scrn: box 400x360 black effect [draw RenderTriangles]  ; basic draw
    across return
    slider 60x16 [Transx: (value * 600 - 300.0) update show scrn]
    slider 60x16 [Transy: (value * 600 - 300.0) update show scrn]
    slider 60x16 [Transz: (value * 600) update show scrn]
    slider 60x16 [Lookatx: (value * 400 - 200.0) update show scrn]
    slider 60x16 [Lookaty: (value * 400 - 200.0) update show scrn]
    slider 60x16 [Lookatz: (value * 200 ) update show scrn]
]

14.8 MySQL

By Nenad Radosevic ("Doc Kimbel"):

REBOL [title: "MYSQL Example"]
; First, start MySQL and create a database named "Contacts"
unless exists? %mysql-protocol.r [
    write %mysql-protocol.r read http://re-bol.com/mysql-protocol.r
]
do %mysql-protocol.r
db: open mysql://root:root@localhost/Contacts
insert db {create table Contacts (
    name            varchar(100),
    address         text,
    phone           varchar(12),
    birthday        date 
)} 
insert db {INSERT into Contacts VALUES 
    ('John Doe', '1 Street Lane', '555-9876', '1967-10-10'),
    ('John Smith', '123 Toleen Lane', '555-1234', '1972-02-01'),
    ('Paul Thompson', '234 Georgetown Pl.', '555-2345', '1972-02-01'),
    ('Jim Persee', '345 Portman Pike', '555-3456', '1929-07-02'),
    ('George Jones', '456 Topforge Court', '', '1989-12-23'),
    ('Tim Paulson', '', '555-5678', '2001-05-16')
}
insert db "DELETE from Contacts WHERE birthday = '1967-10-10'"
insert db "SELECT * from Contacts"
results: copy db
probe results
close db
halt
; see http://softinnov.org/rebol/mysql-usage.html

REBOL [title: "MySQL GUI Example"]
do %mysql-protocol.r
results: read/custom mysql://root:root@localhost/Contacts [
    "SELECT * from Contacts"
]
view layout [
    text-list 100x400 data results [
        string: rejoin [
            "NAME:      " value/1 newline
            "ADDRESS:   " value/2 newline
            "PHONE:     " value/3 newline
            "BIRTHDAY:  " value/4
        ]
        view/new layout [
            area string
        ] 
    ] 
]

14.9 SQLite

By Ashley Truter:

REBOL [title: "SQLITE Example"]
unless exists? %sqlite3.dll [
    write/binary %sqlite3.dll read/binary http://re-bol.com/sqlite3.dll
]
unless exists? %sqlite.r [
    write %sqlite.r read http://re-bol.com/sqlite.r
]
do %sqlite.r
db: connect/create %contacts.db
SQL "create table contacts (name, address, phone, birthday)"
SQL {insert into contacts values 
    ('"John Doe"', '"1 Street Lane"', '"555-9876"', '"1967-10-10"')
}
data: [
    "John Smith" "123 Toleen Lane" "555-1234" "1972-02-01"
    "Paul Thompson" "234 Georgetown Pl." "555-2345" "1972-02-01"
    "Jim Persee" "345 Portman Pike" "555-3456" "1929-07-02"
    "George Jones" "456 Topforge Court" "" "1989-12-23"
    "Tim Paulson" "" "555-5678" "2001-05-16"
]
SQL "begin"
foreach [name address phone bd] data [
    SQL reduce [
        "insert into contacts values (?, ?, ?, ?)" name address phone bd
    ]
]
SQL "commit"
SQL ["DELETE from Contacts WHERE birthday = ?" "1967-10-10"]
results: SQL "select * from contacts"
probe results
disconnect db
halt
; see http://www.dobeash.com/sqlite.html

REBOL [title: "SQLITE GUI Example"]
do %sqlite.r
connect %contacts.db
results: SQL "select * from contacts"
view layout [
    text-list 100x400 data results [
        string: rejoin [
            "NAME:      " value/1 newline
            "ADDRESS:   " value/2 newline
            "PHONE:     " value/3 newline
            "BIRTHDAY:  " value/4
        ]
        view/new layout [
            area string
        ] 
    ] 
]
disconnect

14.10 Doc's Captcha

REBOL [title: "Captcha Example"]
write/binary %Caliban.caf read/binary http://re-bol.com/Caliban.caf
do http://re-bol.com/captcha.r
captcha/set-fonts-path %./
captcha/level: 4
write/binary %captcha.png captcha/generate
write %captcha.txt captcha/text
view center-face layout [
    image (load %captcha.png)
    text "Enter the captcha text:"
    f1: field [
        either f1/text = (read %captcha.txt) [
            alert "Correct"
        ] [
            alert "Incorrect"
        ]
    ] 
]

14.11 Irwin/Ensel Screen Capture

REBOL [title: "Windows Screen Capture Example"]
do http://www.rebol.org/download-a-script.r?script-name=capture-screen.r
the-image: ftp://user:pass@site.com/path/current.png
view center-face gui: layout [
    button 150 "Upload Screen Shot" [
        unview gui
        wait .2
        save/png the-image capture-screen
        view center-face gui
    ]
]

14.12 XML and XML-RPC

See http://earl.strain.at/space/rebXR+Users+Guide

REBOL [title: "XML-RPC Example"]
xmlrpc-exec http://betty.userland.com/RPC2 [examples.getStateName 41]
defh: func [orig-call] [do orig-call]
xmlrpc-serve/default [] 'defh

14.13 Q-Plot Graph and Chart Library

By Matt Licholai:

REBOL [title: "Minimal Bar Graph Example"]
if not exists? %q-plot.r [write %q-plot.r read http://re-bol.com/q-plot.r]
do %q-plot.r
view quick-plot [
    600x400
    bars [5 3 8 2 10 3 4 9 5 7]
]

REBOL [title: "Exploded Pie Chart"]
if not exists? %q-plot.r [write %q-plot.r read http://re-bol.com/q-plot.r]
do %q-plot.r
view center-face quick-plot [
    400x400
    pie [10 3 6 1 8] labels [A B C D E] explode [3 5]
    title "Exploded Sections C and E" style vh2
]

REBOL [title: "Simple Line Graph Example"]
if not exists? %q-plot.r [write %q-plot.r read http://re-bol.com/q-plot.r]
do %q-plot.r
view quick-plot [
    400x400
    line [1 2 4 8 16 32 64 128 256]
]

14.14 Rebzip

Rebzip by Vincent Ecuyer:

REBOL [title: "Rebzip Example"]
do to-string to-binary decompress 64#{
eJztW+uP20hy/+6/oldGsJ7bcEU2X00Zd4bX9iILXO4AY5N8EOYAjkTNMNaQOomy
PTbmf8+vqptkU3xIM4cgARICHmvYVdX1fnRrPn745a9/FsvrFy9W1VfnW75biFVZ
VNnXSixfCDyr/crZlsXtwvzeeVz885IkSsJEJYEQju96bhCHrhKOF8s4CGToSgKS
QeQHnh/jo1KRG8aRFzb0HD9O/CCMPA+fvciPwziUEX4RMkhkpEAIH90gAGFf0j6h
lCqUbhTRPkEcY3dftvx5kUwCMBYSCU+GIO1KIh64sR+5oAo8FUWhBDnwJIJEhX4U
Bgq4IJbIOIqilj/phknoRrSMz1GcuJGrSC5wBCRFvHoKLISQzSchVOwr0PMJHjyr
JArjlj8CVH6SsP4iBQFiLyR6TuCrIJRhABJJ7LsBRCQg3w8j3w8U6S+KPGI1avUn
HNggCsKA9ZdIpUJgJ1hIkhgSu0lEJlBeEvpgFkDYzvO8JKJ9wtiVkCgMGnqe60ZK
KS8I2XQkVBzR3k4cuxArimLCk6RJ6I32gd2TOJaQgei6RKCl50Bb0JRL8jqeD39x
vTCCMgWwYdYgJP3BYSRkc0mIEDYIvdCnfQJP+lC+77XyRqEXwwqK9efHWPahENID
VOdJ2A94xHMko4T2CaMwdGNJvEKqIJJebNFzlAv7h2HC+oOWwFWSkFxR7IGnhPzP
C8mllMv+l4BXP/LZ/+BCkZcElrwiTkLlh6Ek1/IC2N93pQyJJwgC5cAXHUgVKC8G
HxxDgUdUJHuH54VQTEPPIWbJA2lrOBR4ChLaG3huHICviA0FVv1YerRPhF29wA3I
F32yrSv91v+kC6XFiBGXfwtJ867PsYQHvkN2dPA/wGQYUQz5McweRz4LRL8pz2/5
g/IilYRxzC4sOSEkCcUERE1CWJhihewRKNIZ4ilCRMBTSR4J51CBp1r/U1BO7MKY
gmMM1pCkJ0d6HqJAQgO8qx968EyyaUwBqRLONT4JLmPX0h+CN4CTUIoiwYIkSOKI
bBrBt0PF9AQ0CgvAiwnBlyQF0Yb34iUiuPUXz4e/+WHCoSkSFSSkb9JfGMR4i1RF
sSIhYBKHimMoAo8wNwUXtAd2PK/lLyQ4KJi3iwOIgqDlmIDh4bBuRLEC0jHUyj6n
wlj6yqd9EBmsY7flDyaCjwQR2QMxBu1CZxxLgfQofkOOMWDCIsQTlBr6fkIJDsGF
gEx87Nky6LnIXyCpONmQTZFWKdgD5B0w5VKyAZ4HHn1yQGTDIPDBIUUXIgRJTXmi
5RCxhtiCY3K4wEt8BY/kaFIhJQ+OZuI2Voqjzw0SlA6XRIopuCjFWhyCHx+lIuTs
66K6yMAnYJQkpCvgUkpAMqCADtlukfTJb8g8cF3lIUdJYdUkbAG2Ql2TAs6Yih0S
kQXnTEhvKDsKviBp10C5LmKdc4JykbvAsUVQIANQRfRdjkCsJ+QJhIjcmniUvYEo
FZiXigMNsuMfBbFEdkXSQ9RaHEqsgRUv4qqEihdHiUfUkQ6hfeQ7iowIe8HzyFNg
fqRBN+RIgpeBhcAyigigGiklhx1kRe2OFZcRBEQEhmOXPikZIf0Q50IidFA3JaWi
GC4ETwttHcLACAJPG4WqgkpYocJHeUKyIYdHrZYI5Yh6A4QatQYB9w+hQlFBeCuL
QxSwGLHic0/hB1iOeHeH0lmAHEE6pOB2kd042FDIoE1FwQS3jVD/wsTiMIopMwfs
bjC2knBu9nKUeKgAlYKIJFAg0iRnxQAMIF592gh5TmFjyygCFHyJxKkzMrIkJSny
IfBBbQqXJo96IRmw90ewODZyyQ996BaeEUUWh/BYdFcel1+PyiYeyeEWI4uiDHFN
g02gXS/k2gRXcWPuIZDEAAAci8PYRz1CeQ04DkPqtJKQnNbF5hQSFL+UPShwyFho
xqjIUGNDXurD16SVvOC9iuwScdQgUSJsdXFC4kQajbmoUeeFxEuGjdDt+DCdx1GG
foBSXJsaoDwXJLmYQJ9YhqOSXWNoB4mUTOJQQGMXlxtDMIi2hPIRVSrIg+xnpQbl
I/BhK86uCFgoyKVWE0EVkOZZSPg1jImmh2IJFoQpKO84ktw+QuWykleM9EgFjgVD
oUYUwru5PCnkyDgMOakjn8Nu3BrCi9F3UkoXAedMFNOWQ9RjlCBASM0wQiDi2ujF
HrWHIduKOl4F0Ti/oByhpYkSTn2o2S6ylcUhmm1k/5gbYAHTkBlc0mGETgcGcqkT
g1DcuLtc0UARfazkIKPONbECz6HyFlAAkQQOJXzEBHsekij6IF2t0PpT4eUCAGFg
4iCmCg/fRGsCh7I6TPS/KI+xrqA+Eheyp8spCnUT/knujnQCD+Yy5lGCg4dziUQd
hVfDjnaFgmIQooorFNIMFVD2Qm4GYBKSDGWBLBLSRtQ6QHpu19F9INTdxLdUGEgq
skFAoSYirsKBz30bumvEleIeE70cQjIgFYZU0PyYUomA3WDAGA2bVaGgnASxy85B
WQfr3PyFQEMB5pQBRqkwetysYSoj1H1+e1c5h7t8UzlqITbHYmUNY7OP7fJBVHeZ
+Jxuj5m4eRBK3OR4lxZrsc+q4744iLz6edagasBljnnvNtv/cC1mvzfoVSmYpIa+
bvfLcuyxF0V2m1b55+xNTaUzHTqe+FruzRLt71AfORf0kxawTm8pFVDH2uBen5Ah
eALUhBi/hX3R/uQfx906rTIHE+uAkoz8pB9ApTTXWprgV8u0eHAO1T4vbkkX7+kd
1LC6y1afDsf7Fhq4Ha39VuRVnm41lz2Fbcp9lq7uQCfdm426kzQz/KpjZXp3xZra
5atPzRDOO5M+UGp5lWn+JLyeUprZ3ZfjykjFu4/vUEtrCZ+lkL5rZPe76uGNIfHy
u2uexxPjbst0Lf6zzAsxe/l9JpZV6dxlX2vXaY2pCeH17HF2PSzo60N+W6QQKju8
0KRX6dbZ5NvMwcoCTBsxxMvvCONfXEydj1pFWVHtp2ERqxo2K9ZOuXFqlGFoNK8a
mrh21tlhtc93VbkfAcdQ8GhkWJXF52x/yMuCDaYlAUKuj19OjfiOwTm8hfFFMk4q
tnlVQRhwm2OJkCdD/t/rcNf79wMeLNzkRbp/QBJhc3XNiGX2wjbW4ZyDECSLYfRV
C42WU7lXJ7F9Ho2zZKQxUQn9S3HnddKJrgYzCWEc7sp99WyNM/b/PpWfS6EAfaKj
3eS3/7CXrcuBFKDRKdw1a7dZdYFRhszAjDZs9zm0EpzYAX6KVUuXd1lKJZXCNbOM
tSp3D/Ndim01eWmxf063A2H7P8t80LjF/WFdHpwqv88mBGCPYEhBkH1u6e2lzq+N
bUJ2flce9+IPdACsrjpeXtZRPb/Pi2OVAciXfZDTIJgfMuy9pli46gaAlpQKz2WS
EmRfUnr7RElptvyDeHWffhWuYfIhQ/w6NJe4o1KXRXWnhRZ1tzVfpw+WUOR7F9lv
sy/vz1jwKf7WxV1YIazf2Dqg3eCQ6+PqtI3E0ICGvtMAwgk6IJidui2ifQ8g6DTQ
WoUTDea+VktnbN/R0rD1/7u0RLuNaCmkQ2lbB+ROP7HrdAAD5V6qqvEu+1u+c6gD
emjU9P1deb9D63XIKI9RJ9UZOSxmuS/TEJSFMupdVzXymhdaYNNnafB1vs9WaKMe
BO+toa4fG+gihQ8tCZT0/Rf6rdwwaqentaLzX8t1vslXGGLQcPHSEMJ4E2zY1tBz
lqwzINxn1V25tsRzmBxU1r455N8ycfL7qWu8RgHYlEIziaKb0TzRIIF5e9dFN7vr
AYARG6geA4umyRTbrLit7nTr/mKyre84jhZ1IX48wEKW63ShXotv2/xGQJVZet9Z
OdHRonnR5dxoY2MLD+VkG5gxh1v8s+D92xkvLw4V1NEhYIR5ZYt6Jf7Uvjhh5mrg
3rNm0hTM3jo9h0/5rmd8OQ5apfm2B++oHkKj63W22cJrOwDXfV5HbNOofktl5mTf
Lk0rFYw5Zbqpsv2wTz7R1wYT3Gs7cVjxbx5uIF9+fxywVHcQ7C3r4fQRG5jJawIC
2r499NaNO5lg/6NR89BdeYdYAz9slQE7Gnx1gj/kBUyh96bTyHHCm9OncTjOiQy3
HtqBchw9r02W6QMM5DX76SWiIVZMNaydhdP8a3YE/qjfT1gt+4oassmz7XoMlumw
HDXVQd1rkKK0KA7mBaOStqhNBJSBNecJKHHP8PDTA4zzPi4O6KdXfSn7gOiq/z8g
/s8FhGVhUAVL93Cx88Dr/PCpON7fwPiHaqgqtqA0je0LJPS0Ql91g8mt70fWuaHm
+okYpr8sN5tD1uflSUE/EfEtkNHTaOnUP/k/aihp2zfjJ7O/f/y3D3Rw3HbTx705
Xz78PLNHjephh3bWNI14oZvg+lQeWObjtdkcLrRLqztmoLf/92bQAeIP1OZyR23a
+fvyMzp8UiwdYM7ms7b9NgxpaMK9ZOjJdb89fIOAxZfYAhG9yfeH+lBiaQYl6r1E
Qd9S08J1UPXgMTjHNLwukChW5TpzGr0265xs/2k2O2GoPosAL+Crs8Rq1+ukWzGb
XY+tIzIw5HUgOr4BZxU/YsRqZqtfjvl2TXMVvUz3q7v8c1aPoewccBJMBWh0PtUD
zOFnTbt2JhOVWKVakcOGENCQ+rm14Rfk7Y4NhT4Z+kG0089/MAxsekNsibxqFaGr
ShefuCK0X4krQsuL1fa4ziwGWgLzdZbt6DKFQQ5aFII8HG/q8Q/MWwjwqpsSY87s
z/mhqhG+3JFWml60uB2c0DgBcL3maiuKG6dWjiZTIOvDP9o0Y5VpM4kyoJ7hymMl
mqJguTje10GmHbWuj00OqNXecZglJhdEjFk7cfGTWrhkoIWYRLFjrre1IVDusmKu
pZtry8+/7HOUPF63mv9W7oWlt4Vwx3VlIhYtzIsTh9HDHllx+Uq/ubq2XJJsuSzK
iuejNwbnRANky4VZmnvdJWPFhY5BTofa9Plef+pGKotmgHvJPt1uxbJBNNvid3IA
frOn2ZuJLrtkT1u+/KARUkGi8WDdnnC8GZpX9d4UIA3GG83t9QCnr0W6gzXX+ru8
RVVncrFFoAwWsfqSkqEaOUbaNnqMu/HYakKfMOaL7iHOoAIGujnbj6xYtK8260cf
0RXllxcDcpNmqSyUhQlvU0OJtzdDpDDItyEpltrNmISRqg0X42FypBk1ZEzV2qYo
FFqHteuP4LE2N2zX2l0n9N5qgA8tGZq3+fHeOtByBjvU+un3wvVDljfBL0a7oj76
iGS6ctdxbin1gu5cR5fdrQwzBM3VZWC5Q5mqBqKantekr9PDRvjX4MGi9ZijzubU
s6kcWX+y0/vopKlrM3efVrXrzwR3aXGb6SMgpj+XIpDtKUmbawd2Ose7HaP98mW2
G6Db15LupnugVPY0mX6Y2lXCqpQ/NaPLEOJpomSfQcvUeT3lUC0B4m1kbu7poptx
e9f8neXuxEPN1dRyfYiNrTpg7SRnZTvIe9P0aIT+NJQedOecbVrocw7XisXNqWmx
piZDevrQwy0J3ZM07chqS8Fsmo4aqBW51y0fC7tffp+t7NsIu2vmi7vW/9vG2fTL
fy22D+JLuf+EJhK5vHPMrM8XDvVxBg1E9Qms1ULTo6Xo9MFUQupe2Oqh1w2rnUaa
Bes3091riLetTC2ZLo2p9rjFoVHuFSRJj9vq6oTA3485zSrvy+LHiluHCSKDXTYf
EQ3chHAmGD/uoBTraLfS6badwOvXXNubg5sGs0OTYaxY4dmMnZEPr7P9vtwfTjt2
/dZuZgl4pImnpkwriaq30fdpf16gFxnv3qlmma9AzJ7VtpsG9CReCIG4O8Xi7vEr
mdK8OeHnPv2UUYrQwxhDnGOn9tU6Lds9hOnXB8KYNNy83qX7QzYnhmtiJ8lp6iD/
UA42qtXd/nju/P/VeNt51QOWukqP3xVwn6ddXsMO5sRXptf7lu3LNxp8br4NKZbm
6GKTbuFHfRZ4BxNOU1vUlz7WfbJ+NUKSA8nI90p/UaD7xQGGGEHmXqhGHuTH6le7
B6mj0HPNxOB5bEuR6Tg0Csy/Ich6kH2Gg9qGo0e0u4deogomNN2732q+7HNKZkR7
/fvgqe0GLm/bDXuLI1va6XXKiyy4jitZ70d26OfrqX160J3deqsTUnVEm3DIZrgY
nyvo4TIxuNrnoS/yoMDd++3a5IOgw6wf0Jqs7uamYtfJYHxedM/Mkr2KuZgEp6f9
ppj+JvWZSwv7YY3O/uYI50+i/ISaQd3U9d9OzlPtZ3xWVeen5Ha419m2H2zTJGpp
+eBqCmhitK+fM9/pHHteflf014sfgviRmvD37lvvrfuWGvHmO88X0jGXI+8J97d/
ef9xqnE/fWaEMAMiXTcsxsbBoQfzcL5GNPyxo3xx1lPqx3wt9YLLsaGnkdrTd0L0
HXuw4qGbHagXIxQU4d7kcPdsd6G+aN9HnqS3GDNIadj1dp8+HNCPXM76ozWMt1PI
k9CR4Sr7PrYYKpRTBIpS39dt04FzmzHE9h6uaP9e4EJ/eYJb1s5B33aLOt8lu9xF
Zr+9f/t769qDhzpDz8vvsUrePdZfa3pSSLhsiNo2dM8Hvyie4pOuoD+OFL9++PVX
AUVfhPYP5G175+fbtov9pPTz4S/vWxtlxeUx8Dxux6uOXp1crr8vR5PkG0Fnhktd
i/gvXujjBeXCrpSbFG3Kms7/+cBhsl42TJg5Vv8/eJB++vRbgfPZ4gJJntFh0EOT
aHMD+fTsv892WVqJ/Fk1nx778LQnwkUU6Kn/EIP/jIt/6G8mXmCOaR/ULHa7qUv9
Y5zy8MrUxQWxsDzxU1NsZnXVmRFH45s+wVWf4qLDG1r3r3wkN3wBaz9D96uT3xgZ
QrBPjCZtc3p9NJ1p9D3g5e55rU+jlj2MJxv+tQ7Q9jg15b+oQClr7z/GLT52VTY9
PGz6x1fzxQWI9AwcbGnc50bK9JZ0Mr1sDhyvxWG3zSu+yzpv1WE5GfeZchLuM+U0
DPUPVs+zwo5QnwReqvH6eXrKPTT3oU/zDHp6d6eL8fMp+znXJzxtpf+2e87QXa/K
TiNknc6anHxSBfS3cObvc+TYY2EuRdYLpGnr5LOP1V6o0ARS5+pZna07CC0Pet62
j9hfoGv6L0cgfyluTAAA
}
make-dir %./tempfiles/
repeat i 10 [write join %./tempfiles/ i random "asdfghjklqwertyuiop"]
zipfile: request-file/file/save/only %zippedfiles.zip
zip/deep zipfile %tempfiles/

There are numerous other useful libraries available at rebol.org, and at various Rebol community web sites (many by the authors above).

15. Parse

Rebol's 'parse function has two modes of operation. First, it can be used to split strings:

my-text: {"apple","orange","pear"}
parsed-block: parse my-text none

some-text: {    First Name
           Last Name
           Street Address
           City, State, Zip}
parsed-block: parse/all some-text "^/"
foreach item parsed-block [trim item]
probe parsed-block

Beyond that, 'parse is one of the most unique and powerful aspects of Rebol. It is largely responsible for Rebol's ability to create language dialects (DSLs). Parse can accept either a block, or a string of data, and a set of rules which enable it to perform the types of pattern matching typically handled by Regular Expressions (regex).

This example takes a string of text, searches thru "<%" and to "%>" strings - any instance - and replaces everything between with the string " text2 ":

code: "text1 <% replace this %> <% replace this %> text3"
parse/all code [
    any [thru "<%" copy new to "%>" (replace code new " text2 ")] to end
]
print code

This example matches opening and closing HTML title tags, copies the data in between, to the label 'my-ip, then searches the 'my-ip result for the string "Your IP Address is: " and copies everything to the end, to a variable labeled 'stripped-ip:

parse read http://guitarz.org/ip.cgi [
    thru <title> copy my-ip to </title>
]
parse my-ip [
    thru "Your IP Address is: " copy stripped-ip to end
]
alert to-string rejoin [
    "External: " trim/all stripped-ip "  "
    "Internal: " read join dns:// read dns://
]

This example extracts all the image links read from an HTML document, copies the link URLs to a block labeled 'x, downloads the images to a new folder, and then presents a GUI slideshow of each image. Notice that one line of readable 'parse code extracts all the image links:

html: read http://musiclessonz.com/ScreenShots.html
x: copy []
parse html [any [thru {src="} copy link to {"} (append x link)] to end]
make-dir %./downloaded-images
change-dir %./downloaded-images
foreach i x [attempt [
    print rejoin ["downloading:  " i] 
    write/binary (to-file last split-path to-url i) (read/binary to-url i)
]]
foreach i read %. [view center-face layout [image (load i)]]

This example searches for all the http:// URL links in a string of text, and wraps them in HTML < a href > tags:

bb:  "some text http://guitarz.org http://yahoo.com"
bb_temp: copy bb
append bb_temp " "
append bb " "
parse bb [any [thru "http://" copy link to " " (
    replace bb_temp (rejoin [{http://} link]) (rejoin [
    {<a href="} {http://} link {" target=_blank>http://} 
    link {</a>}]))] to end
]
bb: copy bb_temp
print bb

This example allows you to interactively make changes to some GUI layout code:

code: {
    view layout [
        btn "some text"
        btn "some more text"
    ] 
}
strings: copy []
parse code [
    any [thru {"} copy a-string to {"} (append strings a-string)] to end
]
foreach str strings [
    if true = request rejoin [{Change "} str {"?}] [
        replace/all code str request-text/title rejoin [
            {Change "} str {" to:}
        ]
    ]
]
editor code

This removes all comments from every line of code in a Rebol script:

code: read to-file request-file
parse/all code [any [
    to #";" begin: thru newline ending: (
        remove/part begin ((index? ending) - (index? begin))) :begin
    ]
]
editor code  ; all comments removed

This example creates a cleaned up Rebol data block from a CSV document read from a file:

filename: %filename.csv
data: copy []
lines: read/lines filename
foreach line lines [
    append/only data parse/all line ","
]
info: copy ""
foreach line data [
    either find "Header" line/1 [
        info: line/1
    ][
        append line info
    ]
]
remove-each line data [find "Header" line/1/1]
remove-each line data [
    (line/3 = "TITLE") or (line/3 = "DESCRIPTION")
]

Parse is an extremely powerful replacement for regex. It's readable and totally unique to Rebol. If you want to do text processing, data mining, or dialect creation in Rebol, learning to construct parse rules is key.

16. Binding Words Labels to Contexts

In REBOL, words maintain a reference to the context in which they are created:

REBOL []
y: copy []
f1: func [/local x] [x: 1 append y 'x]
f2: func [/local x] [x: 2 append y 'x]
f1 
f2
probe y                        ; [x x]
probe reduce y                 ; [1 2]  !!!

The 'y block above contains two words labeled 'x, both contained in the same block, but each 'x word refers to different values. This is true because each of the 'x variable labels was assigned a value within a different context (in this case, local scope within a function).

This special capability of Rebol can cause a problem when using the same variables defined in two different contexts:

x: [1 2 3]
f: [print y]
foreach y x [do f]

** Script Error: y has no value
** Near: print y

You can use the 'bind function to refer to a specific instance of a word, by associating it with another word from the desired context:

x: [1 2 3]
f: [print y]
foreach y x [do bind f 'y]

You'll see 'bind used when applying 'parse rules, and in certain metaprogramming situations:

gui-size: system/view/screen-face/size - 0x50
gui: {size gui-size}
gui: to-block gui
view layout gui

** Script Error: gui-size word has no context
** Where: do-facets
** Near: gui-size

Binding the word 'gui to the global 'system context fixes the issue above:

gui-size: system/view/screen-face/size - 0x50
gui: {size gui-size}
gui: to-block gui
bind gui 'system
view layout gui

17. Multitasking (Rebol Doesn't Have Threads)

This section demonstrates some basics of multitasking in REBOL. This example demonstrates how to obtain keyboard input without blocking looping program flow:"

p: open/binary/no-wait console://
q: open/binary/no-wait [scheme: 'console]
count: 0
forever [
    count: count + 1
    if not none? wait/all [q :00:00.01] [
        wait q
        qq: to string! copy q
        probe qq
        print ["^/loop count incremented to" count "while waiting^/"]
    ]
]

In "Catch" game example earlier, you saw one method of multitasking a GUI. Use 'view/new to build the GUI, but don't start the event loop (normally using 'do-events). Instead, start a 'forever loop, perform any calculations, evaluations, and manually update the layout display with 'show. Typically, you'll want to include a small delay in each repeate, using the 'wait function:

REBOL [title: "Catch Game"]
random/seed now   
start: now/time
speed: 2
view/new center-face g: layout [
    size 600x440 
    at 280x20   a: image logo.gif
    at 280x420  b: btn 50x20 blue
    key keycode [left]  [b/offset: b/offset - 10x0]
    key keycode [right] [b/offset: b/offset + 10x0]
]
forever [
    a/offset: a/offset + (as-pair 0 round speed)
    if overlap? a b [
        a/offset: as-pair (random 500) 20
        speed: speed + .1
    ]
    if a/offset/y > 440 [break]
    wait .01
    show g
]

Another way to multitask in a GUI is:

  1. Assign a rate of 0 to a GUI widget.
  2. Assign a "feel" detection to that widget, and put the actions you want performed simultaneously inside the block that gets evaluated every time a 'time event occurs.
  3. Stop and start the evaluation of concurrently active portions of code by assigning a rate of "none" or 0, respectively, to the associated GUI widget(s).

Here's a GUI demo of the above technique:

REBOL [title: "GUI Multitasking"]
webcam-url: http://209.165.153.2/axis-cgi/jpg/image.cgi
view layout [
    btn "Start Video" [
        webcam/rate: 0 
        webcam/image: load webcam-url 
        show webcam
    ]
    btn "Stop Video" [webcam/rate: none show webcam]
    return 
    webcam: image load webcam-url 320x240 rate 0 feel [
        engage: func [f a e][
            if a = 'time [
                f/image: load webcam-url show f
            ] 
        ] 
    ]
    clock: field to-string now/time/precise rate 0 feel [
        engage: func [f a e][
            if a = 'time [
                f/text: to-string now/time/precise show f
            ] 
        ] 
    ]
    h3 "Notice the delay in the timer as each image loads"
]

This examples achieves true multitasking by simply writing the code for one process into a separate file and running it in a separate REBOL interpreter process using the "launch" function:

REBOL [title: "Launching Separate Processes"]
write %async.r {
    REBOL []
    view layout [
        origin 10x10
        clock: field to-string now/time/precise rate 0 feel [
            engage: func [face action event][
                if action = 'time [
                    face/text: to-string now/time/precise show face
                ] 
            ] 
        ]
    ]
}
launch %async.r
webcam-url: http://209.165.153.2/axis-cgi/jpg/image.cgi
view center-face layout [
    btn "Start Video" [
        webcam/rate: 0 
        webcam/image: load webcam-url 
        show webcam
    ]
    btn "Stop Video" [webcam/rate: none show webcam]
    return 
    webcam: image load webcam-url 320x240 rate 0 feel [
        engage: func [face action event][
            if action = 'time [
                face/image: load webcam-url show face
            ]
        ]
    ]
]

18. More about Built-In Help

The "help" function displays required syntax for any REBOL function:

help print

"?" is a synonym for "help":

? print

The "what" function lists all built-in words:

what

Together, those two words provide a built-in reference guide for the entire core REBOL language. Here's a script that saves all the above documentation to a file. Give it a few seconds to run:

echo %words.txt what echo off   ; "echo" saves console activity to a file
echo %help.txt
foreach line read/lines %words.txt [
    word: first to-block line
    print "___________________________________________________________^/"
    print rejoin ["word:  " uppercase to-string word]  print "" 
    do compose [help (to-word word)]
]
echo off
editor at read %help.txt 4

You can use help to search for defined words and values, when you can't remember the exact spelling of the word. Just type a portion of the word (hitting the tab key will also show a list of words for automatic word completion):

? to-         ; shows a list of all built-in type conversions
? reques      ; shows a list of built-in requester functions
? "load"      ; shows all words containing the characters "load"
? "?"         ; shows all words containing the character "?"

Here are some more examples of ways to search for useful info using help:

? datatype!   ; shows a list of built-in data types
? function!   ; shows a list of built-in functions
? native!     ; shows a list of native (compiled C code) functions
? char!       ; shows a list of built-in control characters
? tuple!      ; shows a list of built-in colors (RGB tuples)
? .gif        ; shows a list of built-in .gif images

You can view the source code for built-in "mezzanine" (non-native) functions with the "source" function. There is a huge volume of REBOL code accessible right in the interpreter, and all of the mezzanine functions were created by the language's designer, Carl Sassenrath. Studying mezzanine source is a great way to learn more about advanced REBOL code patterns:

source help
source request-text
source view
source layout
source ctx-viewtop  ; try this:  view layout [image load ctx-viewtop/13]

The "word browser" script is a useful tool for finding, cross referencing, and learning about all the critical functions in REBOL:

write %wordbrowser.r read http://re-bol.com/wordbrowser.r
do %wordbrowser.r

18 The REBOL System Object, and Help with GUI Widgets

"Help system" displays the contents of the REBOL system object, which contains many important settings and values. You can explore each level of the system object using path notation, like this:

? system/console/history        ; the current console session history
? system/options
? system/locale/months
? system/network/host-address

You can find info about all of REBOL's GUI components in "system/view/VID":

? system/view/VID

The system/view/VID block is so important, REBOL has a built-in short cut to refer to it:

? svv

You'll find a list of REBOL's GUI widgets in "svv/vid-styles". Use REBOL's "editor" function to view large system sections like this:

editor svv/vid-styles

Here's a script that neatly displays all the words in the above "svv/vid-styles" block:

foreach i svv/vid-styles [if (type? i) = word! [print i]]

Here's a more concise way to display the above widgets, using the "extract" function:

probe extract svv/vid-styles 2

This script lets you browse the object structure of each widget:

view layout [
    text-list data (extract svv/vid-styles 2) [
        a/text: select svv/vid-styles value
        show a focus a
    ]
    a: area 500x250 
]

REBOL's GUI layout words are available in "svv/vid-words":

? svv/vid-words

The following script displays all the images in the svv/image-stock block:

b: copy [] 
foreach i svv/image-stock [if (type? i) = image! [append b i]]
v: copy [] foreach i b [append v reduce ['image i]]
view layout v

The changeable attributes ("facets") available to all GUI widgets are listed in "svv/facet-words":

editor svv/facet-words

Here's a script that neatly displays all the above facet words:

b: copy [] 
foreach i svv/facet-words [if (not function? :i) [append b to-string i]]
view layout [text-list data b]

Some GUI widgets have additional facet words available. The following script displays all such functions, and their extra attributes:

foreach i (extract svv/vid-styles 2) [
    x: select svv/vid-styles i
    ; additional facets are held in a "words" block:
    if x/words [
        prin join i ": "
        foreach q x/words [
            if not (function? :q) [prin join q " "]
        ]
        print ""
    ]
]

To examine the function(s) that handle any of the additional facets for the widgets above, type the path to the widget's "words" block, i.e.:

svv/vid-styles/TEXT-LIST/words

For more information on system/view/VID, see http://www.mail-archive.com/rebol-bounce@rebol.com/msg01898.html and http://www.rebol.org/ml-display-message.r?m=rmlHJNC.

It's important to note that you can SET any system value. Just use a colon, like when assigning variable values:

system/user/email: user@website.com

Familiarity with the system object yields many useful tools.

19. Learning More - Online Resources

Rebol is a powerful general purpose development tool. You can use it to create complex software of all types for desktop, mobile, server, and web environments, with greater simplicity, and dramatically improved productivity, compared to other tools. Because Rebol code is typically orders of magnitude shorter, more concise, and more readable than code written in popular tools such as Java, C, Visual Basic, and even scripting tools such as Python, Ruby, and Lua, developing with it means being able to create much more software over your lifetime, and it makes maintaining, upgrading and improving that software much more manageable than it is with other development tools. Rebol's portability, small size, and unique features enable capabilities which are simply not possible using other development platforms.

To learn more about Rebol, see:

http://business-programming.com: a full 850 book that documents everything you need to know to create many types of applications with Rebol (R2). MORE THAN 100 COMPLETE WORKING PROGRAMS ARE INCLUDED IN THIS TEXT, every topic is fully explained, and links to many other supporting online docs and resources are provided. The examples are a juicy trove of useful solutions and code patterns which demonstrate how to solve many common programming problems with Rebol. An older version of this text is found at http://re-bol.com

http://re-bol.com/examples.txt: example code from the book above, without any extra code. Much of that code is included in the present text, but there are quite a few additional full applications in the examples.txt link.

19.1 R3 Links

http://learnrebol.com: learn about the newest version of Rebol (Saphir R3 and R3-GUI), which is open source and available for Android mobile platforms, as well as desktop and server environments.

The links below are critically important R3 and R3-GUI reference docs:

http://www.rebol.com/rebol3/index.html http://www.rebol.com/r3/docs/gui/gui.html http://development.saphirion.com/rebol/r3gui/ (backed up at http://re-bol.com/saphirion-r3guidocs.zip) https://github.com/saphirion/documentation

It's important to understand that R3 is an open source project, most actively developed now by the group at saphirion.com . R3 docs found on rebol.com are generally helpful and correct, but are not necessarily up to date with improvements made by Saphirion, especially for non-core features such as GUI, protocols, ports to new OS platforms, etc.

19.2 Rebolforum.com

Beyond the list of supporting documents and web sites links in the business-programming.com tutorial, a collection of currently important links is maintained at http://rebolforum.com/index.cgi?f=printtopic&permalink=Nick19-Jan-2014/18:29:47-8:00&archiveflag=new.

19.3 Rebol.org

Rebol.org maintains a searchable history of several hundred thousand posts from both the mailing list and AltME, along with a rich script archive. You can typically find answers to just about any question, already in the archives.

19.4 Forums and The AltME Community

To ask a question directly of other REBOL developers, you can post forum messages:

http://rebolforum.com

http://chat.stackoverflow.com/rooms/291/rebol-and-red

But the Rebol community is most active using this program:

AltME

Download it and set up a user account for the "Rebol4" world. Just follow the instructions at http://www.rebol.org/aga-join.r. Joining AltME and getting to know the community is the best way to ask questions and to find Rebol related support.