Rebol Programming For The Absolute Beginner By: Nick Antonaccio Updated: 3-1-09 ---------------------- 68 YouTube video tutorials that follow this text (10 hours of video) are available here. A MORE CONCISE version of this tutorial is available here (practical fundamentals - just the facts). For an even shorter introduction, try this program for children (it's also suitable for adults who want a simple quick-start). ===For New Programmers and for New Rebolers This tutorial demonstrates how easy it is to accomplish real world programming goals with a flexible and powerful language called Rebol. The text aims to teach average users to program computers to do useful things, without the long and difficult learning curve imposed by other programming languages. If you're an experienced programmer, it's strongly recommended that you read this more concise tutorial. You'll be amazed at Rebol's compact code and simple cross-platform usability. If you're a beginner, you won't find another tutorial, book, or language that enables you to accomplish more useful programming tasks within a few hundred pages. Rebol is more productive than anything else, anywhere. A downloadable package of 11 code examples from this tutorial is available at: http://musiclessonz.com/rebol_tutorial_examples.zip The zip file contains screen shots and executables demonstrating what can be easily accomplished by a casual, part-time Rebol coder, within several weeks of study. Here are a few examples: =image rebol_tutorial/little_email_client.png =image rebol_tutorial/menu.png =image rebol_tutorial/web_cam.png =image rebol_tutorial/r3D.png =image rebol_tutorial/textris.png =image rebol_tutorial/listview_database_a.png =image rebol_tutorial/freecell2.png =image rebol_tutorial/instant_messenger.png =image rebol_tutorial/image_effector.png =image rebol_tutorial/gcdm.png =image rebol_tutorial/calendar.png =image rebol_tutorial/chat_room.png =image rebol_tutorial/calc.png A YouTube video version of this tutorial is available at http://musiclessonz.com/rebol_video_links.html The concepts demonstrated throughout the tutorial will help new programmers understand other languages and programming tools, and will lay the groundwork for novices to acquire essential coding skills. Even novices will be able to actually create useful Rebol programs in the first few days. And Rebol isn't limited to certain types of applications or operating systems. Code written in Rebol is able to run unchanged on over 40 operating systems, and can be used to build an enormous variety of user applications with modern graphics, CGI interfaces, network functionality, database connectivity, and much more. If you're used to other development environments, you'll find that Rebol is a beautifully designed language, contained in a dramatically small and efficient package that elegantly replaces many other mainstream tools. Learning Rebol is productive, enjoyable, and thought provoking for both new and experienced coders. No matter what your experience level, learning Rebol will save you time and frustration at some point in your programming endeavors. It's a fantastically versatile little tool for which you'll find many uses. ===Some Perspective for Beginners Before diving into raw mechanics, here's a fundamental perspective that's helpful to understand: Modern computers, operating systems and programs all do very basic things, in limited ways, with a limited scope of data types: # They let users input various types of data: text, images, sounds, video, etc. # They let users save, retrieve, organize, share/transfer, manipulate, alter, view and otherwise deal with that data in useful ways. Everything that can be done with a modern computer basically involves manipulating text and non-text data (non-text data is called "binary" data). In the current state of modern computing, data of all types is typically input, manipulated, and returned via graphical user interfaces such as Windows program interfaces, web forms displayed in browsers, and other keyboard/mouse driven "GUI"s. Data is saved on local hard drives and storage devices (CDs, thumb drives, etc.) and on remote web servers, and is typically transferred via local networks and Internet connections. Knowing how to control those familiar computing elements to allow users to manipulate data, is the goal of learning to program. It doesn't matter whether you're interested in writing business applications to work with inventory and scheduling (text data), programs to alter Internet web pages or emails (text and image data), programs to organize or play music (binary data), programs to transfer files across networks (text and/or binary data), programs to broadcast video and sound across the Internet (rapidly transferred sequential frames of binary data), programs to control robotic equipment, programs to play games, etc... They all require learning to input, manipulate, and return data of some sort. You can do all those things with Rebol, and once you've done it in one language, it's easier to do with other specialized languages and programming tools. Rebol handles common user interfaces and data types easily and intuitively. It allows programmers to quickly build graphic interfaces to input and return all common types of data. It can easily manipulate text, graphics, and sounds in useful ways, and it provides simple methods to save, retrieve, and share data across all types of hardware, networks, and the Internet. That makes it a great way to begin learning how to program. For more information about the useful qualities of Rebol, see http://musiclessonz.com/rebol.html#section-1. ===Using the Rebol Interpreter to Speak to the Computer - "Hello World" The Rebol interpreter is a program that runs on your computer. It translates written text organized in the Rebol language syntax ("source code") to instructions the computer understands. One of the great things about Rebol is that it's a very small program, contained in a single file. There are versions that run on just about every type of computer and operating system (Windows PC, Macintosh computer, Linux web server, etc.), without any complicated install process. It lets you speak to all those machines using the same language. You just download the Rebol interpreter program, feed it a text file full of code, and the machine does what you want, with the data you want. It's easy to use and only about 1/2 meg to download (less than a minute even on a slow dialup connection). To get the free Rebol interpreter for Microsoft Windows, go to http://rebol.com/view-platforms.html and download the view.exe file for Windows - it's clearly marked, just click it with your mouse and save it to your hard drive. When you run view.exe for the first time, you can install it if you want, but you don't have to. Just follow the instructions on screen. Once you've got the Rebol interpreter downloaded and running on your computer, click the "Console" icon, and you're ready to start typing in Rebol programs. To create your first program, type the following line into the Rebol interpreter, and then press the [Enter] (return) key on your keyboard: alert "Hello world!" =image rebol_tutorial/hello.png Before going any further, give it a try. Download Rebol, and type in the code above to see how it works. It's simple and it only takes a moment. If you want to run Rebol on any other operating system, just select, download and run the correct file for your computer. It works the same way on every operating system. ---Five Short Examples To whet your appetite, here are 5 tiny GUI programs that demonstrate just how potent Rebol code is. The first example is a fully functional web page editor. You can use it to edit html pages and other text files on any web site ftp server: view layout [ h1 "Enter your ftp info, then click 'load' to download and edit a file:" p: field 600 "ftp://user:pass@website.com/public_html/filename.html" h: area 600x440 across btn "Load" [h/text: read (to-url p/text) show h] btn "Save" [write (to-url p/text) h/text] ] =image rebol_tutorial/example_1.png Here's a classic graphic sliding tile game: view center-face layout [ origin 0x0 space 0x0 across style p button 60x60 [ if not find [0x60 60x0 0x-60 -60x0] face/offset - empty/offset [exit] temp: face/offset face/offset: empty/offset empty/offset: temp ] p "A" p "B" p "C" p "D" return p "E" p "F" p "G" p "H" return p "I" p "J" p "K" p "L" return p "M" p "N" p "O" empty: p 200.200.200 edge [size: 0] ] =image rebol_tutorial/example_2.png Here's a little painting program: view layout [ h1 "Paint with the mouse:" scrn: box black 400x400 feel [ engage: func [face action event] [ if find [down over] action [ append scrn/effect/draw event/offset show scrn ] if action = 'up [append scrn/effect/draw 'line] ] ] effect [draw [line]] btn "Save" [ save/png %/c/painting.png to-image layout [ origin 0x0 box black 400x400 effect pick get scrn 9 ] alert "Saved to C:\painting.png" ] btn "Clear" [scrn/effect/draw: copy [line] show scrn] ] =image rebol_tutorial/example_3.png Here's an even more compact version of the above program: view layout[h1 "Paint:" s: area 700x500 feel[engage: func[f a e][ if a = 'over[append s/effect/draw e/offset show s]if a = 'up[ append s/effect/draw 'line]]]effect[draw[line]]b: btn "Save Image"[ save/png %a.png to-image s alert "Saved to 'a.png'"]btn "Clear"[ s/effect/draw: copy [line] show s]] =image rebol_tutorial/example_4.png And just to take that example to the limit, here's the smallest painting program you'll ever see, in any programming language: view layout[s: area feel[engage: func[f a e][if a = 'over[append s/effect/draw e/offset show s]]]effect[draw[line]]] =image rebol_tutorial/example_5.png Here's a short program that uses Rebol's parsing and networking abilities to display the current WAN and LAN IP addresses of your computer: parse read http://whatsmyip.org/ [thru copy my-ip to ] parse my-ip [thru "Your IP is " copy stripped-ip to end] alert to-string rejoin ["WAN: " stripped-ip " ---- LAN: " read join dns:// read dns://] =image rebol_tutorial/example_6.png Here's a little email client you can use to read and send emails to/from any pop/smtp server (edit the first line so it contains your personal email account info): set-net [user:pass@website.com smtp.website.com pop.website.com] view layout[ h1 "Send:" a: field "user@website.com" s: field "Subject" b: area btn "Send"[ send/subject to-email a/text b/text s/text alert "Sent" ] h1 "Read:" m: field "pop://user:pass@website.com" btn "Read"[editor read to-url m/text] ] =image rebol_tutorial/example_7.png Try pasting those examples into the Rebol interpreter to see what just a little Rebol code can do (those programs take up a total of less than 1 printed page of code). Before the end of this tutorial you'll know exactly how they all work, and much more... ===First Code Examples - Creating a GUI Window Now for some code! Computer programs typically use Graphical User Interfaces ("GUI"s) to get data from the user and to display data to the user. GUIs work intuitively, and modern computer users are familiar with them. They contain clickable buttons, text entry fields, menus, images, and other "widgets" that allow the user to interact with the computer. Windows programs are GUIs. Web pages are also GUIs. Users click buttons with the mouse pointer to perform actions, select settings from menus, type text data into fields, etc. Most modern programming languages include some facility to build graphic interfaces that can be used to interact with the user. Rebol makes GUI creation easier than any other language. To create a simple GUI window, just type the following line into the Rebol interpreter, and press [ENTER]. Notice the "view layout" words in the examples below - you'll use them every time you create a GUI interface in Rebol: view layout/size [] 400x300 =image rebol_tutorial/example_8.png That line of code creates a window 400 pixels across and 300 pixels down (pixels are dots on the computer screen). It doesn't do anything yet, but the GUI window can be moved around the screen, minimized and closed (with the "X" in the upper right hand corner), just like any other Windows program. In other programming languages, just creating a window like that can take several pages of code and lots of preliminary understanding. Rebol makes it easy. To add a button to the above GUI, type the following code. Notice that the word "button" has been added between the brackets: view layout/size [button] 400x300 =image rebol_tutorial/example_9.png Now the GUI has a generic blue button that you can click with the mouse. To add some text to the button, type the following code. Notice that the text "Click Me" has been added after the button: view layout/size [button "Click Me"] 400x300 =image rebol_tutorial/example_10.png To make the button do something, type the following code, and then click the GUI button with your mouse pointer. Notice that the text [alert "Clicked!"] has been added after the button text: view layout/size [button "Click Me" [alert "Clicked!"]] 400x300 =image rebol_tutorial/example_11.png Now when you click the button in your GUI, the computer responds by alerting you with the message "Clicked!". To make the program do something a bit more interactive, type the following, and then click the GUI button once again. Notice the "data: request-text" addition at the beginning of the line. That code requests some text from the user and assigns it to the word "data", so it can be referred to and used in the program: data: request-text view layout/size [ button "Click Me" [alert data]] 400x300 =image rebol_tutorial/example_12a.png =image rebol_tutorial/example_10.png =image rebol_tutorial/example_12.png The code above is split onto two lines so that it fits within the width of this web page, but it can be typed into the Rebol interpreter as a single line. That one line is all it takes to create a program which gets some data from the user, creates a graphic user interface that waits for user interaction, and does something with the input data (displays it in a little dialog box). Next, we'll save some data to your computer's hard drive. Type in the following code, and click "yes" if the Rebol interpreter asks for your permission to write to the hard drive. Notice the "write %/c/data.txt data" added to the end of the line. That code writes the data collected from the user, to a file on the C: drive called "data.txt" data: request-text view layout/size [ button "Click Me" [alert data]] 400x300 write %/c/data.txt data If you look on your computer's C: drive after closing the GUI, you'll see that there now exists a text file (C:\data.txt) containing the text you typed into the program. (If you're working in an operating system other than Windows, you'll need to change the "c" character in the above line to refer to a root directory on your hard drive). With that one line of code, you've got a working program that actually does something useful. It gets, displays, and saves some data from a user, using familiar GUI interactions. You could adjust it to store phone numbers, usernames/passwords, or any other useful information. It's easy - and things only get more interesting from there! Rebol is great at dealing with all types of common data - not just text. You can easily display photos and other graphics in your GUIs, play sounds, display web pages, etc. And it's just as good at dealing with data transferred across networks and the Internet. Here's some code that downloads an image from a web server and displays it in a GUI - notice the "view layout" words again: view layout [image load http://rebol.com/view/bay.jpg] =image rebol_tutorial/example_14.png The word "image" inside the brackets displays a picture in the GUI. The word "load" downloads the image to be displayed. Rebol allows many built-in effects to be applied to images. Notice the "effect [effect type]" code in the following examples: view layout [image load http://rebol.com/view/bay.jpg effect [Grayscale]] view layout [image load http://rebol.com/view/bay.jpg effect [Emboss]] view layout [image load http://rebol.com/view/bay.jpg effect [Flip 1x1]] =image rebol_tutorial/example_15.png =image rebol_tutorial/example_16.png =image rebol_tutorial/example_17.png Here's how you can read that same file from the web server and save it to your C: drive. The "/binary" modifier is used whenever reading or writing binary (non-text) data: write/binary %/c/bay.jpg read/binary http://rebol.com/view/bay.jpg Now you can read the image directly from your hard drive and display it in a GUI. Just type in the code below. Notice the "view layout" words again, and this time in between the brackets, the file is loaded from the local C: drive: view layout [image load %/c/bay.jpg] =image rebol_tutorial/example_14.png It's important to note here that you could also use that image in other graphic applications on your computer - including other programs that aren't written in Rebol. You could, for example, open that file in an image editing program, or attach it to an email and send it to a friend. It's critical to understand that data can be interoperable between languages, programming tools, operating systems, and other connected domains. As you learn more about programming, you may find specialized tools that accomplish certain programming goals easily and effectively. You can trade data between various tools by just saving it to a shared storage medium. In that way, all programming languages and tools can be used together to accomplish complex goals. That's a key concept to keep in mind when learning about general programming. Very little technology is truly new. The basis for most computing applications has been around for several decades (hard drives for storing files within directory structures, hardware for inputting and outputting text, graphic, audio, and other data, networks for transferring data between machines, etc.). Those base components haven't changed too dramatically. They've simply improved in speed and capacity - and the software tools used to work with them have evolved to allow programmers to do high level things more quickly and easily. An adept programmer writing an application to deal with large amounts of text data shared across the Internet, however, will realize that the data all still resides in files on a hard drive somewhere, in a machine connected to a network, and that data can be accessed by various old fashioned programming means - even if it was created and put there by a program using cutting edge database technology, displayed in a flashy new graphic interface, and transferred over broadband wireless connections. It's still just text data saved in a file somewhere, and it can be manipulated via virtually any language that provides access to networks and text files! Keep that in mind as your exposure to various programming tools expands... ===More Examples Below are some more short examples of reading, writing, and manipulating data on the hard drive and Internet, and interacting with the user. Type them into the Rebol interpreter to familiarize yourself with a bit more of the Rebol language. Before going on, you may want to configure Rebol to open straight to the interpreter console. Click the "User" menu on the Rebol Viewtop, and uncheck "Open Desktop On Startup". That'll save you the trouble of clicking the "Console" button every time you start Rebol. =image rebol_tutorial/viewtop.png =image rebol_tutorial/viewtop2.png The following line displays the current day and time in the interpreter: print now =image rebol_tutorial/example_19.png The word "print" displays text data directly in the Rebol interpreter. The word "now" refers to the current date and time. The following line performs some mathematical computations, and displays the result: print (10 + 12) / 2 =image rebol_tutorial/example_20.png The following code asks the user to choose a file on the hard drive: request-file =image rebol_tutorial/example_21.png The code below allows the user to choose a color: request-color =image rebol_tutorial/example_22.png The following code asks the user a yes-no question: request "Are you having fun yet?" =image rebol_tutorial/example_23.png Here's a nice way to let the user select a date: request-date =image rebol_tutorial/example_24.png The code below requests a username and password from the user: request-pass =image rebol_tutorial/example_25.png The following code opens your computer's web browser and displays the indicated web page: browse http://rebol.com =image rebol_tutorial/rebol_home.png The following code launches Rebol's built in text editor, and opens the file c:\test.txt editor %/c/test.txt =image rebol_tutorial/example_26.png Notice the percent character ("%") in the example above. In Rebol, that character is used to represent all file labels. Because Rebol can be used on many operating systems, and because those operating systems all use different syntax formats to refer to drives, paths, etc., Rebol uses the universal format: %/drive/path/path/.../file.ext . For example, "%/c/windows/notepad.exe" refers to "C:\Windows\Notepad.exe" in Windows. Rebol converts that syntax to the appropriate operating system format, so that your code can be written once and used on every operating system, without alteration. The following line sends an email to user@website.com, containing the text "Hi user. How are you doing?". Try replacing the username and website with your own email address (If you downloaded and ran Rebol without actually installing it, you'll need to run the configuration wizard in order to send the email. To do that, type "install" and follow the instructions.): send user@website.com "Hi user. How are you doing?" The line below sends a web page to user: send user@website.com read http://www.rebol.com The code below displays the contents of user's email inbox: print read pop://user:pass@website.com The following line uploads a single file to user's web server using ftp: write/binary ftp://user:pass@website.com read/binary %file The following uploads an entire directory of files to user's web server: foreach file load %./ [if not dir? file [write/binary join ftp://user:pass@website.com/ file read/binary file]] It all looks a lot like spoken English, doesn't it? You just need to type things in correctly, and the computer does what you want. The more of the language you learn, the more you'll be able to make the computer do your bidding... Easy, right? ===A Quick Comparison To provide a quick idea of how much easier Rebol is than other languages, here's a short example. The simplest code to create a basic Rebol GUI window was presented earlier: view layout/size [] 400x300 It works on every type of computer, in exactly the same way. Code for the same simple example is presented below in the popular programming language "C++". It does the exact same thing as the Rebol one-liner above, except it only works in Microsoft Windows. If you want to do the same thing on a Macintosh computer, you need to memorize a completely different page of C++ code. The same is true for Unix, Linux, Beos, or any other operating system. You have to learn enormous chunks of code to do very simple things, and those chunks of code are different for every type of computer. Furthermore, you typically need to spend a semester's worth of time learning very basic things about coding format and fundamentals about how a computer thinks before you even begin to tackle useful basics like the code below: #include /* Declare Windows procedure */ LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM); /* Make the class name into a global variable */ char szClassName[ ] = "C_Example"; int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nFunsterStil) { HWND hwnd; /* This is the handle for our window */ MSG messages; /* Here messages to the application are saved */ WNDCLASSEX wincl; /* Data structure for the windowclass */ /* The Window structure */ wincl.hInstance = hThisInstance; wincl.lpszClassName = szClassName; wincl.lpfnWndProc = WindowProcedure; /* This function is called by windows */ wincl.style = CS_DBLCLKS; /* Catch double-clicks */ wincl.cbSize = sizeof (WNDCLASSEX); /* Use default icon and mouse-pointer */ wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION); wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION); wincl.hCursor = LoadCursor (NULL, IDC_ARROW); wincl.lpszMenuName = NULL; /* No menu */ wincl.cbClsExtra = 0; /* No extra bytes after the window class */ wincl.cbWndExtra = 0; /* structure or the window instance */ /* Use Windows's default color as window background */ wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND; /* Register window class. If it fails quit the program */ if (!RegisterClassEx (&wincl)) return 0; /* The class is registered, let's create the program*/ hwnd = CreateWindowEx ( 0, /* Extended possibilites for variation */ szClassName, /* Classname */ "C_Example", /* Title Text */ WS_OVERLAPPEDWINDOW, /* default window */ CW_USEDEFAULT, /* Windows decides the position */ CW_USEDEFAULT, /* where the window ends up on the screen */ 400, /* The programs width */ 300, /* and height in pixels */ HWND_DESKTOP, /* The window is a child-window to desktop */ NULL, /* No menu */ hThisInstance, /* Program Instance handler */ NULL /* No Window Creation data */ ); /* Make the window visible on the screen */ ShowWindow (hwnd, nFunsterStil); /* Run the message loop. It will run until GetMessage() returns 0 */ while (GetMessage (&messages, NULL, 0, 0)) { /* Translate virtual-key messages into character messages */ TranslateMessage(&messages); /* Send message to WindowProcedure */ DispatchMessage(&messages); } /* The program return-value is 0 - The value that PostQuitMessage() gave */ return messages.wParam; } /* This function is called by the Windows function DispatchMessage() */ LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) /* handle the messages */ { case WM_DESTROY: PostQuitMessage (0); /* send a WM_QUIT to the message queue */ break; default: /* for messages that we don't deal with */ return DefWindowProc (hwnd, message, wParam, lParam); } return 0; } Yuck. Back to Rebol... ===Understanding Variables and Functions All programming languages make use of two important features: variables and functions. Functions are commands that tell the computer to do something. They're similar to verbs in spoken language. You've used some functions already in the earlier examples. For example, "print" is a function - it can be thought of as a verb that represents some action. Variables are names given to data to be input, stored, manipulated, displayed, etc. They're similar to nouns in spoken language. For example, "now" can be thought of as a variable. It's a noun referring to a piece of data (the current time). Functions and variables must be used in a syntax defined by the programming language you're using. No existing computer language allows you to speak freely as in the natural language sentence "show the user some photos of their children". You have to write the commands in a more specific way - using a syntax that invariably involves variables and functions. As a programmer, to get the computer to "show the user a photo", you'd: # assign the picture filename to a variable (noun) # use a function (verb) to display the image referred to by the variable # assign the function action to a button or some other graphic widget in a GUI (which is itself created by arranging variables and functions in a specific order) Rebol does many things in as "high level" a way as can be expected - as close to speaking to the computer in human English as is currently possible, but it still requires specific syntax and structure. All contemporary computer languages do. If you want to learn how to program in any language, you'll need to learn how to use variables and functions in the grammar the language defines. That's at the core of all programming. In Rebol, variables and functions are assigned to "words". ===Rebol Words If you want to give a label to some data - so that it can be used in your program - you must assign it a word. The same is true for actions performed by functions. There are many function words built into Rebol that represent common actions ("print", "alert", "request-date", etc). You've seen a number of those built-in words already in the earlier examples. To create your own functions, you take previously created function words and variables, group them together in a specific order that accomplishes what you want, and assign a word to that collection of code. Then you can refer to that group of actions and/or related data, using the word you've assigned to it. Learning the function words that are already defined in a programming language is a big part of learning to program. Those words are the existing vocabulary of the language, and in order to work correctly, they typically expect some other words to follow afterwards, in a specific order and format. The word "write", for example, writes data to a storage device (hard drive, flash drive, web server, etc). It's a function that performs an action, and expects the name of a file to be written to the hard drive, and then the name of the data to write to that file. It's gotta be written in that order: write %/c/text.txt "This is some random text" If you type the above line in the wrong order, it won't work: write "This is some random text" %/c/text.txt ; WRONG If you type it in backwards, Rebol won't understand the syntax, and you'll receive an error. =image rebol_tutorial/example_27.png ===Before You Get Too Far - Learn by Doing To learn any programming language, you'll need to type in lots of code, by rote. Be sure to manually key in the examples in this tutorial, not just to see them work, but to get used to speaking the language. Avoid copying and pasting if you really want to learn. At first, you'll learn to code in the same way you'd learn a new spoken language - by immersing yourself in the words and phrases of the language (even if at first you don't understand what's being said). Repeating sentences by rote is a natural way to get a feel for how a language sounds and works. Certain words, phrases and patterns eventually become familiar, and the language syntax becomes clear and more intuitive. Seeing the examples run is absolutely essential. It helps form associations between things the computer does, and chunks of code that make those things occur. Just reading through the code isn't enough - seeing how it executes is the key. If you don't have time to manually type in code, at very least paste it into the interpreter. Because programming languages require more specific syntax than spoken languages, learning the exact grammar is more important than in spoken language. Simple examples like the ones that follow are easy to remember - just like simple phrases memorized in spoken foreign languages. To become more fluent, however, the correct order of words, characters and other elements must be typed in exactly, in a much more specific way than components of spoken language. The interpreter is not intelligent enough to "guess" what you intend to do, and if you get the grammar wrong (even one character), it'll interpret your code incorrectly and do something different then you want. Sometimes, you'll get an error, and the interpreter will tell you where you got the grammar wrong in your code. Sometimes your program just won't work correctly. Getting intimately familiar with the syntax, from the beginning, will help you avoid those problems. ===GUI Words and Grammar - Some More Depth As you saw in the earlier examples, the words "view layout", followed by two brackets ("[]") can be used to display a Graphic User Interface in Rebol. You can put elements that you want to see in the GUI, inside the brackets. Rebol contains words that display all the common graphic elements used in GUIs. Try typing in the following code examples: view layout [button] =image rebol_tutorial/example_28.png view layout [field] =image rebol_tutorial/example_29.png view layout [text "Rebol is really pretty easy to program"] =image rebol_tutorial/example_30.png view layout [text-list] =image rebol_tutorial/example_31.png view layout [ button field text "Rebol is really pretty easy to program" text-list check ] =image rebol_tutorial/example_32.png Notice that the words can be separated by "white space" inside the brackets. Extra spaces, carriage returns, and other empty characters are ignored by the interpreter. Tab stops are traditionally used to indent the lines within brackets, but they're not required. More descriptive characteristics about the graphic elements can be included directly after each of their respective words. Such modifiers are called "facets" in Rebol, and they allow you to adjust all characteristics of every type of graphic widget (size, color, displayed text), etc. Try typing in the code below - it's the same as the above code, with some additional facet characteristics: view layout [ button red "Click Me" field "Enter some text here" text "Rebol is really pretty easy to program" purple text-list 400x300 "line 1" "line 2" "another line" check yellow ] =image rebol_tutorial/example_33.png ---Actions IMPORTANT: If you want a graphic element to perform an action, just put the action word(s) in brackets after it. When the GUI element is clicked with the mouse or otherwise activated, the action will be performed. Type in the following code to see how it works: view layout [button [alert "You clicked the button."] ] =image rebol_tutorial/example_34.png =image rebol_tutorial/clicked.png view layout [button red "Click Me" [alert "You clicked the red button."]] =image rebol_tutorial/example_35.png =image rebol_tutorial/example_35b.png 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 line" [alert value] check yellow [alert "You clicked the yellow check box."] button "Quit" [quit] ] =image rebol_tutorial/example_36a.png =image rebol_tutorial/example_36b.png =image rebol_tutorial/example_36c.png =image rebol_tutorial/example_36d.png =image rebol_tutorial/example_36e.png =image rebol_tutorial/example_36f.png Notice how the word "value" refers to the selected item in the text-list, and to the text contained in the text input field. Alternate actions (i.e., those triggered by a right click of the mouse) can be included for any graphic widget. Just enclose them in a second block, surrounded by brackets: view layout [button [alert "left click"] [alert "right click"]] Here are some other GUI elements used in the Rebol language: view layout [ backcolor white h1 "More GUI Examples:" box red 500x2 bar: progress slider 200x16 [bar/data: value show bar] area "Type here" drop-down across toggle "Click" "Here" [print value] rotary "Click" "Again" "And Again" [print value] choice "Choose" "Item 1" "Item 2" "Item 3" [print value] radio radio radio led arrow return text "Normal" text "Bold" bold text "Italic" italic text "Underline" underline text "Bold italic underline" bold italic underline text "Serif style text" font-name font-serif text "Spaced text" font [space: 5x0] return h1 "Heading 1" h2 "Heading 2" h3 "Heading 3" h4 "Heading 4" tt "Typewriter text" code "Code text" below text "Big" font-size 32 title "Centered title" 200 across vtext "Normal" vtext "Bold" bold vtext "Italic" italic vtext "Underline" underline vtext "Bold italic underline" bold italic underline vtext "Serif style text" font-name font-serif vtext "Spaced text" font [space: 5x0] return vh1 "Video Heading 1" vh2 "Video Heading 2" vh3 "Video Heading 3" vh4 "Video Heading 3" label "Label" below vtext "Big" font-size 32 banner "Banner" 200 ] =image rebol_tutorial/example_38.png The examples above demonstrate how Rebol creates GUIs that can be used to input and display data in a variety of ways that are familiar to users. They can be customized using facets, and they can perform actions. That's a big part of building a typical modern computer program! For more information about GUI design, see http://rebol.com/docs/easy-vid.html and http://rebol.com/docs/view-guide.html. To make your GUIs do useful things, you need to learn more about making the language manipulate data in useful ways... ===Creating Your Own Variable Words Just like spoken languages, programming languages are malleable and expressive. There's never only one way to write a given program. You need to choose and use your own words, and you need to organize them so they have the meaning and function you intend. Just like in spoken language, you have to think about what you're trying to say, and organize your thought process to say it. In Rebol you have the additional benefit of being able to create your own language words to express actions and to label data. Words are created and assigned to variables and functions in Rebol by the use of the colon (":") character. You can use any word you want to refer to any specific data or action(s). For example, If you want to use the word "picture" to refer to an image file on the Internet, you could do the following: picture: load http://rebol.com/view/bay.jpg The above line creates a variable (label) that can be used in your program anywhere you want to read and use the file located at http://rebol.com/view/bay.jpg . The built-in Rebol word "load" does the actual work of going out to the web site address and collecting the image data. Now you can use the word "picture" to refer to the above image. Display it in a GUI using the following code: view layout [image picture] =image rebol_tutorial/bay.png The words "view", "layout", and "image" are built into Rebol, and the word "picture" is now just as valid, because it's been defined to the interpreter. Because the word "picture" is _variable_, you can also redefine and _change_ the data referred to by it: picture: load http://rebol.com/view/demos/palms.jpg Now, when you use the word "picture" in your program, it refers to a different file at a different Internet location. Writing the same GUI code now displays that different photo: view layout [image picture] =image rebol_tutorial/palms.png You can also make the word "picture" refer to a file on your hard drive, or anywhere else you'd like: picture: load %/c/bay.jpg Here are some more examples of creating and using variable words. Type them into the Rebol interpreter to see how they work, and to understand how assigned words can take the place of _any_ data. NOTE: anything after a semicolon in Rebol code is ignored by the interpreter. It's used to include human readable comments in the code. You don't need to type any of the comments into the interpreter: acolor: "blue" alert acolor ; alerts you with a dialog box displaying the text "blue" =image rebol_tutorial/blue1.png =image rebol_tutorial/blue.png print acolor ; prints the word "blue" in the Rebol interpreter =image rebol_tutorial/blue2.png anumber: 12 print anumber ; prints the number 12 in the interpreter computation: (10 + 12) / 2 print computation ; prints the answer filename: request-file print filename ; prints the name of the user chosen file =image rebol_tutorial/filename1.png =image rebol_tutorial/filename2.png =image rebol_tutorial/filename3.png chosen-color: request-color print chosen-color answer: request "Are you having fun yet?" print answer pick-a-date: request-date print pick-a-date userpass: request-pass print userpass webpage: http://rebol.com browse webpage file: %./test.txt editor file ; launches Rebol's built in text editor and opens the filename ; assigned above email-address: user@webpage.com message: "Hi Luke. How are you doing?" send email-address message The key is to be aware of the colon character (":"). It's the thing that tells Rebol to create a new variable word and assign it to some value that'll be used later. ===Blocks and Series In Rebol, you can use words to represent multiple pieces of data grouped together, collections of other word variables, and other programming elements. Just surround the data in brackets. Type in the code below to see how it works: somecolors: ["red" "yellow" "blue" "black"] ; "somecolors" is now a defined word used to represent the entire ; data block enclosed in brackets. print somecolors =image rebol_tutorial/somecolors.png In Rebol, collections like this are called "blocks", and they're very important. In fact, they're the primary organizational unit in Rebol and the main structure in which data is stored in the language. That's very important - remember, programming is all about inputting, storing, retrieving and otherwise manipulating data. In Rebol, ANY type of data can be assigned a word, and blocks can contain any combination of words and raw data. Blocks provide a simple way of managing every conceivable type of data, and that's at the heart of all programming. Learning to work with blocks is therefore a fundamental part of learning Rebol. You've already used blocks - brackets were snuck in earlier to display GUI elements and to perform actions on them. Any group of words surrounded by brackets forms a block, and you can assign a word to refer to that block. Words referring to complex blocks of data can be likewise grouped together into other blocks simply by surrounding the words with brackets. This makes dealing with very complex and useful data structures very easy in Rebol. Type in the code below to see how an entire GUI layout can be built and represented by using one word label: gui-layout: [button field text-list] "gui-layout" now refers to that entire block of code. You can display it using the standard "view layout" syntax: view layout gui-layout =image rebol_tutorial/gui_layout.png Blocks of data can also be spread out over several lines, and can be separated by extra white space. Just surround the elements with brackets: gui-layout2: [ button red "Click Me" field "Enter some text here." text "Rebol is really pretty easy to program." purple text-list 400x300 "line 1" "line 2" "another line" check yellow ] Now display it in a GUI: view layout gui-layout2 =image rebol_tutorial/gui_layout2.png Layout blocks much bigger than that can be stored in a single word! NOTE: It's standard practice to indent compound blocks with consecutive tab stops. Starting and ending brackets are typically placed at the same indentation level. This is conventional in most programming languages, because it makes complex code easier to read. For example, the compound block below: [blue red green [1 2 4 [jan feb march [monday tuesday wednesday]]]] can be written a bit more clearly as: [blue red green [1 2 4 [jan feb march [monday tuesday wednesday] ] ] ] Indentation is not required, but it's very helpful when dealing with more intricate programming structures. Here's a simple data table example, containing schedule information within a block: schedule: [ ["John Smith" "Monday" "3:00 pm"] ["Dave Jones" "Tuesday" "11:00 am"] ["Janet Duffy" "Wednesday" "4:45 pm"] ] You can display the above block in a GUI, using the built-in Rebol word "list". Notice the "data schedule" code in the last line below - it inserts the entire schedule block defined above as data to be displayed in the list widget (the line "across text 150 text 150 text 100" is just syntax expected by the list widget to define its graphic layout): view layout [ vh2 "This Week's Appointments:" list 600x400 [ across text 150 text 150 text 100 ] data schedule ] =image rebol_tutorial/example_40a.png The beginnings of a graphical database application, in several lines of code... Not tough at all! Using blocks to store collections of data is similar to using variable words to store single values. Once a word is assigned to represent a block of data, that word can be used in place of the actual data, to represent it in code. Blocks are simply able to respresent larger collections/varieties of data. You don't have to worry about how the computer stores or works with the data in memory. You can simply label the data and use it in it's natural human-understandable form, using a single consistent bracket syntax to deliniate each group of values. That's very powerful, and it's much easier than in other programming languages. In most other languages, formatting and using data in code requires quiet a bit more preparation, management, and a variety of code structures to handle different types of data. Many of Rebol's built in words help you manipulate data stored in blocks. Type in the following code to see how the "sort" word works: somecolors: ["red" "yellow" "blue" "black"] sortedcolors: sort somecolors =image rebol_tutorial/sortedcolors.png "Sort" is a built in function word that alphabetically (/ordinally) sorts the elements of a given block. The line above creates the newly defined word "sortedcolors", and assigns it to the sorted block of words contained in "somecolors". print sortedcolors ; This code displays the sorted block of text. print first sortedcolors ; "first" is another built-in word. ; It selects the first item in a given block. find somecolors "red" ; "find" is a built in word that searches for data within a block. =image rebol_tutorial/sortedcolors2.png You can easily save blocks of data to your hard drive, read them from a web server, and perform other file operations with them. write %/c/colors.txt somecolors ; writes the entire block of text represented by "somecolors" ; to a text file called colors.txt on the C: drive. Here's an interesting twist, demonstrating how Rebol can easily mix datatypes within a block: an-image: load http://rebol.com/view/bay.jpg ; downloads an image from the Internet and assigns the ; word "an-image" to it. append sortedcolors an-image ; "append" adds the downloaded image to the end of the data block ; currently containing the simple text words defined above. ; Now the block contains both text and binary image data, ; all assigned to a single word - not a problem in Rebol! Now you can select items from that new block: print first sortedcolors ; prints the first item in the data block - the text "black". view layout [image fifth sortedcolors] ; displays the fifth item in the data block - ; the image downloaded above - in a simple GUI. That should get you thinking a bit. You can store images, sounds, text, complex data structures, and anything else you want in a block, all with equal ease. That's a complex ability which requires some hard core learning in most programming languages. Here's some more notation to be familiar with when working with sequential data in blocks. That type of data is called a "series" in Rebol. All blocks of data are actually series in Rebol, and can be treated as sequential lists by default: view layout [image sortedcolors/5] ; "sortedcolors/5" is another way to refer to the fifth item ; in a data block. The following example puts the above notation to work in a useful way: length-of-block: length? sortedcolors ; the built in word "length?" returns the number of items ; in the block (5 in this case). view layout compose [image sortedcolors/(length-of-block)] The word "compose" allows variables in parentheses to be evaluated and inserted as if they'd been typed explicitly into the code of the program. In the example above, the code reads as if sortedcolors/5 had been typed in manually. Another way to use variable values explicity is with the format below: view layout [image sortedcolors/:length-of-block] The colon symbol in front of a variable does the exact same thing as the compose function and parentheses above. You'll see both formats in Rebol code, so it's good to know both. The following examples demonstrate additional words used to traverse sequential series of data within blocks: insert sortedcolors "mauve" ; adds the word "mauve" to the sortedcolors block of data. remove sortedcolors ; removes the first item from the block. head sortedcolors ; sets a position marker at the beginning of the data block next sortedcolors ; sets a position marker at the next item in the data block last sortedcolors ; sets a position marker at the last item in the data block back sortedcolors ; sets a position marker at the previous item in the data block tail sortedcolors ; sets a position marker after the last item in the data block =image rebol_tutorial/blocks.png The fact that you can mix together all types of data within a block, refer to parts of blocks by name, and access/alter data within them using built-in functions, is very powerful and useful. Blocks and variable words assigned to blocks help you store, manipulate, and refer to all the data you'll deal with in your programs. Doing so is similar to assigning nouns to groups of people, places, and things in spoken language. Within those groups, smaller groups and individual items can be named, ordered and otherwise organized - in the same way that complex database applications allow you to store and work with all types of information. The possibilities of dealing with data in that way are endless. ===Function Words It's easy to define your own function words (actions), once you know some of the built-in vocabulary of Rebol. Creating new functions is comparable to creating your own verb words in a spoken language. Just be careful not to unintentionally use words that are already defined in the Rebol language, or in your current program. That would change the meaning of the existing word. For example, you could accidentally change the meaning of the word "write" to refer to a picture on your hard drive by typing the following: write: read %/c/bay.jpg ; *** DON'T TYPE THAT IN - it'll change the meaning of the word ; "write" in the Rebol interpreter (only for the current session). ; It's an example of what not to do. *** You can protect all of the built-in Rebol words by typing in "protect-system". That'll alert you with an error and disallow any attempt to redefine native Rebol words. You still must be careful not to accidentally redefine words that you've created. Word definitions only last for the current session, so if you make a mess, all you need to do is restart the Rebol interpreter. Here's a really important concept: (drum role) ... In Rebol, you can use single words to represent entire blocks of actions (i.e., collections of function words grouped together). In fact, blocks like that form the primary code sections that make up programs in the Rebol language. Here's an example of several function words grouped together into a block (i.e., enclosed in brackets), and assigned a new function word: some-actions: [ alert "Here is one action." print "Here's a second action." write %/c/anotheraction.txt "Here's a third action." ] The above code has created a kind of super-verb that refers to several actions. You can perform the actions contained in any block using the "do" function word. To perform all the actions in the above code block, just type: do some-actions You can also include the word "does" right inside a word definition - that'll make the actions (function words) contained in the block perform automatically every time the new word is used in Rebol: more-actions: does [ alert "4" alert "5" alert "6" ] In fact, by including the "does" command right in the word definition, you've just created a new function (or "subroutine") that can be used like any other built-in action word in Rebol! You can now talk to the interpreter using that word, and it understands what you mean for it to do. After you've entered the code above, try typing the word "more-actions" into the Rebol interpreter: more-actions It's taken on a life of its own! As with variable words, the key is to be aware of the colon character - that's the symbol that actually creates the new function word. Here's an example of a useful little action word to clear the command line screen in the Rebol interpreter. cls: does [prin "^(1B)[J"] The native way to clear the Rebol interpreter's command screen is by typing "prin "^(1B)[J". That's kind of awkward to type, and even tougher to remember. Instead, we can assign the word "cls" to perform the action - just like in old Basic languages. Now just type: cls and the screen clears - that's much easier. =image rebol_tutorial/cls1.png =image rebol_tutorial/cls2.png Here's a little program that creates the new action word "send-email". It provides a simple text requestor interface for users to send email: send-email: does [ email-address: to-email request-text/title/default "Enter an email address:" "user@webpage.com" ; the above line creates the new variable word "email-address" ; "email-address" is assigned the value of the ; text input using the built in word "request-text" ; the "title" and "default" refinements customize the ; info displayed in the text requestor. message: request-text ; the above line creates the new variable word "message" ; and assigns it to some requested text send email-address message ; the above line sends the user-provided message to the ; email address given earlier alert "Your message has been sent." ] send-email ; do the routine above send-email ; do it again to send another message to someone else =image rebol_tutorial/send-email1.png =image rebol_tutorial/send-email2.png =image rebol_tutorial/send-email3.png =image rebol_tutorial/send-email4.png The above process is VERY important. It's the basis of how you perform more complex and useful actions in Rebol. Blocks of actions and blocks of data form the basis of how you program in Rebol. And performing actions upon data is what you'll do to complete all your programming tasks. In Rebol you just group bits of data together into blocks, and assign a name to refer to it. You also group functions together into blocks to perform actions upon the data, and assign a name to refer to those actions. You can even combine complete groups of data and functions into blocks that can be assigned unique word identifiers that perform complete programmatic tasks for you: store and manipulate data all with a single word that you define! That allows you to create your own unique language in any program you write, using words that you define. You could create, for example, your own language that reads something like "email tom at noon with the main news page from yahoo.com". Those words aren't built into Rebol, but they can be assigned the appropriate action and data meanings to make that sentence a completely functional piece of code that the computer understands! (It would still need to be syntactically correct and exact). Building word meanings in that way is called building a "dialect" in Rebol, and it's one of the ways Rebol is different from other languages. You could conceivably write a dialect for programming robotic devices that reads "Vacuum the floor. Move in concentric circles around the perimeter". You could write a dialect for doctors that reads "print prescription for John Smith" (Ponder for a moment the possibilities for that type of natural language capability and flexibility...). Translating the existing Rebol language to any other language is as simple as assigning built-in function/data words to words in the desired language (for example, translating the print function to French, Italian, and Spanish is as simple as typing "imprime: stampa: impresion: :print"). Other languages focus on different ways of grouping and managing functions and variables. Typically, other languages use more cryptic ways of doing things. For example, blocks that contain both fully encapsulated functions and variables are called "objects". You'll see more about "object-oriented" programming as you study Rebol and other languages in depth... ===Several Ways to Create Functions in Rebol - Passing Variables There are several built-in words in the Rebol language that allow you to create more complex function words. To create simple functions, you can use the "does" command, as described above. But some functions are more complicated than that. They perform work upon _variable_ data. For example, the following simple function displays the square root of 4. That's all it can do: sqr-four: does [print square-root 4] This type of function is similar to what you saw in the previous section. The word "sqr-four" is now assigned to the action "print square-root 4" (the word "square-root" is built into Rebol). After entering the above line in the Rebol interpreter, type: sqr-four It'll give you the expected result of 2. =image rebol_tutorial/sqr4.png Say now, however, that you now want to do something using numbers other than 4... What if you want to create a function that adds 4 to some other number, and then computes the square root of that sum? For that to work, the other number must be changeable, and therefore must be assigned a variable name. That variable name can then be "passed" to the function. The built in word "func" is used to create functions to which changeable variables can be passed. The syntax for the word "func" expects it to be followed by two blocks of code. The first block contains the name(s) of the variables to be passed. The second block contains the actions to be taken. Here's how it looks: func [names of variables to be passed] [ actions to be taken with those variables ] In the following line, a function is created in which a variable word "anumber" is named and passed. That whole function is assigned the word "sqr-var": sqr-var: func [anumber] [print square-root (4 + anumber)] Now you can use the word "sqr-var", and the Rebol interpreter knows what to do with the assigned data. Try the following code: sqr-var 12 ; prints "4", the square root of 12+4 (16) sqr-var 96 ; prints "10", the square root of 96+4 (100) =image rebol_tutorial/sqrvar.png Printing the square root of 4+somenumber may not sound so exciting to you, but it helped to illustrate one of the most important techniques employed in all modern programming languages. The process of passing variable parameters to functions is a fundamental part of all modern programming. It's perhaps the single most common element in contemporary languages, and understanding how it's done is absolutely essential. Below are some more interesting real-world examples. The following line creates a simple function to display images: display: func [filename] [view layout [image load filename]] It accepts an image filename as the passed parameter (%somefile.jpg, %somefile.gif, %somefile.png, or %somefile.bmp), and then creates a GUI to display the image. That set of actions is assigned to the word "display". Simple, right? Now you can use the word "display" like this: image1: to-file request-file/title trim { Select an image from your hard drive:} "" ; gets an image filename from the user =image rebol_tutorial/lambertville1.png display image1 ; displays the above image using the new function word =image rebol_tutorial/lambertville2.png display http://rebol.com/view/bay.jpg ; displays the image at the above url display %/c/bay.jpg ; displays an image that was saved to the ; hard drive earlier in this tutorial Once the word "display" is defined in your programs, you can use it as if it's a built in Rebol action word. Here's an example that asks the user for 2 website urls, and then opens those sites in separate brower windows: openwebsite: func [nameofwebsite] [browse nameofwebsite] ; The line above creates a new function that passes a url ; to the built-in Rebol word "browse", to open the passed ; web site name in the user's default browser. It also ; assigns the new function word "openwebsite" to that set ; of actions. website1: request-text/title "Enter a web site URL:" ; The line above assigns a new variable word "website1" to text ; returned by the built-in Rebol "request-text" function. website2: request-text/title "Enter another web site URL:" ; Gets some more text and assigns the new variable word "website2" ; to it. openwebsite website1 ; The line above uses the new "openwebsite" function word defined ; above, and passes it the "website1" variable word openwebsite website2 ; Uses the openwebsite function again, this time passing the ; website2 variable In that example, the word "openwebsite" is assigned to a new function definition. "Website1" and "website2" are labels for variables passed to that function. Below is a variation which assigns a single word to that entire process: display-website: does [ openwebsite: func [nameofwebsite] [browse nameofwebsite] website: request-text/title "Enter a web site URL:" openwebsite website ] Now you can use the single word "display-website" in your programs to do that entire block of code. display-website =image rebol_tutorial/openweb.png =image rebol_tutorial/openweb2.png Getting used to the above syntax and way of thinking is absolutely essential. Remember, dealing with all types of data is the main thing you'll do as a programmer. Passing variable data to functions that you create is the main way you'll perform actions upon data in all your programming. That's the current state of programming, and it's what you'll do in virtually every programming situation. Rebol makes it easy to deal with data, using its built in support for most common data types. Understanding how to input, manipulate, and output that data is your main objective. Using functions and variables as described above is a fundamental part of that process. ===Conditional Operations Programs often need to make decisions based on user input, program states, data content, etc. "If the user selects this option from a list, respond by executing this function". That's a common situation. "If a certain amount of time has passed, save data to the hard drive". Giving the computer a variety of actions to perform, based on a variety of expected conditions, is a fundamental programming technique used in all languages. Mathematical operators such as = < > <> (equal, less-than, greater-than, not-equal) are typically used to perform conditional operations. Type in the following code to see how a conditional operation works: if now/time > 12:00 [alert "It's after noon."] ; now/time is a variation, or "refinement" of the built-in ; function "now", that returns only the current time. Here's a more complex example: daily-calories: to-integer request-text/title {How many calories have you eaten today?} ; gets some info from the user and assigns the variable word ; "daily-calories". The built-in "to-integer" function ; helps make sure Rebol interprets that info as a number. ; The "{}" characters surrounding the title text work the ; same way as quotes, but allow the string of text to ; span several lines. Very important. if daily-calories > 2500 [alert "You need to stop eating now."] =image rebol_tutorial/calories1.png =image rebol_tutorial/calories2.png The built-in Rebol word "either" chooses between two blocks of functions to perform, based on a conditional evaluation. Its syntax is: either {condition} [ block to perform if the condition is true ][ block to perform if the condition is false ] Here's an example: either now/time > 8:00am [alert "It's time to get up!"] [ alert "You can keep on sleeping."] Here's a variation of the above example that allows you to set the wake up time: wake-up: to-time request-text/title "What time do you want to wake up?" either now/time > wake-up [alert "It's time to get up!"] [ alert {You can keep on sleeping."}] =image rebol_tutorial/get_up1.png =image rebol_tutorial/get_up.png The built-in Rebol word "switch" chooses between numerous functions to perform, based on conditional evaluations. Its syntax is: switch/default (main value) [ (value 1) [block to execute if value 1 = main value] (value 2) [block to execute if value 2 = main value] (value 3) [block to execute if value 3 = main value] ; etc... ] [default block of code to execute if none of the values match] You can compare as many values as you want against the main value, and run a block of code for each matching value. Here's an example: favorite-day: request-text/title "What's your favorite day of the week?" switch/default favorite-day [ "Monday" [alert "Monday is the worst! Just the start of the week..."] "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!"] =image rebol_tutorial/favorite_day1.png =image rebol_tutorial/favorite_day2.png "Switch" gets used a lot because programs often require numerous multiple evaluation results to choose from. Rebol includes a rich set of words and functional structures that help you evaluate conditions in all types of situations, and with all types of data. Understanding how to use them is a big part of learning the language. ===Looping Programs are often required to check conditions and to execute functions repeatedly. In fact, in most large applications the computer often loops through many instances of repetitive work. For example, in a reminder program, the application may need to continually check the time and date to see if the user should be reminded of a given event at the moment. In other types of programs, the computer may need to repetitively scan through collections of data, or repeatedly request/respond to user input. To handle such situations, "loop" structures provide programmatic ways to methodically repeat actions. The built in word "forever" creates a simple repeating loop. Its syntax is: forever [block of actions to repeat] The following code builds a simple timer that alerts the user when one minute has passed. It uses a forever loop to continually check the time. alarm-time: now/time + 60 ; assign a variable to the time 60 seconds from now forever [if now/time = alarm-time [alert "60 seconds has passed" break]] Notice the word "break" in the example above. It exits the forever loop so that the program doesn't run on forever once the alert has been displayed. Here's a more interactive version that uses some info provided by the user. The heart of the program is still the loop at the end: event-name: request-text/title "What do you want to be reminded of?" ; requests the name of an event from the user seconds: to-integer request-text/title trim { How many seconds do you want to wait?} ; requests a number of seconds to wait alert join "it's now " [ now/time ", and you'll be alerted in " seconds " seconds." ] ; display a message alarm-time: now/time + seconds ; set the alarm time forever [ if now/time = alarm-time [ alert join "it's now " [ alarm-time ", and " seconds " seconds have passed. It's time for: " event-name ] break ] ] ; the forever loop continually compares the set alarm time to ; the current time, then displays an alert when they match. IMPORTANT: Notice the "join" word used in several of the lines above. It's syntax format is: join {data} [block of data items] Using that format, it joins together variables, text, blocks and other bits of data, so that they can be printed together, displayed, and otherwise manipulated to form a single piece of data. It's very useful! A variation on join is "rejoin". It takes a single block of data, and concatenates all the individual items into one piece of data. You should be familiar with this syntax too: rejoin [item1 item2 item3 ...] Now, back to loops. Here's a simple forever loop that displays/updates the current time in a GUI. Notice the block indentation: view layout [ timer: field button "Start" [ forever [ set-face timer now/time wait 1 ] ] ] =image rebol_tutorial/timer.png The above GUI contains two widgets: a text field which is assigned the variable label "timer", and a button with the word "Start" on it. The action block for the button contains a forever loop that loops 2 actions repeatedly (until the user closes the GUI): the built in word "set-face" sets the text in the "timer" field to the current time, the program waits 1 second, and then loops. Often, the data dealt with in each repetition of a loop must change. Like most languages, Rebol includes a variety of functions and programmatic structures that allow you to loop through blocks of data and perform operations using consecutively changed values. A common looping structure in many languages is the "for" structure. It allows you to specify a starting value, an ending value, an incremental value, and a variable name to hold the current value, so that you can loop through consecutively changing values in a controlled way. Here's the basic Rebol syntax for a "for" loop: for {variable word to hold current value} {starting value} {ending value} {incremental value} [block of code to perform, which can make use of the current variable value] Here are some simple examples. Be sure to type them in to see how they work: for counter 1 10 1 [print counter] ; starts on 1 and counts to 10 by increments of 1 for counter 10 1 -1 [print counter] ; starts on 10 and counts backwards to 1 by increments of -1 for counter 10 100 10 [print counter] ; starts on 10 and counts to 100 by increments of 10 for counter 1 5 .5 [print counter] ; starts on 1 and counts to 5 by increments of .5 for timer 8:00 9:00 0:05 [print timer] ; starts at 8:00am and counts to 9:00am by increments of 5 minutes for dimes $0.00 $1.00 $0.10 [print dimes] ; starts at 0 cents and counts to 1 dollar by increments of a dime for date 1-dec-2005 25-jan-2006 8 [print date] ; starts at December 12, 2005 and counts to January 25, 2006 ; and by increments of 8 days for alphabet #"a" #"z" 1 [prin alphabet] ; starts at the character a and counts to z by increments of 1 letter =image rebol_tutorial/example_for.png Notice that Rebol can easily loop through various types of data, forewords, backwards, and in partial increments, with a native understanding of those data types. It can automatically increment dates, money, time, etc. In other languages, that can be tough to do. Also, notice the use of the "prin" word in the last example. It works like "print", except it doesn't automatically insert a carriage return (i.e., it prints each successively output character next to the previous one, instead of on separate lines). Here's a "for" loop example that displays the first 5 file names in the current folder on your hard drive: files: read %. ; gets the current directory list, ; and assigns that block of filenames to the variable "files" for count 1 5 1 compose [print files/(count)] ; starts printing with the 1st item in the block, ; and counts up to the 5th item. =image rebol_tutorial/5files.png In the example above, "files/1" is the syntax representing the first item in the file list, "files/2" represents the second, and so on. Notice the "compose" word used in the for loop. In the example above, the first time though the loop, the code reads as if [print files/1] had been typed in manually, etc. "Foreach" is another useful looping structure built into Rebol. It lets you easily loop through a block of data. Its syntax is formatted as follows: foreach {variable name referring to each consecutive item in the given block} [a given block] [block of functions to be executed upon each item in the given block, using the variable name to refer to each item successively] The example below prints the name of every file in the current directory on your hard drive: folder: read %. ; gets the current directory list, ; and assigns that block of filenames to the variable "folder" foreach file folder [print file] ; loops through each file name contained in the "folder" block, ; and prints each one consecutively. =image rebol_tutorial/example_foreach_dir.png The following line reads and prints each successive message in a user's email box: foreach mail read pop://user:pass@website.com [print mail] "While" is other useful looping structure found in most programming languages. It repeatedly performs a conditional evaluation, and then performs a block of code while the condition is true. While loops are formatted as follows: while [condition] [ block of functions to be executed while the condition is true ] Here's a simple example: x: 1 ; create an initial counter value while [x <= 5] [ alert to-string x x: x + 1 ] =image rebol_tutorial/while1.png =image rebol_tutorial/while2.png =image rebol_tutorial/while3.png =image rebol_tutorial/while4.png =image rebol_tutorial/while5.png In English, that code reads "x initially equals 1. While x is less than or equal to 5, display the value of x, and add 1 to the value of x". (i.e., the program displays a count from 1 to 5) NOTE: In Rebol, the code "x: x + 1" adds 1 to the current value of x. It's one of the most commonly used expressions in all programming, found in all sorts of looping situations. Notice also the word "to-string". It converts the number value in "x" to a text ("string") value. This is required because the "alert" word in Rebol only displays string types of data. Here are some additional "while" loop examples: while [not request "End the program now?"] [ alert "Select YES to end the program." ] =image rebol_tutorial/example_while.png =image rebol_tutorial/example_while2.png In the above example "not" reverses the value of data received from the user (i.e., yes becomes no and visa versa) alert "Please select today's date" while [request-date <> now/date] [ alert join "Please select TODAY's date. It's " [now/date]] =image rebol_tutorial/select_today1.png =image rebol_tutorial/select_today2.png while [request-pass <> ["secret" "password"]] [ alert "The username is 'secret' and the password is 'password'"] The example below uses several loops to alert the user to feed the cat, every 6 hours between 8am and 8pm. It uses a for loop to increment the times to be alerted, and a forever loop to do the same thing every day, continuously. Notice the indentation: forever [ for timer 8:00 14:40:00 6:00 [ if now/time = timer [ alert join "It's now " now/time ". Time to feed the cat." ] ; if not, continue looping until the next meal time arrives. ] ] ===Working With Longer Examples Most programs are longer than the examples shown so far in this tutorial. Below is an example of a more complex program. It allows the user to type in the Internet address of a web cam, and display streaming video from it. It displays the video images in a GUI that has several buttons used to control the size, location, and action on screen. In order to avoid typing in this program every time you run it, it can be saved as a text file. Whenever you save a Rebol program to a text file, the code must begin with the following bit of text: REBOL [] That text tells the Rebol interpreter that the file contains a valid Rebol program. It must be included at the beginning of any saved Rebol program. You should include additional documentation about the program such as title and version info in between the brackets, but it's not required. Type in or copy/paste the complete code source below into a text editor such as Windows Notepad. You can also use the built in Rebol text editor by typing "editor" in the Rebol interpreter. Save the text as a file called "webcam.r" on your C:\ drive. (Note: this example includes a number of Rebol words and techniques that have not yet been discussed in the tutorial. The purpose of the example is simply to provide a longer text example that can be cut, pasted, and saved into a file. Don't worry if you can't understand the code at this point.) Rebol [Title: "Webcam Viewer"] ; try http://www.webcam-index.com/USA/ for more webcam links. temp-url: "http://209.165.153.2/axis-cgi/jpg/image.cgi" while [true] [ webcam-url: to-url request-text/title/default trim { Enter the web cam URL:} temp-url either attempt [webcam: load webcam-url] [break] [either request [trim { That webcam is not currently available.} trim { Try Again} "Quit"] [temp-url: to-string webcam-url] [quit] ] ] resize-screen: func [size] [ webcam/size: to-pair size window/size: (to-pair size) + 40x72 show window ] window: layout [ across btn "Stop" [webcam/rate: none show webcam] btn "Start" [ webcam/rate: 0 webcam/image: load webcam-url show webcam ] rotary "320x240" "640x480" "160x120" [ resize-screen to-pair value ] btn" Exit" [quit] return webcam: image load webcam-url 320x240 with [ rate: 0 feel/engage: func [face action event][ switch action [ time [face/image: load webcam-url show face] ] ] ] ] view center-face window Once you've saved the webcam.r program to C:\, you can run it in any one of a variety of ways: # If you've already installed the Rebol interpreter in Windows, just find the C:\webcam.r file icon in your file explorer and double click it (i.e., click My Computer -> C: -> webcam.r). The Rebol interpreter automatically executes the script. By default, during Rebol's initial installation, all files with an ".r" extension are associated with the interpreter. They can be clicked and run as if they're executable programs, just like ".exe" files. This is the most common way to run Rebol scripts, and it works the same way on all major graphic operating systems. # Type "do %/c/webcam.r" into the Rebol interpreter. # Use the built-in editor in Rebol by typing "editor %/c/webcam.r" at the prompt. Pressing F5 in the editor will automatically run the script. # Scripts can be run at the command line. In Windows, copy rebol.exe and webcam.r to the same folder (C:\), then click Start -> Run, and type "C:\rebol.exe C:\webcam.r" Those commands will start the Rebol interpreter and do the webcam.r code. # At the Windows command prompt (in a Windows DOS box), type "C:\rebol.exe C:\webcam.r" # Create a text file called webcam.bat, containing the text "C:\rebol.exe C:\webcam.r" . Click on the webcam.bat file in Windows, and it'll run those commands. # Use a program such as XpackerX to package and distribute the program. XpackerX allows you to wrap the Rebol interpreter and webcam.r program into a single executable file that has a clickable icon, and automatically runs both files. That allows you to create a single file executable Windows program that can be distributed and run like any other application. Just click it and run... # Buy the commercial "SDK" version of Rebol, which provides the best method for packaging Rebol applications. IMPORTANT: To turn off the default security requestor that continually asks permission to read/write the hard drive, type "secure none" in the Rebol interpreter, and then run the program with "do {filename}". Running "C:\rebol.exe -s {filename}" does the same thing . The "-s" launches the Rebol interpreter without any security features turned on, making it behave like a typical Windows program. =image rebol_tutorial/webcam_viewer2.png ---"Compiling" Rebol Programs - Distributing Packaged .EXE Files: Being able to save, run, and distribute your programs is important. You should become very familiar with the options above. Using the XpackerX packager is especially useful (not just for Rebol, but for other interpreted languages too). It saves your users from having to download, install, or run the Rebol interpreter. By packaging the Rebol.exe interpreter, your Rebol script(s), and any supporting data file(s) into a single executable with an icon of your choice, it works like a Rebol compiler that produces regular Windows programs that look and act just like those created by other compiled languages. To do that, you'll need to create a text file in the following format (save it as "template.xml"): your_program_name your_program_name.exe false your_rebol_script.r your_rebol_script.r C:\Program Files\rebol\view\Rebol.exe rebol.exe $TMPRUN\rebol.exe -si $TMPRUN\your_rebol_script.r =image rebol_tutorial/example_xpackerx.png Just download the free XpackerX program and alter the above template so that it contains the filenames you've given to your script(s) and file(s), and the correct path to your Rebol interpreter. Run XpackerX, and it'll spit out a beautifully packaged .exe file that requires no installation. Your users do not need to have Rebol installed to run this type of executable. To them it appears and runs just like any other native compiled Windows program. There are a variety of other options you can use to create distributable Rebol .exe programs. All such options involve creating self-extracting executable zip files ("SFX" files) that package the Rebol.exe interpreter together with your scripts. When run, the SFX files automatically open Rebol.exe, and execute your script on the command line. StubbieMan SFX is such a program, made specifically for creating packed executables, using a simple wizard interface. You can also use any other zip archiving application capable of creating SFX files. For example, PowerArchiver (version 6.11) is a popular free zip manager. To package Rebol executables with PowerArchiver, just follow the instructions to create a zip file that includes Rebol.exe and your script/data files, select "Actions->Make .EXE file" from the main menu, select "Create AutoRun SFX" and "Overwrite Files", then type "Rebol.exe| -s yourscript.r" into the field labeled "Command line after extracting" . It'll pop out a compact file executable with even less work than XpackerX or StubbieMan. Full instructions for creating self-extracting executables are included in the PowerArchiver help file. PowerArchiver and StubbieMan do not provide an option to change the resulting program icon. XpackerX is a bit tougher to use, but it does allow you to choose a specific icon for your program. The most complex distribution option is to create a standard installation package. To do this, try NSIS, the NSIS SFX Tool (my favorite), Inno Setup or Pack-X2. These programs all create single file .exe install packages that provide users with a familiar installation routine, uninstall option, and a group icon in the "Start" menu, so that your Rebol program can be accessed and run like any other installed program. (Note that these programs are all for MS Windows. You'll need to find a different packaging system for other operating systems). To create a self-extracting Rebol executable for Linux, first create a .tgz file containing all the files you want to distribute (the Rebol interpreter, your script(s), any external binary files, etc.). For the purposes of this example, name that bundle "rebol_files.tgz". Next, create a text file containing the following code. For the purpose of this example, save this script file as "sh_commands": #!/bin/sh echo "" echo "Running... please wait" echo "" SKIP=`awk '/^__REBOL_ARCHIVE__/ { print NR + 1; exit 0; }' $0` tail +$SKIP $0 | tar xz exit 0 __REBOL_ARCHIVE__ Finally, use the following command to combine the above script file with the bundled .tgz file: cat sh_commands rebol_files.tgz > rebol_program.sh The above line will create a single executable file named "rebol_program.sh" that can be distributed and run by end users. The user will have to set the file permissions for rebol_program.sh to executable before running it ("chmod +x rebol_program.sh"), or execute it using the syntax "sh rebol_program.sh". For more information about using this technique to create self-extracting Linux executables, see the article at http://linux.org.mt/article/selfextract. ===Embedding Binary Data You'll often need to use images and other binary data in your Rebol programs. For example, simple games often use graphic and sound files as part of their interface. As you've seen, there are many ways for Rebol to load in such binary data. You can read it from a hard disk, you can download it from the Internet, you can read it from just about any other local or networked storage medium, etc. When distributing your Rebol programs, however, those methods aren't always desirable. You don't want to require your user to download a number of images and sounds every time they play a simple game. They may not always have an Internet connection available. A possible alternative is to make a zip file or similar package that includes your program and all its supporting files. Creating and distributing such packages, however, can be overly complicated for simple scripts. Also, various operating systems use different compression types (zip, tar, etc.) and XpackerX/PowerArchiver/etc. only work in Windows. To eliminate those problems, Rebol provides a method to encode and include external files within the text of your programs. To see how it works, use the code below: Rebol [Title: "Binary Embedder"] system/options/binary-base: 64 file: to-file request-file/only data: read/binary file editor data =image rebol_tutorial/testpng.png =image rebol_tutorial/testpng2.png Type that program into your text editor, save it as a text file in "C:\embed.r", and then run it using any of the methods described in the previous section (try typing "do %\c\embed.r" into the Rebol interpreter). When run, the program will let you select a file, read it, and then display a binary representation of its data in the built-in editor. You can copy and paste the text of that printout directly into your program, assign it a variable name, and use it as if it had been read straight from a hard drive or another storage medium. Here's an example. Download the picture below - you can use your web browser to go to the url, and then save the image to your hard drive. Or, you can download it using Rebol, as demonstrated earlier in the tutorial: http://musiclessonz.com/test.png Once you've downloaded the file, use the program above to select and convert it to binary data. You should get the following printout: 64#{ iVBORw0KGgoAAAANSUhEUgAAAFUAAABkCAIAAAB4sesFAAAAE3RFWHRTb2Z0d2Fy ZQBSRUJPTC9WaWV3j9kWeAAAAU1JREFUeJztlzEOgzAQBHkaT7s2ryZUUZoYRz4t e9xsSzTjEXIktqP3trsPcPPo7z36e4/+3qO/9y76t/qjn3766V/oj4jBb86nUyZP lM7kidKZPFE6kydq/Pjxq/nSElGv3qv50vj/o59++hNQM6Z93+P3zqefAw12Fyqh v/ToX+4Pt0ubiNKZPFE6Ux5q/O/436lkh6affvrpp38ZRT/99Ov6+f4tPPqX+8Ps /meidCZPlM7kidKZPFE6kydKZ/JE6UyeKJ3JE6UzeaJ0Jk+UzuSJ0pk8UTMmvn8L j/7l/nC7tIkonekLdXm9dafSmeinn376D/rpp5/+vv1GqBkT37+FR/9yf7hd2kSU zuSJ0pk8UTqTJ0pn8kTpTJ4onckTpTN5onQmT5TO5InSmTxROpMnasbE92/h0b/Q //jR33v09x79vUd/73XvfwNmVzlr+eOLmgAAAABJRU5ErkJggg== } Now copy and paste that data into the Rebol interpreter and assign it a variable label, like this: picture: load 64#{ iVBORw0KGgoAAAANSUhEUgAAAFUAAABkCAIAAAB4sesFAAAAE3RFWHRTb2Z0d2Fy ZQBSRUJPTC9WaWV3j9kWeAAAAU1JREFUeJztlzEOgzAQBHkaT7s2ryZUUZoYRz4t e9xsSzTjEXIktqP3trsPcPPo7z36e4/+3qO/9y76t/qjn3766V/oj4jBb86nUyZP lM7kidKZPFE6kydq/Pjxq/nSElGv3qv50vj/o59++hNQM6Z93+P3zqefAw12Fyqh v/ToX+4Pt0ubiNKZPFE6Ux5q/O/436lkh6affvrpp38ZRT/99Ov6+f4tPPqX+8Ps /meidCZPlM7kidKZPFE6kydKZ/JE6UyeKJ3JE6UzeaJ0Jk+UzuSJ0pk8UTMmvn8L j/7l/nC7tIkonekLdXm9dafSmeinn376D/rpp5/+vv1GqBkT37+FR/9yf7hd2kSU zuSJ0pk8UTqTJ0pn8kTpTJ4onckTpTN5onQmT5TO5InSmTxROpMnasbE92/h0b/Q //jR33v09x79vUd/73XvfwNmVzlr+eOLmgAAAABJRU5ErkJggg== } You'll get the response below, indicating that the image has be defined and created: make image! [85x100 #{ FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF...} Now you can use the variable "picture" as you would any other data. Display it, save it, transfer it between networked computers, etc. Here it is in a GUI: view layout [image picture] The example below displays a photo of my dog, and then saves it to the hard drive as a .png image: dog: load 64#{ iVBORw0KGgoAAAANSUhEUgAAAFkAAACNBAMAAAAuisulAAAAMFBMVEUeEw6kh3OM TRZDSE/RxbWFal1KLiCtkoakZSXW0cm9mnlCNzq5no5oSDTg29B0UkzBjRe5AAAA CXBIWXMAAC4jAAAuIwF4pT92AAAQ80lEQVR4nG1Yf2gjZ3r+kiW6gjHJZHdOjuLU W0GMKRgShG7/UIVqqu2eUtd3c9pvpe6uGJ32dnqca+zhIoa6glubczCBZVsQhGkX 9UR369zgEKeqXeic0G6xERHTxLGxWFCH+WehGIKIVx2viTJ8fd9v5M2m9JN/CPx8 zzzv8/74PpkIQTUYVDUtqMFP+L22uKitaaXFktaEZZXgyzpdD0hQEFRcz+CA10pr HIurVLKa36KpAHhhAA8CeBHIm8BcwgXA5+APCGM0GBSCQU4ML5VvaDWfLav5HDez 40dAHhSQHaHwDNUw1pB5EXiR3hqIR/S+I3FupA4iWDPWjRYXUlovIfe3SuRcp+6q Ag92QK5ebVmGhgFqKMQq8e8Sj5LadkMQnoFBh321ZTSbmtZcszBIHuuAW5L6pv1U OPUFvhy3FbpnlLQScnPDTzc8IKLssc4Bxsmlgw67fdlo7WjWwMJBmPgFuj3W7zuc G/wGx4PC3Kix/THsazZ9fNPHct2s0fC2JBW5ueuC91/r1nr6Ok++LwQf40cpyZ1+ o3+Anmg898VZuzX6+f4FID/VUnoWpTvm1Fmn4SdIQ+3Uvh/6OAJoXljIPjAQ0EfU Yw22JQHcLys1/cX2vfuRaPM73AMlDvX6rNH5Arm54X9z45OL60YkgnnC7OAOyy8W rCrZ6bP2pm94MFiMzb40tb6ejmSwvJrWoMx5/h8Qm8m2Z3v7AleuqbFI9BNrvfU6 CA8OytaXA/zALdO+Y7MnEk+QGolsmKMP32n9MvI2VkGz+XwP8eww16XyFz64Vntr 46+qllE0o6h7kTNDXvmOBeLYDnPlrHsTuIuRWiQSe/13Rsu4b9Z4mH4dwo41rtur 1z2XUgrCg2kAR8yEZRmGEakFfVMAvjbQskBYpwNSKLOF4NlYDNAbG9eNe8Z6j5uy 1iwtDroIFkRZ7zgMinz2SOXgmPlV6GurZfVqaAqKBkfWfO4HyN1waNGc/SJdQyGR WnT0nXVjZz92QfNdATHgJP6GOjmGOmGsvvUJp45Ezpilq4bxT/COTxcYRc9sB91e w5EZ65i1Wo3DzUDwfWM9B++CPhy0NK0BNwMowPtbphnx14ZlhCDIiJ97BA/qC7gp ZbQGVWiasVP0N62fhK5BvCCczyJtbTCIsKqY3IMfpmnWfPRbLy++aqQBHeHDhXNj dSG37bq5PmOJ3iz+HVeMXDDW/9NHa0E/obxaAG0yythmRO6btdqplO+1WuhJLePP 3NKpKwukwUSXNVw3fYoF0pes9c/xDS8soF9r+llaIHVJ7rMelKFZi32Ltjj6Wyl+ Gz0gW7LEWJ+5aTNyamHsTMvaQSU8nX7hLvpKzHlXhqpywO9TdCQw3RK5sAt+Yy/y SJvNBTJXTx9BycZMns0aN5K0jCo+I1JrDsjXBp50zCO3waRrpjkLyA0SGDJjeahw 4IZnZXA0ojF8PC+Qrc4BTBR6La+Y9Y6iBAhRlI3R9RbnPk0nwuGwA0+2nIzsCa8T Zc5htn1ntZ0PkOuW1ePwqCZwX4KLqAmibNejMpNyRHl0kpvIZulKm5DrrVAPpNQi Mc0/6PwF3HN1lx24VwJvrtyZfHN1ZSmXy5PZg2Cac0cuqKr6HNo052SXuf0AXZm0 DyRh5D1QQsjNaxEeZkLjZ5fa9NGz7brnMHYlYNu2Yp9kaX4WwOTM6zVeZNHTowuT Ckpmbc/NOcdKwW4rc7BlN5+/sZW/+X2QXat98uw8Rzxwzx04jiQrCkADbaXd2c0r b3uBr38D9teikn9iYKAqcs/Z0Glf5PKAPgarbaVOhjobc6F/DkCy7AZVtec9cRwn 7tGOEphdOQ7Y7XZ7bjewFfja+CigbHU6diPDrxgaRqkuEJexHGWe8uIcvf0izOb2 7DXSrhvGq6TWUeYU+6B5yq1ybteTXY8MP6K5+YM7nq2wgKJUhY/JDa/TNh0m+Acj KoJ54sA8dj1l+OAk+3O6cscLOCQ/N1qtEuKmZ2EAXxXUb7k3mStRdqwMe4p4kHVp LnCg5OcMwSCk1rZPbHbET9ES8oMST6Ii27CH7Xw2m7NdOe/eHi9cNQRCAqbNnBO3 iuTWwG9ZEsV+3j4/pLgrVwIb9UcrdMVeCoXyhEQajic7SG1ZeCuCOqFUlNJ52w7k Xa89fMyosJLNHhnGLqTfTMA5kwENpekm545RWRyTQVCeOCd5olBRGJHuvG8Yt0DK 0GbcZZhPranyKGXqjhXN/LyzG2B38oF5ikfhyteC+GMoxcANV04/5XcoPKVharqC IPZNxesoNtTVAaIl+Q9CxitDCiFD0Wjubf92CTeABdKjY1RuNAAKy3OoJIgivfKL 6vu/ma1vkPwNNx4N8pslryrXFWGCs858p922nQMqHwhS9vgFQ/j+nA09RzbNTd4+ uGGBpNkBhSONzbdnoSps1v49OJW/ekEQXp1LIPpMrIG6+aULMu/yF2NbirJVNxXy klSq5l+uVj+aW/EAPWSagsZ1Yy7rDegzWN5u3lQATDZDhkjmq9WReXqb95wZ9G9c qKSD0AYMzo2AsgEtGThaN64E5sCZE5rNc3JB4zqC2isEkC6GyfL5PAGLX75utXaH hkNidUWiu0huZhCMTbFA4p0j6qMJyQ8FyOaodT/fCRjgupRF9NbQU1DB75avELNB JUT3kWaIBDJB60rgB0QQQmA85x7KwC1Hww2ge1Pk3MeIJRsBQRXzDiGhkGFU5TZn gHnl3/+BG64b4KDHcEJtkTMSjQ/Bc65WBUPEkQjr3wS8cMEHjFfglILbPZX37a0N CJ+c6e0rL8FzlkBKNeuhKeR7eBHFO/ePCcfKcr2zZZKhfv5GQlEOfkACV6uGIdA7 HP0yVwKefkVkF8+dXqfeCfRqdEOJBEwxT4alqiBUBZmjAxAxXgCFXSJx6r7Xr2+B 8UptP3AgbhDIzki1KmZ94XMuflYQgEWGKF0ZzqiOCSe+UtuapWMwkK8u3Qd2bjie LYUJLOU8UUUq0V6stm/Os4ZMbhzPS1cIca6i4QJ9j6NhHClK+OQNQoIihfLvwYgE NCU34pvCFTIEHWQAN7x9hoZFoC8pFTTITwyV5JVEgr5F5kXBGAGwX4UAz8MMViBk 0JGWKd5SYg3mbQ0n6NhG4EAQ0RI4V3xwWwF4ANAQZcaV5V4PTvp6vD7rlu6+VcuE AAsbBF6zhHi37XYehMBE7kmy25Oj4GS93utcmOmmQfV94b4hQs+NbSD6Xwp79hOo aJ7LNHzD6pkwnK/PdC+iH6ERcWlFWro3xS0cHx8P77W5EkGuJVxG0zSWkMXRbndK DVn3YceH4pI4c/gWpmc8XCjMLecJEyFMvM4Uo4m06UjnKodfautWtWqIwtKSNJNc APRwGLgLYIpMpSgk8whsT9N+jb7b7X6plqwd4cNQVUD03wL6xUKhoIyHzxNIvACF Ar+CxUS67pa63cfC9dJU6EOQPiLM6H+GFoZhjY8DtyWqwR24VMNhJLu16Llut/tQ spJCNWQsSdKM/g+oBIUUgJsW5SKI6blPNfFX0VjjOqDLglUxQqGqNCLO6P844AYx MLTpAZXSImUylIucTvxRBeCHle7D6vvVEUF8V/8TRO+hhePnibPP5KD4UDzrRqlI 0+6FmUqle9g9bIHnwP0aV0ImUXc4T+Jxu+dCsRQxUFFMRD/rVgBcnjJC1RGRfqr/ KaILBZ/bi8MLGwhshNMtkv4MmLuHqQ9CSyNLAk3pf0wCdeXFcAGUg+59247HqewW 0/RteHmvge6KXvlvFZplSdL1B6TO2oFCGNjPk307ntjfx09IqgvJ76W/Qe7uzAdB NbskZnT9lQbNHiun3HGPxSOuy+QLIgrq/bsO3F39cTIp0qVRXU9TaeWJUsAoz5Oe E9+3HYqlSCVJjCbcFIR5qFf0aQG4U2UpKGa9AmAL44T0mQPUULVQsbBBZrnfgoVd /bA8nR0RR1NlzaqKe1AkhfPgDe31bAduEWkqylBfCSZf7lYqlcOkNSVJwrs6oK0R 8GScNxEV7YTrwT1CALsBzORvdG5heQo67VO9DB8sxfEAthl4InmOg40DlQsvFkn0 6QwKP9SnBENI6eXtbeuNPWiy4QJoGYvXMD6QMQaFEmGA/uywWyl39UtqFdC6Nf0w ayt5ZS8MnlxzDvADI5VgaNFcohGtb547hCB1valWReC2th/a7YKNhXKenN0/QLAY 1EQ3Gq9H3PpPL6PfUClBzn1p+mN7r4DtANyZiAfRjWVEYSftxGPM7bxwWUfZoEQV dF1PNnN7T07R4lg8CjkMCsUii8ccx2vfRAvLlXIzpIqpsp7c8Zw9ZYB2pbRXlKWH gnY2kbYTCda2r0M36F9OXwq2qqlyqnyxPdk+5abS5z13LLMjFYu93j4eQt5lqCvI /KWSVQW/k9NtZbyw56N3opmEqz5UqVBMRPYbUC7HoAT06h+UrNanKPwaTvvwMqKb NCP3mk2VBqWeF09AUnOXu4AB7net5l/rern8I6Uwh+Dl8+TSr9xfskwJjiza68eZ K9DcX0CUun5417KsUbDw7lSuDdDl5fAtUiqmpQj+O0zIsHgcL57Hv9/FgtXvvmtZ 5yA9+vTYJFIj90MBToeSoDWFo17f9grHk23I/GESfLa2LSulJ/WyHA5PIDugNTUK 52xRUDNyxLEVZa89A5kE7qSV3C6BJ3ryYyCemMAowei0WBRLmaDI4jzFNlZg6hBq dXu7qZeB/T+4f6hkVINTjcINXlJZHDqqEJ7AGLGszm1PN5PJZDn5owmf+xYoCWpH VJAeSiKzMWWFX1TAk5ReuRvcti5D5svTF4EX4beAu6ip0O1FVWb2OPb2z2CyHZb1 u1S2Wj8rg/7kFHLDBsi8RoPQNEeqKHvIXZj8NZQgDIhL0vH2pd+VpVS5PBVe5XGe J0UYZ+JZMbMDByyUfGF8kncxUDqxjJiZCkRTyallsA/YoWI1KmagYvGAxdFYmPwt 2FeZeVyOJ9i/Tv/5T7WMPrU8gUKWbxFVE0WQjTcxbxye94h+CgZWKjNlmrn0P2Ur un12+yKCl1F3UwvCbHgqwPUU/AtPrkATH6KSSvIQivuzi9Jr537IDVwNI7cGrUNV l8sOhyXxUxiCjzGZKTD93sxdIfnRKq8qjFKVZEiP6LoehBhWQgJwdzm2rM+k7pUr enIEaSbCgM6oIESG+6bsYX4nJOGzFFY3wqG4p/VUahpV+35ndvBe9XTMZbwYlJs0 iY2DdTUD7JfK5fIPl1HGMvqtqYIruxmRHo/z0lHe3AbsDNDCK1X+u1Qq+SrECHAf rUbhwClCjNB8sObvpRB4yHsz9fcpzPsyZwclF7Qig9GmHg8/CfvrXjKVgnRDhaTK j7/cTmZ5blZXJ1Zvkb+Ez0aOTD/3z3Jcl5Plx2Wdy4E99+5jk61OLMPXLfISzTHK pF1A+yMm/IdY02Xkr1SSZQrQMIpeXV49T/JwqaL0GpzOJz54+c0kwvXyYQp+ffjo 0SpmErATE7fI+TdyTKZfwei/fSrl1wjXU8lU8p3VlTso2H8B966D/0zEq9kjnjD8 sZ3k6537q7DeXD1d4ODkPNySj/EEguzA/gl8rJF8x2hVVzjmwwF2eRWiHHfuMPpj fvJjfiFly/yPSwPQ0tQzbnAwPM/Y0TEq+fkjnHX8Ac/D7z0DI/dN+Dx/uw3wR8sY +nJ4efU769zIc9zjc8xzH9mT7w2DCCDGLc+tidX3q8/egxLwJDu5fPJkNcwL0/cK 14Bz6TvcDnNyJ5MYHCZs9XkdE/9H1C1ye555P1lG43ybTpn/v3XrfwFzPoFQpggI EQAAAABJRU5ErkJggg== } view layout [image dog] save/png %dog.png dog =image rebol_tutorial/example_dew.png ---Compression The code above can be shortened dramatically using the built in Rebol word "compress". Rebol's built in compression and decompression only work with text strings, so the binary embedder program provided earlier must be adjusted as follows to create compressed embedded data: REBOL [Title: "Rebol Binary Embedder"] system/options/binary-base: 64 file: to-file request-file/only if not file [quit] uncompressed: read/binary file compressed: compress to-string uncompressed ; Note that the line above converts the binary data ; to a text string, and then compresses it. editor compressed alert rejoin ["Uncompressed size: " length? uncompressed " bytes. Compressed size: " length? compressed " bytes."] IMPORTANT: To use the compressed version of the data above, you'll need to reverse the text-binary conversion after decompressing it. To do that, use the following code: to-binary decompress {compressed data} So, using the variables above, the following two lines of code display the same image: view layout [image load uncompressed] view layout [image load to-binary decompress compressed] Here's a complete example demonstrating the size difference between raw and compressed embedded data: ; Here's an uncompressed embedded image: image-uncompressed: load 64#{ iVBORw0KGgoAAAANSUhEUgAAAP8AAAEsCAIAAACDt/KoAAAAE3RFWHRTb2Z0d2Fy ZQBSRUJPTC9WaWV3j9kWeAAAB9FJREFUeJzt3bGRHEkQBMETDaKd5v8UmlmzIaoz MGtZ4QLUIBtBLXM//0lb/bz9D5BeY/3ay/q1l/VrL+vXXtavvaxfe1m/9rJ+7WX9 2sv6tZf1ay/r117Wr72sX3tZv/ayfu1l/drL+rWX9Wsv69de1q+9rF97Wb/2sn7t Zf3ay/q1l/VrL+vXXmD9P1ICmCh4WkoAEwVPSwlgouBpKQFMFDwtJYCJgqelBDBR 8LSUACYKnpYSwETB01ICmCh4WkoAEwVPSwlgouBpKQFMFDwtJYCJgqelBDBR8LSU ACYKnv7w+1f8W+fyP307B+a8MxA8Xf92Dsyx/jnjIPQPvD/xixn8G4wjqH/g/Qnr r42jfuD9CeuvjaN+4P0J66+No37g/Qnrr42jfuD9CeuvjaN+4P0J66+No37g/Qnr r42jfuD9CeuvjaN+4P0J66+No37g/Qnrr42jfuD9CeuvjaN+4P0J66+No37g/Qnr r42jfuD9CeuvjaN+4P0J66+No37g/Qnrr42jfuD9CeuvjaN+4P0J66+No37g/Qnr r42jfuD9CeuvjaN+4P0J66+No37g/Qnrr42jfuD9CeuvjaN+4P0J66+No37g/Qnr r42jfuD9CeuvjaN+4P0J66+No37g/Qnrr42jfuD9CeuvjaN+IHi6/u0cmGP9c8ZB 6B8Inq5/Owfm9Nf/56/ftHP54T8szoFBDwPBRMHT3/F28W8dDgyyfuTt4t86HBjU X780ACYKnpYSwETB01ICmCh4WkoAEwVPSwlgouBpKQFMFDz94fy8Ff/Ww4908W8d Dgx6ZyB4uv7tHJhj/XPGQegfCJ6ufzsH5lj/nHEQ+geCp+vfzoE51j9nHIT+geDp +rdzYI71zxkHoX8geLr+7RyYY/1zxkHoHwiern87B+ZY/5xxEPoHgqfr386BOdY/ ZxyE/oHg6fq3c2CO9c8ZB6F/IHi6/u0cmGP9c8ZB6B8Inq5/OwfmWP+ccRD6B4Kn 69/OgTnWP2cchP6B4On6t3NgjvXPGQehfyB4uv7tHJhj/XPGQegfCJ6ufzsH5lj/ nHEQ+geCp+vfzoE51j9nHIT+geDp+rdzYI71zxkHoX8geLr+7RyYY/1zxkHoHwie rn87B+b01//K37mPf+twYNDDQDBR8PR3vF38W4cDg6wfebv4tw4HBvXXLw2AiYKn pQQwUfC0lAAmCp6WEsBEwdNSApgoeFpKABMFT384P2/Fv/XwI138W4cDg94ZCJ6u fzsH5lj/nHEQ+geCp+vfzoE51j9nHIT+geDp+rdzYI71zxkHoX8geLr+7RyYY/1z xkHoHwiern87B+ZY/5xxEPoHgqfr386BOdY/ZxyE/oHg6fq3c2CO9c8ZB6F/IHi6 /u0cmGP9c8ZB6B8Inq5/OwfmWP+ccRD6B4Kn69/OgTnWP2cchP6B4On6t3NgjvXP GQehfyB4uv7tHJhj/XPGQegfCJ6ufzsH5lj/nHEQ+geCp+vfzoE51j9nHIT+geDp +rdzYI71zxkHoX8geLr+7RyYY/1zxkHoHwiern87B+ZY/5xxEPoHgqfr386BOf31 v/J37uPfOhwY9DAQTBQ8/R1vF//W4cAg60feLv6tw4FB/fVLA2Ci4GkpAUwUPC0l gImCp6UEMFHwtJQAJgqelhLARMHTH87PW/FvPfxIF//W4cCgdwaCp+vfzoE51j9n HIT+geDp+rdzYI71zxkHoX8geLr+7RyYY/1zxkHoHwiern87B+ZY/5xxEPoHgqfr 386BOdY/ZxyE/oHg6fq3c2CO9c8ZB6F/IHi6/u0cmGP9c8ZB6B8Inq5/OwfmWP+c cRD6B4Kn69/OgTnWP2cchP6B4On6t3NgjvXPGQehfyB4uv7tHJhj/XPGQegfCJ6u fzsH5lj/nHEQ+geCp+vfzoE51j9nHIT+geDp+rdzYI71zxkHoX8geLr+7RyYY/1z xkHoHwiern87B+ZY/5xxEPoHgqfr386BOdY/ZxyE/oHg6fq3c2BOf/2v/J37+LcO BwY9DAQTBU9/x9vFv3U4MMj6kbeLf+twYFB//dIAmCh4WkoAEwVPSwlgouBpKQFM FDwtJYCJgqelBDBR8PSH8/NW/FsPP9LFv3U4MOidgeDp+rdzYI71zxkHoX8geLr+ 7RyYY/1zxkHoHwiern87B+ZY/5xxEPoHgqfr386BOdY/ZxyE/oHg6fq3c2CO9c8Z B6F/IHi6/u0cmGP9c8ZB6B8Inq5/OwfmWP+ccRD6B4Kn69/OgTnWP2cchP6B4On6 t3NgjvXPGQehfyB4uv7tHJhj/XPGQegfCJ6ufzsH5lj/nHEQ+geCp+vfzoE51j9n HIT+geDp+rdzYI71zxkHoX8geLr+7RyYY/1zxkHoHwiern87B+ZY/5xxEPoHgqfr 386BOdY/ZxyE/oHg6fq3c2CO9c8ZB6F/IHi6/u0cmNNf/yt/5z7+rcOBQQ8DwUTB 09/xdvFvHQ4Msn7k7eLfOhwY1F+/NAAmCp6WEsBEwdNSApgoeFpKABMFT0sJYKLg aSkBTJQ7LX0569de1q+9rF97Wb/2sn7tZf3ay/q1l/VrL+vXXtavvaxfe1m/9rJ+ 7WX92sv6tZf1ay/r117Wr72sX3tZv/ayfu1l/drL+rWX9Wsv69de1q+9rF97Wb/2 sn7t9T/igalLLsvMjgAAAABJRU5ErkJggg== } ; Here's a compressed version of the same data: image-compressed: load to-binary decompress 64#{ eJzrDPBz5+WS4mJgYOD19HAJAtL/GRgYdTiYgKzm7Z9WACnhEteIkuD8tJLyxKJU hiBXJ38f/bDM1PL+m2IVDAzsFz1dHEMq5ry9u3GijKcAy0Fh3kVzn/0XmRW5WXGV sUF25EOmKwrSjrrF9v89o//u+cs/IS75763Tv7ZO/5qt//p63LX1e9fEV0fu/7ap 7m0qZRIJf+2DmGZoVER5MQiz+ntzJix6kKnJ6CNio6va0Nm0fCmLQeCHLVMY1Ljm TRM64HLwMpGK/334Hf4n+vkn+1pr9md7jAVsYv+X8Z3Z+M/yscIX/j32H7sl/0j3 KK+of/CX8/X63sV1w51WqNj1763MjOS/xcccX8hzzFtXDwyXL9f/P19/f0vxz4f2 OucaHfmZDwID+P7Hso/5snw8m+qevH1030pG4kr8fhNC4f/34Z89ov+vHe4vAeut SsdqX8T/OYUCv9iblr++f67R8pp9ukzLv8YHL39tL07o+3pekn1h/dDVBgzLU/d3 9te/Lki4cNgBmA6/lO+J/RPdzty8Rr5y94/tfOxsX6/r8xJK0/UW9vlH93/9oAzR e09yKIUBVbT9/br/U/m7x6CU98VAAJS2ZPPF/197eEDhtfs9vX9rDzc6/v3qzUyo nJA/dz76Y77tHw+w3gXlbEMpDKihza/+7/o/c3+DU54tDwsobR2/fXR/qYXBiV8T t3eDEmpA/d9LDASK0y/tnz+H/Ynmt78E1vti7lAKA6pouxz/X7v+uR045ZFdRE6x 1q21pG7NiSzx1f5R40pvvdNn+oB1P4Onq5/LOqeEJgCemFy1KQgAAA== } view layout [image image-uncompressed] view layout [image image-compressed] =image rebol_tutorial/example_compressed.png The compressed version of the image data is exactly the same, but much smaller when included in your programs. When you use compression like that, just be sure to include the "to-binary decompress" words when loading the embedded data. Using the short binary embedder program provided earlier, you can convert any type of file into embeddable text data. Images, sounds, videos, even entire executable programs can be included in your distributed code! ===Modular Programming and Code Reuse Rebol provides a fantastic array of easy to use programming tools built right into the language. As you've seen, Rebol's built-in words can accomplish useful "high-level" actions that can be the basis of simple programs. You can even use the interpreter as a powerful little swiss army knife utility that doesn't require any real programming (i.e., as a multiplatform text editor, calculator, email reader/sender, image viewer/editor, command interface for copying/pasting files, ftp uploader, etc.). For larger programs, however, the built in capabilities are merely simple building blocks. To build more complex applications, you need to _create_ new functionality by combining and using the native words, grammatical structures, and simple pieces of code. Language elements are just raw materials that can be put together to achieve more powerful and specific goals. Toward that end, a very important concept in programming is (drum roll...): the reuse of existing code. Never re-invent the wheel. Reusing bits and pieces of existing code is essential if you want to become a productive programmer. The creation of functions is a basic way to implement code reuse - once you create a new function word to accomplish a given action, you can copy its definition and use it over and over again in your programs. That saves you the trouble of reinventing those actions every time they're needed in your programs. It also reduces the likelihood of introducing errors - old trusted code is less likely to contain bugs if it's already been tested and put to use in a variety of situations. As demonstrated earlier, the built in word "do" opens and runs Rebol code that's been saved to a text file. You can use it to import existing modules of code, as if that code had been typed into your program. That existing code can contain function and variable definitions, new programmatic structures, and even complete programs of any length and level of complexity. Once those words and definitions have been imported into your program, you can use the included functions and variables as if they're native words in the language. You don't even necessarily need to know how they were created. Try typing in the following code example, and save it to "C:\play_sound.r". Rebol [title: "play-sound"] play-sound: func [sound-file] [ wait 0 ring: load sound-file sound-port: open sound:// insert sound-port ring wait sound-port close sound-port ] The code above creates a new function word "play-sound", which accepts a passed variable filename "sound-file" (that file must be a ".wav" file), and plays the sound through your computer speakers. You don't have to understand how the code works - just type it in and save it to C:\play_sound.r . Now, whenever you want to play a sound, you can include the code in your program: do %/c/play_sound.r And use the "play-sound" function just like any other built-in function word (the syntax is "play-sound {sound-file}"): play-sound %/C/WINDOWS/Media/chimes.wav Easy, right? You only have to type in the line "do %/c/play_sound.r" once in your program. After that, the word "play-sound" is defined, and you can use it wherever you need: alert "Here's a sound:" play-sound %/C/WINDOWS/Media/chimes.wav alert "And here's another sound:" play-sound %/C/WINDOWS/Media/chord.wav alert "Now try choosing a .wav file from your hard drive:" play-sound to-file request-file/file %/C/WINDOWS/Media/tada.wav This whole concept becomes much more useful with the realization that (final drum roll...): You don't have to write everything yourself! There exists a community of developers around the world working to create useful pieces of code. Finding and learning to use modules of code, functions, and complete dialects created by other programmers will remain at the heart of your initial learning process, and will continue to play an important role in your efforts as a proficient developer. Many self contained modules of Rebol code have been created to extend the built-in abilities of Rebol, and that list continues to grow as the language matures. Learning to use dialects and parts of programs created by others will make you a much more capable programmer. Existing modules of code can help you do high level, complex, and specific things with ease, so that you don't have to start from scratch in every programming effort. In that way, learning to program is a lot like learning to use other types of technology that exist in our society. To call your friend on the phone, for example, you don't need to reinvent the telephone and all its electronic components. You don't need to manufacture any of those items, and you don't need to install miles of cable. You just need to know how to use the existing phone system. Furthermore, you can put that system to use in more complex operations that are functional to you at another level. Business owners rely on the phone to contact clients and employees, without necessarily caring how the system works. They just use it functionally as part of their higher level business goals. Existing code modules can work for programmers in a similar way. They make available high level functions that can be used to build even higher level, specific, and complex applications. You just need to know how to use them. To reuse code modules, it's essential to first learn the language, so that you can understand code written by other developers. You may need to adjust and extend code written by others, so that it fits your needs more exactly. In most cases, however, you can just learn how to use the words in an imported module or dialect, execute a "do {filename}" command, and you're off and running with a whole new language extention. Here's an example: The web site http://www.dobeash.com/it/ offers several free extension modules for the Rebol language. They provide a module called "RebGUI", for example, which extends the already powerful GUI syntax built into Rebol ("view layout..."). Using RebGUI, it's possible to easily display graphic widgets that aren't natively possible in Rebol. RebGUI constructs those components from the built in Rebol raw materials, and makes them reusable in your own programs. To use RebGUI, just download the files at the web site above, and unzip them to C:\ . Then type in the following code and save it as C:\rebgui_example.r. You can run it by any of the means described in the previous section ("do %/c/rebgui_example.r", etc.): REBOL [] do %\c\rebgui.r display "Grid" [ table #WH options [ "Day" left .5 "Time" left .3 "Name" left .3 ] data [ Monday 9:00 "John" Tuesday 9:30 "Jane" Wednesday 10:00 "Bob" ] ] do-events =image rebol_tutorial/example_rebgui.png The code above makes use of some new commands that aren't part of the native Rebol language. Specifically, the functions and variables "display", "table", "#WH", "options", and "data" are defined in the rebgui.r file, and they add new functionality to the Rebol language. Using those words, as defined in rebgui.r, the above code displays the given data block [Monday 9:00 "John" Tuesday 9:30 "Jane" Wednesday 10:00 "Bob"] in a resizable GUI display that can be automatically sorted by clicking on column headers in the GUI. That type of display is a common requirement in modern programs that display lists of data ("database" applications), so the added commands are a welcome addition to the Rebol language. RebGUI contains a broad collection of additional functions that are useful in building intricate GUIs. To use them, you must first understand basic Rebol syntax, and then learn how to use the RebGUI language extensions within that syntax. Once you've done that, you can simply include the "rebgui.r" file in your programs, and use those language extensions as if they're part of the language. The key is to understand that RebGUI commands are built from native raw materials in the Rebol language, and to use them, all you need to do is import rebgui.r using the "do" command. NOTE: rebgui.r is packaged with several additional files, which are in turn imported within the rebgui.r code. Those files contain "lower level" code that actually define the new words and grammar that make up RebGUI itself. They contain code that must all stay together with rebgui.r. Just as the above program is not complete without the included rebgui.r file, rebgui.r itself is not complete without its included files. As you begin to create longer and more complex programs, your source code will often consist of many separate source files tied together to make up a whole program. Managing and remembering which included files are required in your programs becomes more of an obligation as you build applications of greater complexity. Here are some web links containing free modules that can help you accomplish useful programmatic tasks in Rebol: http://www.hmkdesign.dk/rebol/list-view/list-view.r - a powerful listview widget to display and manipulate formatted data in GUI applications. Perhaps the single most useful additional to the Rebol GUI language. http://www.dobeash.com/it/rebdb/ - a database module that lets you easily store and organize large amounts of data using the "SQL" database language. There's also a spell checker module that can be included in your programs. http://www.rebol.org/cgi-bin/cgiwrap/rebol/view-script.r?script=rebzip.r - a module to compress/decompress zip formatted files. http://www.colellachiara.com/soft/Misc/pdf-maker.r - a dialect to create pdf files directly in Rebol. http://softinnov.org/rebol/mysql.shtml - a module to directly manipulate mysql databases within Rebol. Very useful for web programming. http://www.rebol.org/cgi-bin/cgiwrap/rebol/view-script.r?script=menu-system.r - a dialect to create all types of useful GUI menus in Rebol. A later chapter is dedicated to this topic. http://softinnov.org/rebol/uniserve.shtml - a framework to help build client-server network applications http://www.rebol.net/demos/BF02D682713522AA/i-rebot.r http://www.rebol.net/demos/BF02D682713522AA/objective.r and http://www.rebol.net/demos/BF02D682713522AA/histogram.r - these examples contain a 3D engine module written entirely in native Rebol. The module lets you easily add and manipulate 3D graphics objects in your Rebol apps. http://web.archive.org/web/20030411094732/www3.sympatico.ca/gavin.mckenzie/ - a Rebol XML parser library. http://box.lebeda.ws/~hmm/rswf/ - a dialect to create flash (SWF) files directly from Rebol scripts. http://www.rebolforces.com/articles/tui-dialect/ - a dialect to position characters on the screen in command line versions of Rebol. http://www.rebol.net/docs/makedoc.html - converts text files into nicely formatted HTML files. This tutorial is written and maintained entirely with the Makedoc tool. http://www.rebol.org/cgi-bin/cgiwrap/rebol/view-script.r?script=layout-1.8.r - a simple visual layout designer for Rebol GUI code. http://www.rebol.org - the official Rebol library - full of many additional modules and useful code fragments. The first place to look when searching for Rebol source code. Pieces of code aren't always intended strictly for modular reuse, but can be found within published open source programs. Searching for useful code sections within programs released to the public is a vital way to save time and programming effort, and to improve your capabilities as a developer. A great deal of potential work in any conceivable programming domain has already been accomplished by developers around the world. Always check for functionalities you need in related programs written by other programmers. If the examples you find are well commented and organized, you'll likely be able to reuse some of the code with limited effort. (Be on the lookout especially for useful function word definitions). A list of Internet resources containing available source code has been included at the end of this tutorial. The programs available at those sites are a gold mine of existing code, necessary for productive development work. ---Using External Programs as "Modules": Remember that data is often interoperable between existing programs. Once data has been saved to a storage medium, you can pass it to other tools. The built-in Rebol word "Call" allows you to run other programs on your computer. It provides a variety of options to send command line parameters and deal with output from those programs. Using "Call", you can execute all of the built in "shell" commands included in your computer's operating system (i.e., DOS and Unix commands). You can even embed and use entire premade applications to help manipulate data in your Rebol programs. The example below opens Windows' Notepad to edit the "rebgui_example.r" text file created earlier: call "notepad.exe c:\rebgui_example.r" This next example opens Windows' Paint program to edit an image we downloaded earlier in the tutorial: call "mspaint.exe c:\bay.jpg" Here's an example that embeds an executable program into the code, decompresses, and writes the program to the hard drive, and then runs it with the call function: program: load to-binary decompress 64#{ eJztF11sU2X03K4VqJsrkZJp6OzchhFJsx8qDB9od1fHdIO6ds7AgJX2jttyey/p vWUjJuNnmNhMibzwaCSLi+EBE1ziGIkBGh0BSYTwwAMme9Dk4kgkgSiKcj3nu7es QrKFhMUQOcn5+c7fd875+vXe27FJAg4AbIiGAQwWIwZMEbqTcmODN5xRdmRi6aoy Z83YogngLlaNtV+s6kV7q9KelHeu9LYqQTXt7e/v97UqLcLuqKJIvriShnAIoJ0r gXvPn+StlDAF5dyzHLwAdlw4TZ1Mm7oQvWDu7jKLslsxBc4KQ30bb9bMHF3F/D5j MFAHEIbHD+cwb88s9riSEIjvK7EKogZs//bxAvQmYlqM5JsOUwHPWFgEAYDTvqTp eYdy1Fn5Sh/O96h9nLrrDcD4IpQm7UOkWL/nt6MlqMvxrkl+GVWS7xqWalzDzqGz 9rbyD5ehpmnl+ezt3M/RSPe7Q9/ajeh5+9Ztm3vKh9xoM7SaimLUR18C2JKf+Kg2 APoJwzDOuiAF+hHU/pHXryObdLyP+y2kEhx7UaLfo0gq/RJa60/n88Ndrpz7FmqG u5bk3L8zwdWXc0+jdOYXkn4lnYfW++/qOPLyDz7BfH3jTXVnplx949inhPvnSgw/ 8RSIHM7P8PdSUYtxlxSkONE+o/u7EkNElMbpcuRKUhTjmLH/iHbDQQ7DHqL77zbh oQxeRa9duBQHkRj+HnIdr7y/e178AvmmnHt5VQAmaNo59/EZ8QSJAY7EURJvMu2x KipYj2CaEToYve2eYYiwl4rWY6jN8RWF5XtsuWSyhO7aJG8XXQFkNdWYIqIHK8nH 8FOSFJMoteEfZfQEo1SNCPCW2/BTjWK1uXkp9dDDegjrDqpkAUtiJhNp4ma3qUrx MG6dqkyFMQ2ExQmaxgU2c/07D2ZJsCz3Q68Xh76Cvac2pZwi8jCO8rIZd4jielmc uHxmsEMe1vMBZJf0YY8Pda95yH5p+tWrI86XMZbTE5a1gVlXFKyryeowp0Cy4Wf+ hdSrWGp26N008hW4XnS6/OBS7MnUVHoK0osoTV+22qF56c95qKdtZBzB66J/imSc /Rmsg/KDdHFbA9O3RrZWByD/qPf1KTCwze3y2KCbn9vnP4ExoItiwr11zvncqq6+ oXGV//XVa5qCzXxL6M3ZfBfMZyFPBvywgD3FGDjLnGVl83o4T+HJAZ/PFxWTqrcj GxerHljRqyL9sWXxqU2/nkHki1H4HDkvJeM7vZooeLdnNU2R10K34G1XdgveTmE7 vmv7fNDcFY1u3ABpNa5J6rZd9MouqGpjw6z1GLXn6vDxV/s9o1cYvcroNUanGP2J UZ3RG4zeZPQ2o3cY/YtRqCdqZ3Qho6WMuhitYHQZ0pr6mRr21Zvv03VFuuMoX0Gd VqT7BlupKFoXw8eo/8yynUR+HvEa4g3EPxEXYuwSxOWIaxADiGHEBKKGeADxCOIx a1wXkE81zH/ut0OdG0LtjQ2+hCSBzLUKWoeSyErC+pickIQgfAmhgaSG319xPEvo ioQ6Ld9D0CL04ddZQuknaxA4W1hRtXeySa0DXWM7BHjDFhHkhLUKYs2cJTcrA0H4 mmtXYgk+m1GVTBBOsVVbXJGDsNTWKexIqpqQ4aWYqgbps4LPCDFNMPcLYXQpldrC g0bcVHcKcQ220DqyB4PTHYKWScZVgCGsw/LBEgHWsjYLZR2zRTMxWZUwfaFwOAot SXVXTIuLM9V/ZeuSMw/UxW/s4KOF6W2GNjmp8Uo6rci8ImsZRVLxG+1hZWhgrlv6 /4F/ABcSIgQAEAAA } write/binary %program.exe program call %program.exe =image rebol_tutorial/we_love.png =image rebol_tutorial/we_love2.png Copy and paste that entire block of code into the Rebol interpreter and run it. It will execute a little demo program, which was written and created by a language entirely unrelated to Rebol. There are a world of open source applications available to perform just about every specific high level task conceivable. Many are free to use and distribute even in commercial applications (see http://sourceforge.net ). If you can interface with them on the command line, they can be used to help in your own applications - even if you don't know how they were written. This extends the power of the language, but can also restrict it's cross platform usability. For example, if you write a program that calls DOS operating system commands, that program can't be used on a UNIX system. Whenever you use any executable or code created by another programmer, be absolutely sure to check, and follow, the licensing terms by which it's distributed. ===A Quick Summary of the Rebol Language The list below summarizes some key characteristics of the Rebol language. Knowing how to put these elements to use constitutes a fundamental understanding of how Rebol works: # To start off, Rebol has hundreds of built-in function words that perform common tasks. As in other languages, function words are typically followed by passed parameters. Unlike other languages, passed parameters are placed immediately after the function word and are not necessarily enclosed in parenthesis. To accomplish a desired goal, functions are arranged in succession, one after another. Line terminators are not required at any point, and all expressions are evaluated in left to right order, then vertically down through the code. For example, "function1 parameter1 parameter2 function2 parameter function3 (expression that evaluates to a single parameter)" is a valid line that evaluates 3 functions from left to right. Empty white space (spaces, tabs, newlines, etc.) can be inserted as desired to make code more readable. Text after a semicolon and before a new line is treated as a comment. You can complete significant work by simply knowing the predefined functions in the language, and organizing them into a useful order. # Rebol contains a rich set of conditional and looping structures, which can be used to manage program flow and data processing activites. If, while, for, foreach, and other typical structures are supported. More powerful and specific looping structures and conditions such as "forskip", "any", and "all" add to Rebol's flexibility. # Because many common types of data values are automatically recognized and handled natively by Rebol, calculating, looping, and making conditional decisions based upon data content is straightforward and natural to perform, without any external modules or toolkits. Numbers, text strings, money values, times, tuples, urls, binary representations of images, sounds, etc. are all automatically handled. Rebol can increment, compare, and perform proper computations on most common types of data (i.e., the interpreter automatically knows that 5:32am + 00:35:15 = 6:07:15am, and it can automatically apply visual effects to raw binary image data, etc.). Network resources and Internet protocols (http documents, ftp directories, email accounts, dns services, etc.) can also be accessed natively, just as easily as local files. Data of any type can be written to and read from virtually any connected device or resource (i.e., "write %file.txt data" works just as easily as "write ftp://user:pass@website.com data", using the same common syntax). The percent symbol ("%") and the syntax "%(/drive)/path/path/.../file.ext" are used cross-platform to refer to local file values on any operating system. # Any data or code can be assigned a word label. The colon character (":") is used to assign word labels to constants, variable values, evaluated expressions, functions, and data/action blocks of any type. Once assigned, variable words can be used to represent all of the data and/or actions contained in the given expression, block, etc. Just put a colon at the end of a word, and thereafter it represents all the following actions and/or data. That forms a significant part of the Rebol language structure, and is the basis for it's flexible natural language dialecting abilities. Because anything in Rebol, data or code, can be grouped together and assigned a label, it's easy to organize coding thought and data structure in a way that tends to flow like natural human language thought processes. # Both data and action code are stored in "blocks", which are delineated by starting and ending brackets ("[]"). Blocks can contain data of any type: groups of text strings, arrays of resource locators, collections of related binary data, collections of actions (functions), other enclosed blocks, etc. Data items contained in blocks are separated by white space. Blocks can be automatically treated as lists of data, called "series", and manipulated using built-in functions that enable searching, sorting, ordering, and otherwise organizing the blocked data. Data and function words contained in blocks can be evaluated (their actions performed and their data values assigned) using the "do" word. New function words can also be defined using the "does" and "func" words. "Does" forces a block to be evaluated every time its word label is encountered. The "func" word creates an exectuable block in the same way as "does", but additionally allows you to pass your own specified parameters to the newly defined function word. You can "do" a module of code contained in a text file, as long as it contains the minimum header "rebol[]". Blocks/series are a simple, freeform data structure that can be used to store any type of information or code. The fact that data and code can be organized using one ubiquitous syntactic structure is another main reason Rebol is easier to use than other programming languages. # The syntax "view layout [block]" is used to create basic GUI layouts. You can add graphic widgets to the layout simply by adding widget identifier words to the enclosed block: "button", "field", "text-list", etc. Color, position, spacing, and other facet words can be added after each widget identifier. Action blocks added immediately after any widget will perform the enclosed functions whenever the widget is activated (i.e., when the widget is clicked with a mouse, when the enter key pressed, etc.). Path refinements can be used to refer to items in the GUI layout (i.e., "face/offset" refers to the position of the selected widget face). Those simple guidelines can be used to create useful GUIs for data input and output, in a way that's native (doesn't require any external toolkits) and much easier than any other language. Because of the simple GUI syntax, the remarkably uncluttered general coding syntax, and the single ubiquitous block data structure, Rebol programs can be much shorter, simpler, and more powerful than comparable code in any other language (see http://www.rebol.com/oneliners.html). ---Using Rebol's Built-In Help Typing "help (any word)" at the Rebol console prompt will display required syntax for any built-in Rebol function. The word "what" lists all built-in words. Together, those two words provide a reference that's sufficient for most situations in which you need syntax help - without any external guide. "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 (i.e., "editor system/view/VID/vid-styles"). The Rebol "desktop" that appears by default when you run the view.exe interpreter can also be used as a gateway into a world of "Rebsites" that developers use to share useful code. Surfing the public rebsites is a great way to explore the language more deeply. All of the code in the rebol.org archive, and much more, is available on the rebsites. It's amazing what you can do with that little view.exe file! =image rebol_tutorial/helpforeach.png =image rebol_tutorial/what.png =image rebol_tutorial/editorstyles.png ===8 Complete Rebol Programs For You To Study The following programs use a number of techniques explained throughout the tutorial. They demonstrate how pieces of code can be put together to construct complete applications, and they provide some guidance as to how basic language building blocks can be assembled for useful purposes. There are detailed line-by-line explanations of each program included in the comments (it's amazing how short these programs are without the comments!). Studying these examples is perhaps the most valuable part of the tutorial. A downloadable package of all the demo programs is available at: http://musiclessonz.com/rebol_tutorial_examples.zip. The zip file contains screenshots, separated source code, and packaged executables (clickable ".exe" files) of each example. The XpackerX XML files used to create each executable are also included. NOTE: In order to fit the code examples within the width of a web page, certain formatting options have been introduced. Long lines of code have been shortened, so they they don't get chopped off when printed. The built in word "trim" has been used to clean long strings of text that are written on separate lines. mystring: trim { This string} is the same as: mystring: "This string" "Join" and "rejoin" have also been used to join together long sections of text: join "All " [ "the " "same " "line of text." ] is the same as: "All the same line of text." Blocks have also been written onto multiple lines where necessary: myblock: [This block ] is the same as: myblock: [This block] To run the example programs in this section, you can use any of the methods described earlier: type or copy/paste the code into the Rebol interpreter, save the program as a text file and run it using "do {filename}", install the Rebol interpreter on your computer and click the saved code file (this is the easiest way), use XpackerX to package and distribute it as an executable, etc. --- Little Email Client The first example is a complete graphical email client that can be used to read and send messages. The code is heavily commented to provide line-by-line explanations of how each element works: Rebol [Title: "Little Email Client"] ; (every program requires a minimum header) view layout [ ; The line above creates the GUI layout. h1 "Send Email:" ; The second line adds a text label to the GUI. address: field "recipient@website.com" ; This line creates a text entry field, containing ; the default text "recipient@website.com". It assigns ; the variable word "address" to the text entered here. subject: field "Subject" ; another text entry field for the email subject line body: area "Body" ; This creates a larger, multiline text entry area for ; the body text of the email. btn "Send" [ ; A button with the word "send". The functions ; inside this action block are executed whenever ; the button is clicked. send/subject to-email address/text body/text subject/text ; This line does most of the work. It uses the ; built-in Rebol word "send" to send the email. The ; send function, with its "/subject" refinement ; accepts three parameters. It's passed the current ; text contained in each field labeled above ; (referred to as "address/text" "body/text" and ; "subject/text"). The built-in "to-email" function ; ensures that the address text is treated as an ; email data value. alert "Message Sent." ; alerts the user when the previous line is complete. ] h1 "Read Email:" ; Another text label mailbox: field "pop://user:pass@website.com" ; Another text entry field. The user's email account ; info is entered here. btn "Read" [ ; An additional button, this time with an action ; block that reads messages from a specified mailbox. ; It only takes one line: editor read to-url mailbox/text ; The built-in "to-url" function ensures that the ; text in the mailbox field is treated as a url. ; The contents of the mailbox are read and displayed ; in the built-in Rebol editor. ] ] =image rebol_tutorial/little_email_client.png Here's the same code, without comments - it's very simple: Rebol [Title: "Little Email Client"] view layout [ h1 "Send Email:" address: field "recipient@website.com" subject: field "Subject" body: area "Body" btn "Send" [ send/subject to-email address/text body/text subject/text alert "Message Sent." ] h1 "Read Email:" mailbox: field "pop://user:pass@website.com" btn "Read" [ editor read to-url mailbox/text ] ] --- FTP Chat Room The second example is a simple chat application that lets users send instant text messages back and forth across the Internet. It includes password protected access for administrators to erase chat contents. It also allows users to pause activity momentarily, and requires a username/password to continue ["secret" "password"]. The chat "rooms" are created by dynamically creating, reading, appending, and saving text files via ftp (to use the program, you'll need access to an available ftp server: ftp address, username, and password). Rebol [title: "FTP Chat Room"] ; required header webserver: to-url request-text/title/default trim { Web Server Address:} {ftp://user:pass@website.com/chat.txt} ; get the url of a webserver text file to use for the chat. ; The ftp username, password, domain, and filename must be ; entered in the format shown. name: request-text/title "Enter your name:" ; get the user's name cls: does [prin "^(1B)[J"] ; "cls" now clears the screen (explained earlier in the tutorial). write/append webserver join now [ ": " name " has entered the room." newline ] ; The line above writes some text to the webserver. ; The "/append" refinement adds it to the existing ; text in the webserver file (as opposed to erasing ; what's already there). Using "join", the text ; written to the webserver is the combined value of ; {the user's name}, some static text, the current ; date and time, and a carriage return. forever [ current-chat: read webserver ; read the messages that are currently on the webserver, ; and assign the variable word "current-chat" cls ; clear the screen using the word defined above print join "--------------------------------------------------" [ newline {You are logged in as: } name newline {Type "room" to switch chat rooms.} newline {Type "lock" to pause/lock your chat.} newline {Type "quit" to end your chat.} newline {Type "clear" to erase the current chat.} newline {Press [ENTER] to periodically update the display.} newline "--------------------------------------------------" newline] ; displays a greeting and some instructions print join "Here's the current chat text at: " [webserver newline] print current-chat sent-message: copy join name [ " says: " entered-text: ask "You say: " ] ; get the text to send, then check for commands below ; ("quit", "clear", "room", "lock", and [ENTER]) ; The built-in word "ask" requests some info within ; the interpreter. switch/default entered-text [ "quit" [break] ; if the user typed in "quit", ; stop the forever loop (exit the program) "clear" [ if/else request-pass = ["secret" "password"] [ write webserver ""] [alert trim { You must know the administrator password to clear the room!} ] ] ; if the user typed in "clear", erase the ; current text chat. But first, ask user ; for the administrator username/password "room" [ write/append webserver join now [ ": " name " has left the room." newline] webserver: to-url request-text/title/default {New Web Server Address:} to-string webserver write/append webserver join now [ ": " name " has entered the room." newline ] ] ; if the user typed in "room", request a new ; webserver address, and run some code that was ; presented earlier in the program, ; using the newly entered "webserver" variable, ; to effectively change chat "rooms". "lock" [ alert trim {The program will now pause for 5 seconds. You'll need the correct username and password to continue.} pause-time: now/time + 5 ; assign a variable to the time 5 seconds from now forever [if now/time = pause-time [ ; wait 5 seconds while [request-pass <> ["secret" "password"]] [ alert "Incorrect password - look in the source!" ] ; don't go on until the user gets the password right. break ] ] ; exit the forever loop after 5 seconds have passed ] ] [if entered-text <> "" [ write/append webserver join sent-message [newline]]] ; default case: as long as the entered message is not ; blank ([Enter]), write the message to the web server ; (append it to the current text) ] ; when the "forever" loop is exited, do the following: cls print "Goodbye!" write/append webserver join now [ ": " name " has closed chat." newline] wait 1 =image rebol_tutorial/chat_room.png The bulk of the program runs within a "forever" loop, and uses a conditional "switch" statement to decide how to respond to user input. This is a classic structure that can be adjusted to match a variety of generalized situations in which the computer repeatedly waits for and responds to user interaction. --- Looping Through Data One of the most important applications of loop structures is to step through lists of data. By stepping through elements in a block, loops can be used to process and perform actions on each item in a given data series. This technique is used in all types of programming, and it's a cornerstone of the way programmers think about working with tables of data (such as those found in databases). Because many programs work with lists of data, you'll very often come across situations that require the use of loops. Thinking about how to put looping structures to use is a fundamental part of learning to write code in any language. The example below demonstrates several ways in which you'll see loops commonly put to use. Be sure to see case study #5 in the "Real World Examples" section for a GUI version of this code. ; First, a small user database is defined. It's organized ; into a block structure: the "users" block contains 5 ; blocks, which each contain 5 items of information for ; each user. Blank items are represented with empty quotes. ; Note that each block is split onto two lines, so that ; they fit onto this web page (they don't need to be split ; this way in normal use): users: [ ["John" "Smith" "123 Toleen Lane" "Forest Hills, NJ" "555-1234"] ["Paul" "Thompson" "234 Georgetown Place" "Peanut Grove, AL" "555-2345"] ["Jim" "Persee" "345 Portman Pike" "Orange Grove, FL" "555-3456"] ["George" "Jones" "456 Topforge Court" "Mountain Creek, CO" ""] ["Tim" "Paulson" "" "" "555-5678"] ] ; This program does not have a GUI. Instead, it's a text ; based "console" program. Since there's no GUI, we need ; to format the output so that it's got a nice layout on the ; screen. Here's a little function that uses a loop to draw ; a line. It prints 65 dashes next to each other, and then ; a carriage return. We'll use those lines to help print ; nicely formatted output: draw-line: does [loop 65 [prin "-"] print ""] ; Note that this is not the most efficient way to draw a line ; of characters, because the program needs to run through ; the loop every time a line is drawn. You'll see some ; flicker on the screen every time this happens, because ; the computer has to run through the "prin" function 65 ; times for each line. Although it only takes a fraction of ; a second on a modern computer, it's still quite noticable. ; It would be faster, instead, to build a block of characters ; once, and then print that block, as follows: ; ; a-line: copy [] ; loop 65 [append a-line "-"] ; ; remove the spaces and turn it ; ; into a string of characters: ; a-line: trim to-string a-line ; ; now you can print "a-line" ; ; anywhere you need it: ; print a-line ; ; The inefficient code above is left in this example to ; demonstrate a point about how the coding thought process ; can dramatically effect the performance of programs you ; create. That's especially true for programs that perform ; complex loops on large lists of data. The more effecient ; line printing function is implemented in another example ; following this one, to demonstrate the difference in its ; effectiveness. ; Next is a small function that prints out all of the data ; in the database. It uses a foreach loop to cycle through ; each block of user data, and then it prints a line ; displaying each element in the block (items numbered 1-5 ; in each block). This creates a nicely formatted display: print-all: does [ foreach user users [ draw-line print rejoin ["User: " user/1 " " user/2] draw-line print rejoin ["Address: " user/3 " " user/4] print rejoin ["Phone: " user/5] print newline ] ] ; The following code uses a forever loop to continually ; request a choice from the user. It uses several foreach ; loops to pull information from the data block, and a ; conditional "switch" structure to decide how to respond ; to the user's request (as in the ftp chat example above, ; the "switch" inside a forever loop is used - it's a ; common design in command line programs): forever [ ; First, print some nice formatting and display info: prin "^(1B)[J" ; this code clears the screen. print "Here are the current users in the database:^/" ; The "^/" at the end of the line above prints a newline. draw-line ; Now print the list of user names. A foreach loop is ; used to get the first and last name of each user in the ; database. The first name is item 1 in each block, and ; the last name is item 2 in each block. So for each ; block in the database, "user/1" and "user/2" are ; printed: foreach user users [prin rejoin [user/1 " " user/2 " "]] print "" draw-line ; Now ask the user for a choice: print "Type the name of a user below.^/" print "Type 'all' for a complete database listing." print "Press [Enter] to quit.^/" answer: ask {What person would you like info about? } print newline ; Now decide what to do with the user's response: switch/default answer [ ; If they typed "all", execute the "print-all" ; function defined earlier: "all" [print-all] ; If they typed the [Enter] key alone (""), print a ; goodbye message, and end the program. Note that ; "ask" is used to display the message, instead of ; "print". This allows the program to wait for the ; user to press a key before ending the program: "" [ask "Goodbye! Press [Enter] to end." quit] ; If neither of the choices above were selected, the ; default block below is executed (this is the last ; part of the switch structure): ][ ; This section starts by creating a "flag" variable, ; which is used to track whether or not the user's ; choice has been found in the database - the word ; "found" is initially set to false to indicate that ; the user name has not yet been found: found: false ; Next, a foreach loop steps through each user block ; in the database: foreach user users [ ; If the entered user name is found in the ; database (either the first or last name), the ; info for that user is printed out in a nicely ; formatted display, and the "found" flag is set ; to true. The "rejoin" action is used to join ; the first name and last name, and is used in ; conjunction with the "find" action to check ; whether the user's answer matches any part of ; the names in the database (when you run this ; code, try entering single characters, or a ; part of a name, to see what happens). if find rejoin [user/1 " " user/2] answer [ draw-line print rejoin ["User: " user/1 " " user/2] draw-line print rejoin ["Address: " user/3 " " user/4] print rejoin ["Phone: " user/5] print newline found: true ] ] ; If the "found" variable is still false after ; looping through the entire user database, then the ; user name was not found in the database. Print a ; message to that effect: if found <> true [ ; "<>" means "not equal to" print "That user is not in the database!^/"] ] ; Wait for a user response, and then continue again at ; the beginning of the forever loop: ask "Press [ENTER] to continue" ] =image rebol_tutorial/users.png Here's the entire program without the comments. Try to follow the program flow on your own. NOTE: In this version, the ineffient "draw-line" function is replaced by the suggested "print a-line" routine above. As a result, you'll see a dramatic reduction in screen flicker: users: [ ["John" "Smith" "123 Tomline Lane" "Forest Hills, NJ" "555-1234"] ["Paul" "Thompson" "234 Georgetown Place" "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"] ] a-line: copy [] loop 65 [append a-line "-"] a-line: trim to-string a-line print-all: does [ foreach user users [ print a-line print rejoin ["User: " user/1 " " user/2] print a-line print rejoin ["Address: " user/3 " " user/4] print rejoin ["Phone: " user/5] print newline ] ] forever [ prin "^(1B)[J" print "Here are the current users in the database:^/" print a-line foreach user users [prin rejoin [user/1 " " user/2 " "]] print "" print a-line print "Type the name of a user below.^/" print "Type 'all' for a complete database listing." print "Press [Enter] to quit.^/" answer: ask {What person would you like info about? } print newline switch/default answer [ "all" [print-all] "" [ask "Goodbye! Press any key to end." quit] ][ found: false foreach user users [ if find rejoin [user/1 " " user/2] answer [ print a-line print rejoin ["User: " user/1 " " user/2] print a-line print rejoin ["Address: " user/3 " " user/4] print rejoin ["Phone: " user/5] print newline found: true ] ] if found <> true [ print "That user is not in the database!^/" ] ] ask "Press [ENTER] to continue" ] =image rebol_tutorial/looping2.png For some perspective, here's a GUI version of the same program that demonstrates how GUI and command line programming styles differ. Notice how much of the data handling is managed by the built-in GUI tools in the language, rather than by homemade loops: Rebol [title: "User Database GUI Example"] users: [ ["John" "Smith" "123 Tomline Lane" "Forest Hills, NJ" "555-1234"] ["Paul" "Thompson" "234 Georgetown Place" "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"] ] user-list: copy [] foreach user users [append user-list user/1] user-list: sort user-list view display-gui: layout [ h2 "Click a user name to display their information:" across list-users: text-list 200x400 data user-list [ current-info: [] foreach user users [ if find user/1 value [ current-info: rejoin [ "FIRST NAME: " user/1 newline newline "LAST NAME: " user/2 newline newline "ADDRESS: " user/3 newline newline "CITY/STATE: " user/4 newline newline "PHONE: " user/5 ] ] ] display/text: current-info show display show list-users ] display: area "" 300x400 wrap ] =image rebol_tutorial/users2.png More powerful and specific looping structures and conditions such as "forskip", "any", and "all" add to Rebol's flexibility. For more information, see the Rebol Dictionary in the Rebol desktop folder REBOL->Tools. --- Image Effector The next application creates a GUI interface, downloads and displays an image from the Internet, allows you to apply effects to it, and lets you save the effected image to the hard drive. In the mix, there are several routines which get data, and alert the user with text information. REBOL [Title: ""] ; header is still required, even if a title isn't included effect-types: ["Invert" "Grayscale" "Emboss" "Blur" "Sharpen" "Flip 1x1" "Rotate 90" "Tint 83" "Contrast 66" "Luma 150" "None"] ; this creates a short list of image effects that are built ; into Rebol, and assigns the variable word "effect-types" ; to the block do %/c/play_sound.r ; The line above imports the simple "play-sound" function ; created earlier in the tutorial. For this program to work ; correctly as it is, the play_sound.r file should be saved ; to C:\ image-url: to-url request-text/title/default { Enter the url of an image to use:} trim { http://rebol.com/view/demos/palms.jpg} ; ask user for the location of a new image (with a default ; location), and assign it to the word "new-image" gui: [ ; The following code displays the program menu, using a ; "choice" button widget (a menu-select type of button ; built in to Rebol). The button is 160 pixels ; across, and is placed at the uppermost, leftmost ; pixel in the GUI (0x0) using the built-in word "at". ; The action block for the button contains various ; functions to be performed, based on the selected choice ; (using conditional "if" evaluations. This could have ; been done with less code, using a "switch" syntax. ; "If" was used, however, to demonstrate that there are ; always alternate ways to express yourself in code - ; just like in spoken language.). across ; horizontally aligns all the following GUI widgets, ; so they appear next to each other in the layout ; (the default behavior in Rebol is to align elements ; vertically). space -1 ; changes the spacing of consecutive widgets so they're ; on top of each other at 20x2 choice 160 tan trim { Save Image} "View Saved Image" "Download New Image" trim { -------------} "Exit" [ if value = "Save Image" [ filename: to-file request-file/title/file/save trim { Save file as:} "Save" %/c/effectedimage.png ; request a filename to save the image as, ; defaults to "c:\effectedimage.png" save/png filename to-image picture ; save the image to hard drive ] if value = "View Saved Image" [ view-filename: to-file request-file/title/file trim { View file:} "Save" filename view/new center-face layout [image load view-filename] ; read the selected image from the hard drive ; and display it in a new GUI window ] if value = "Download New Image" [ new-image: load to-url request-text/title/default trim { Enter a new image url} trim { http://www.rebol.com/view/bay.jpg} ; ask for the location of a new image, ; and assign it to the word "new-image" picture/image: new-image ; replace the old image with the new one show picture ; update the GUI display ] if value = "-------------" [] ; don't do anything if value = "Exit" [ play-sound %/c/windows/media/tada.wav quit ; exit the program ] ] choice tan "Info" "About" [alert "Image Effector - Copyright 2005, Nick Antonaccio"] ; a simple "about" box below ; vertically aligns successive GUI widgets - ; the opposite of "across" space 5 ; spread out the widgets some more pad 2 ; put 2 pixels of blank space before the next widget box 550x1 white ; draws a line 550 pixels wide, 1 pixel tall ; (just a cosmetic separator) pad 10 ; put some more space between widgets vh1 "Double click each effect in the list on the right:" ; a big text header for the GUI return ; advances to the next row in the GUI across picture: image load image-url ; get the image entered at the beginning of the program, ; and give it a label text-list data effect-types [ current-effect: to-string value picture/effect: to-block form current-effect show picture ] ; The code above creates a text-list gui widget ; and assigns a block of actions to it, to be run whenever the ; user clicks on the list. The block of actions is indented ; and each action is placed on separate line for readability. ; The first line assigns the word "current-effect" to the value ; which the user has selected from the list. The second line ; applies that effect to the image (the words "to-block" and "form" ; are required for the way effects are applied syntactically. ; The third line displays the newly effected image. The "show" ; word is _very_ important. It needs to be used whenever a GUI ; element is updated. ] view/options center-face layout gui [no-title] ; display the gui block above ; "/options [no title]" displays the window without a title bar ; (so it can't be moved around), ; and "center-face" centers the window on the screen =image rebol_tutorial/image_effector.png The built-in words included in the image effector program are: rebol[], invert, grayscale, emboss, blur, sharpen, flip, rotate, tint, contrast, luma, none, vh1, across, image, load, text-list data, to-string, to-block, to-image, to-url, value, form, show, return, button, save/png, alert, view/new, center-face, layout, at, now/time, forever, while, request-pass, request-text/title/default, request-file/title/file/save, break, quit, center-face, space, box, pad. The image effector also imports and uses the "play-sound" module created earlier in the tutorial. It plays a sound when the program is shut down. --- Sliding Tile Game Here's a simple example that implements a GUI version of the classic sliding tile game ... in only 8 lines of code. The board contains 15 movable pieces and 1 empty space. The point is to rearrange the pieces in inverse numerical order on the grid. A piece can only be moved if the empty space is adjacent to its border. Click on a piece to slide it into the empty space. Rebol [Title: "Sliding Tile Game"] gui: [ ; Define some basic layout parameters. "origin 0x0" ; starts the layout in the upper left corner of the ; GUI window. "space 0x0" dictates that there's no ; space between adjacent widgets, and "across" lays ; out consecutive widgets next to each other: origin 0x0 space 0x0 across ; The "style" word below allows you to redefine the ; appearance and action characteristics of any built- ; in GUI widget. The section below creates a newly ; defined button style called "piece", with an action ; block that swaps the current button's position with ; that of the adjacent empty space. That action is ; run whenever one of the buttons is clicked. style piece button 60x60 [ ; The line below checks to see if the clicked button ; is adjacent to the empty space. The "offset" ; refinement contains the position of the given ; widget. The word "face" is used to refer to the ; currently clicked widget. The "empty" button is ; defined later (at the end of the GUI layout). ; It's ok that the empty button is not yet defined, ; because this code is not evaluated until the ; the entire layout is built and "view"ed: if not find [0x60 60x0 0x-60 -60x0] face/offset - empty/offset [exit] ; In English, that reads 'subtract the position of ; the empty space from the position of the clicked ; button (the positions are in the form of ; Horizontal x Vertical coordinate pairs). If that ; difference isn't 60 pixels on one of the 4 sides, ; then don't do anything.' (60 pixels is the size of ; the "piece" button defined above.) ; The next three lines swap the positions of the ; clicked button with the empty button. ; First, create a variable to hold the current ; position of the clicked button: temp: face/offset ; Next, move the button's position to that of the ; current empty space: face/offset: empty/offset ; Last, move the empty space (button), to the old ; position occupied by the clicked button: empty/offset: temp ] ; The lines below draw the "piece" style buttons onto ; the GUI display. Each of these buttons contains all ; of the action code defined for the piece style above: piece "1" piece "2" piece "3" piece "4" return piece "5" piece "6" piece "7" piece "8" return piece "9" piece "10" piece "11" piece "12" return piece "13" piece "14" piece "15" ; Here's the empty space. Its beveled edge is removed ; to make it look less like a movable piece, and more ; like an empty space: empty: piece 200.200.200 edge [size: 0] ] ; Display the whole GUI block, centered on the user's screen: view center-face layout gui =image rebol_tutorial/tile_game.png Here's the whole program without any comments, and reduced to a more compact format. It's tiny: Rebol [Title: "Sliding Tile Game"] view center-face gui: layout [ origin 0x0 space 0x0 across style p button 60x60 [ if not find [0x60 60x0 0x-60 -60x0] face/offset - empty/offset [exit] temp: face/offset face/offset: empty/offset empty/offset: temp ] p "A" p "B" p "C" p "D" return p "E" p "F" p "G" p "H" return p "I" p "J" p "K" p "L" return p "M" p "N" p "O" empty: p 200.200.200 edge [size: 0] ] Be sure to take a break from coding and play a few games :) --- Guitar Chord Diagram Maker The fifth example is a program that creates, saves, and prints collections of guitar chord fretboard diagrams. It demonstrates some more common and useful file, data, and GUI manipulation techniques: Rebol [Title: "Guitar Chord Diagram Maker"] ; load embedded images: fretboard: load 64#{ iVBORw0KGgoAAAANSUhEUgAAAFUAAABkCAIAAAB4sesFAAAACXBIWXMAAAsTAAAL EwEAmpwYAAAA2UlEQVR4nO3YQQqDQBAF0XTIwXtuNjfrLITs0rowGqbqbRWxEEL+ RFU9wJ53v8DN7Gezn81+NvvZXv3liLjmPX6n/4NL//72s9l/QGbWd5m53dbc8/kR uv5RJ/QvzH42+9nsZ7OfzX62nfOPzZzzyNUxxh8+qhfVHo94/rM49y+b/Wz2s9nP Zj+b/WzuX/cvmfuXzX42+9nsZ7OfzX4296/7l8z9y2Y/m/1s9rPZz2Y/m/vX/Uvm /mWzn81+NvvZ7Gezn8396/4l2/n+y6N/f/vZ7Gezn81+tjenRWXD3TC8nAAAAABJ RU5ErkJggg== } barimage: load 64#{ iVBORw0KGgoAAAANSUhEUgAAAEoAAAAFCAIAAABtvO2fAAAACXBIWXMAAAsTAAAL EwEAmpwYAAAAHElEQVR4nGNsaGhgGL6AaaAdQFsw6r2hDIa59wCf/AGKgzU3RwAA AABJRU5ErkJggg== } dot: load 64#{ iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAIAAAACUFjqAAAACXBIWXMAAAsTAAAL EwEAmpwYAAAAFElEQVR4nGNsaGhgwA2Y8MiNYGkA22EBlPG3fjQAAAAASUVORK5C YII= } ; Gui Design: ; The routine below was copied from ; http://rebol.com/how-to/feel.html movestyle: [ engage: func [face action event] [ if action = 'down [ face/data: event/offset remove find face/parent-face/pane face append face/parent-face/pane face ] if find [over away] action [ face/offset: face/offset + event/offset - face/data ] show face ] ] ; With that defined, adding "feel movestyle" to any widget ; makes it movable within the GUI. It's very useful for all ; sorts of graphic applications... If you want to pursue ; building graphic layouts that respond to user events, learning ; all about how "feel" works in Rebol is very important. See ; the URL above for more info. gui: [ backdrop white ; makes the GUI background white currentfretboard: image fretboard 255x300 ; show the fretboard image, and resize it ; (the saved image is actually 85x100 pixels) currentbar: image barimage 240x15 feel movestyle ; Show the bar image, resize it, and make it movable. ; Notice the "feel movestyle". Thats' what enables ; the dragging. text "INSTRUCTIONS:" underline text "Drag dots and other widgets onto the fretboard." across text "Resize the fretboard:" tab ; "tab" aligns the next GUI element with a predefined ; column spacer rotary "255x300" "170x200" "85x100" [ currentfretboard/size: to-pair value show currentfretboard switch value [ "255x300" [currentbar/size: 240x15 show currentbar] "170x200" [currentbar/size: 160x10 show currentbar] "85x100" [currentbar/size: 80x5 show currentbar] ] ] ; The rotary button above lets you select a size for the ; fretboard. In the action block, the fretboard image is ; resized, and then the bar image is also resized, ; according to the value chosen. This keeps the bar size ; proportioned correctly to the fretboard image. ; After each resize, the GUI is updated to actually display ; the changed image. The built-in word "show" updates the ; display. This needs to be done whenever a widget is ; changed within a GUI. Be aware of this - not "show"ing ; a changed GUI element is an easily overlooked source of ; errors. return button "Save Diagram" [ filename: to-file request-file/save/file "1.png" save/png filename to-image currentfretboard ] ; The action block of the above button requests a filename ; from the user, and then saves the current fretboard image ; to that filename. tab ; The action block of the button below prints out a user- ; selected set of images to an html page, where they can be ; viewed together, uploaded the Internet, sent to a printer, ; etc. button "Print" [ filelist: sort request-file/title "Select image(s) to print:" ; Get a list of files to print. html: copy "" ; start creating a block that holds the html layout, ; and give it the label "html". foreach file filelist [ append html rejoin [ {} ] ] ; The foreach loop builds an html layout that displays ; each of the selected images. append html [] ; finish up the html layout. Now the variable "html" ; contains a complete html document that will be ; written to the hard drive and opened in the default ; browser. The code below accomplishes that. write %chords.html trim/auto html browse %chords.html ] ] ; Each of the following loops puts 50 movable dots onto the GUI, ; all at the same locations. This creates three stacks of dots ; that the user can move around the screen and put onto the ; fretboard. There are three sizes to accommodate the resizing ; feature of the fretboard image. Notice the "feel movestyle" ; code at the end of each line. Again, that's what makes the ; dots draggable. loop 50 [append gui [at 275x50 image dot 30x30 feel movestyle]] loop 50 [append gui [at 275x100 image dot 20x20 feel movestyle]] loop 50 [append gui [at 275x140 image dot 10x10 feel movestyle]] ; The following loops add some additional dragable widgets to ; the GUI. loop 6 [append gui [at 273x165 text "X" bold feel movestyle]] loop 6 [append gui [at 273x185 text "O" bold feel movestyle]] view layout gui =image rebol_tutorial/gcdm.png =image rebol_tutorial/gcdm2.png =image rebol_tutorial/gcdm3.png --- Listview Database This example is a simple database program with a GUI interface. It lets you store, search, alter, organize and display data in a way that's useful. This type of application is common in any situation where categorical information must be organized. Although this example handles client schedule information, the techniques it demonstrates can be directly applied to managing information of any type - inventories, customer/vendor lists, payroll/tax records, mp3 collections, photo albums, high score tables in games, etc. This program uses the listview module found at http://www.hmkdesign.dk/rebol/list-view/list-view.r, which is compressed and embedded within the script. The listview module handles all the main work of displaying, sorting, filtering, altering, and manipulating data, with a familiar user interface that's easy to program. Documentation is available at http://www.hmkdesign.dk/rebol/list-view/list-view.html. Learning to use the listview module is absolutely essential if you intend to do any programming that involves information management. It's very simple to learn - you can get the basics in one sitting, and you will use it repeatedly. The program below creates a default database if one doesn't already exist (a simple text file containing Rebol data blocks), and makes a date stamped backup of the previous file whenever data is saved. There's an additional function that traps the GUI close button to keep the program from being shut down accidentally. Rebol [title: "Database"] ; The function below watches for the GUI close button, to keep ; the program from being shut down accidentally. The code was ; adjusted from an example at: ; http://www.rebolforces.com/view-faq.html evt-close: func [face event] [ either event/type = 'close [ inform layout [ across Button "Save Changes" [ ; when the save button is clicked, a backup data ; file is automatically created: 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 ; The code below is the list-view.r module in compressed format. ; It's decompressed, and then imported with the "do" command. ; Like any other module, you don't have to understand how it was ; programmed (the uncompressed code is all just native Rebol). ; You just have to include the compressed blob, and learn how ; to use it... do decompress #{ 789CCD3D6B73E3C871DFF52BE6369592F67629905A9F73A6B2A7B2EFECD8E557 2AE738A96221551031142181040D8092B8A9FC97FCD474F7BCBA07038ADA3B3B E6D56909CCABA7BBA75FD333FCB75FFEE28FBF538B33A5FE54F5B59EAB377FFE CD77EA77BFF9FE4F933FFFE697FFA17E552CF51B28FD558585FF58575D3F79AC F4D3650B2F7FBEEFD74D3B578B37BFD6DBB67A50BFAF1E0A5DABDFB6504D6F3B BD7D9343B56F9BDDA1ADEED63DF47E359D7EF55EC1DF9FAA89FAF5EF7FABBED3 5D75B7C521BE6D75D1EB728EA55F4D665793AB9FC1DBEFE01DBDFAE9643A9B5C CDE0D59F75DB55CD76AEA697D3CBABAFE1CDEFAA250E3757FF0D0F4AFDE2FBEF D4C5D3D3D365B383D7CDBE5DEACBA6BDCB6A53ADCB6EBB72621F2E77EBDD5B6A F5EF9D5645AF0E505F354F5B059378B88492FF81FFFF75DFEE1A1AE05FF456B7 45AD76E68D428C2042D453D5AFD5A6D81ED40AE6B16F75A7564DABF650A7DA2A C0EA2576F487A6F760FE695D411D40AC827F8BC7A2AA8BDB1A619853F1BAEF77 F32CC379AC370F25E1E9B27CC85A7DDBD499A74426690218D39B4615DB52F5BA EB9705CCF787765E428FAEF36609DDB59A7509B3DB140FBA6C965738E30DE0F0 F3A7D03FF79FD972DD6FEACF6B3A013AF44D7BF8FCC17D0F1608A4F4AFCDAB39 2D2EA5BED740F71FDC717E969F9D75FDA1AE3EE96C53C02A6BA97F6AB0AA740D CBE7177FFC4FC38C66E00EAAC25279BE9AD2A32EEFE01109465FD5C294CF9E67 4AAF567A098BF4BCBAD58FB08C974D8DABFBEA27B0CACCFF3975317C4FAF57CD 161A6F9BADA6C75DD1167620FCAA164F6DB1BB99AB555177DA74B4AA8BBB0E10 4480ABBEB8BDD5A56A352C9EAD6AB693FD76D52CF71D30D86EDFDB165AD7B6D3 65FF3CE9F5739FE9B2EAD56D050C6F26ACA08BB22D9E60A8FD7609BD830853C5 B257B05E735745A96AA58ABA560B605FAA714ED3EAD46DDD2C1F6EE85D665EE5 2AB452AC60AE76D5F281D7343DFDF337808B65514FF0C9B7CCCFE4BFA5EE09DB 1E614A358FBA152FF4F6AE4072451301EA6C7B3695FA71AEEE748F2B713537F0 00CAA1CE24FEAECED9836FDF01BB2CD7D4359F69895290BF008080AF80E5F45F F6456D70A4904DBFCCC284D562BFAD51DA1375F2A8036413A069B66D26DDBA79 A22EF2A886E9710980027A9AD5AAD3D05733A117664C4240668AA2C6BE57F69E 0F80388E60024ED836FDC44E8A8DFEF2E083D999BE6C276B40834145D7172D54 4EBE9EF321635CA841577A5B8A16F3410BF56AA4A5D12611C7BF3FE843CC18B0 0889E06C2CCF93C478C05DA0B463C41BA0B0BF8FEA1FDEFCD7E4CD00A382E506 70C3B2D7CFC089B8FC138CBFD5C349E1A78695D5AF6F469AC420E05250AB0C06 A9BAF584A69A980DA068A42148B663AD4E40B9141E3962D5C93F75DEE97A7566 CA480FE0DB97D48011D62448F1AB9FB2D50633608802D6BF91460A358EB6DF0B 6043B0BDCE6BBDEA9D2AB8AD8BE5C35900F015B29F8404BDE95B90726801BA8A F4B62DB64102EE8AAA9D29FC7BA5B21A458E6AD50EDE5C0539B39BC1C8801F56 D7955C214CCF891298C2128C54B5707846CB0D2ACE32EC1BFECC184F60D901CB AEB0EC0ACB2A3024617DF7601201404537C1CEA18383A79B6A197A56FBBAB654 62EA72ABD9235374F8D50F6F5404D307CD63EB50B10245404A4EFD78AA019628 8EC019BB7ECC108AC952FB59505F7E56AC2E6958D7A06D9EEC0AC065411A3B51 543FF22500E353E78139A2D579ADBE47D1D5374DADFA6A37B27AAEDD572B4D08 AEF056D124091898172C0754E1386C59F4C5B5AA9BED1DC8DD667FB70E6D72D1 DEB49D23136BFE7E25D06DBB9F87CE79DD6BB009EAE26015231FC9F58EEC11DB 11A336422FF9023919987355FEC8EC615029308116D404451108F5B0922D4780 83B440003FAA733230ECF7A2063F039E39F1A5FDB4906C01E3A04C4517E48678 6C5D959AC46CCC3F25C377C43DB05AE67EBD9EA64B18ED784F82B194F80029B2 6E5DAD62AD4BA800C0417E832538D934A5464C6CF6755FA991F7B852621BC154 45C64A16401FBA9C80A4DE6FA4EA19DA66D0C0CA5B12876047EF61C2D63C8C20 1AEA3A03F86258A01C8A13255285C31868DA7878BBC41CC6E76B3E40D3C1FB7C 685779741E8518A47B18EEE8349C49319842B2769A8992A0C6CF4EC1396265B3 F0F52AAE6B55DE297523E22FE2719D92EC1A5075963586988BA8899527F48E34 E66BAA5F0DAAC7101DD3D2BC0E703294B6465727C8C1D5B7C302D3E202AE6C7E 1892ECB845878A0298B2AB6EABBAEA0FA0AB9461695528E43F80B05096B76F01 BFBAEFABED9D0279026F9BB6ACB6A0F8B0D527DD36D09769F5D4ECC1695E178F 9A6A3E633B57E9BDEAF6E8D07560F0FD945AD8EEA3468774A30FCF5368D46378 CA34D075B5B15040ABAD06371DF1EA56E135859B6E8B4EDB89811EEF804F6A5D 1FC09167B3E8A82621B8734380BAC3316E01E5A0C43072A61E4D84EFF2F2122A B560FA57184E6BF5535B010CCD8AC020610CCF6D417643DBEC016DB1A9FF1253 0FC58BF5EA8792084898CD86CD9BBA9C5844CC5F12C5B1F21F679A11971C3FC6 2D77961559FC43AB8ABF068B2A9EB6D3B6A11F7C33D2575C34E82F02DD982FE9 6234188CC2DFEFD4A2450F83D011A935F20CFBAC6CF6A0DE27CB1A293270DA53 96C6005B29854CE4307D53D77C76290FCE2162A40914BD20A89C99B42A85D992 1BF50FF81AD82D273A82F41759095C3BE32E2146A7DC1B442E9EAB5FFDFCDB5F 7277705D8238873F93DB7E6BBEAC2A30DFFDD3B269B7DA96D65D4F7F262B0C2F 764B68A94BEEBB18A7F1C314BD4B034659B5FD01FD38E8D41AC5F89A50C71A8E 0423A7CF53E759CE7E32BDB4FFC7F14933F96B0A21BC574D59BEB7D2E73D88A2 E5C31D08846D6986A5E01C389B2C64A9AEAEE0DF0FF0FFD554CDBE867FA730CA D7533EA20877960D88A08535F3705E18B76C3BEB037760A417E1C1B4EC76C512 24F9C4F600D2AE2D6D0D2A47D181C47322C60AA9C42B60C06AA9DDD3B2814712 B27327D1B02B29AF4C90883FAF7551A2DFE69E3D19B0F15355F66B78E9FEDD3D FB57A21E460BE09DFD2714451EA12FE80F3BCD2B3A478EFBF8655BDCF1E755D5 33A601866BEA1ABA2778E6C8E293B536FB49369A0153F2D0AE2786E9A69C04C4 533C9EE011ED31081A0698CF2B8181562013C03D300EB66B99C38F9C402B9A55 5C4FCC3C78BD60557828E815DAD748DAA25B824B087FB60DBEB735820D8E753A 985DADADED8D7641B06B0DB083568E1179479E0CBE5F7AB0668A2D78B4BEA7AF 87EB1B57406818E60012C01802AE529899E5830AC39FBD9E60012337185DDB3E 3069C096779E85C6F55F3CA64D1D74970DC5AB12D06B14B7571782DB802ACDD3 0D0301390CB51217713D182E456BF8DF8AACEED0F57A93B9A2CC44417EFC685A B1240368821EBE67946AB323BC19B7DFB8B6CDED3DE0FC8BDC3976A6A67E3635 8D00B390D1046CFDC00F2BD68C948799C06351EFB5FD3E658BC58DDE7DCA9DE8 EE3EA9FD0EA071513EC26D2C3C0DC2A109AD0858CCADCE1764368096C9D9F085 E5A028463457914982032455B30B8E0F43B7731545652DB1A901ED019168BE71 B063549F74387ED19B1DA837921F14EE777B466635C05B9884D54EDBE2B1BA33 162A228BA4F699957250DBF0AE208D0B789B31409881890B4BA5459F60E1D97D C6E2210497F351C3CA083ECB2CC4210DF2199D77AD7E044FE74E7350E8253C67 DD43B523CA077E60CD8C394B3CC06AD21F1B7B22D670ABE1D3DC4D2E5426BEC9 171EC2E45CFC4C8FE286194CE8F3CE5C733581A1830565D5368260FC4EE674BA 165B7D87DE4E34E724FEB6A0EFFE8E118181021B9948CCF39DC40C6B6807409F F8E638AE60C94B039B0CA15091DBB1B2E2098460E025CDE0117AC4FCEC8834CE CFA4B35F5A8C0CC085503C6C62FEFDA9241A92274D9131B40A1C48CF00DDF63E E84A3133E79378960739B64293F9668C18E8AE582CF1419CA524442522E5C637 74C2D020011110C942B22C3986AC85967AF7E68D159F5444115CBB3B445B1F45 DF23CA7153268496785CA971E6B9EB5CA24720250CE2E722DA5AE3CDC7C5C40B D0D467D7185AA76885896C204B6816F2C0221E1B6A566AB9060B887044639890 09E86B1B13DAB8584EB7D3CB6A552DCF184060FE16308752AF0A30032795AAD4 93B23C073A1BDEA9BB8961B8208E1273F17112CBC481A45110DDF536F76C0C64 DA156D87012BC7ED92F7597E83EFDF75A3BE5153B138AE0943B4606DD8E7B6EA 3168B5D9C36AB8B586EFAEAE2815E9F6A0BE668DCD8C9D0B65FE1502C8E3C95A 74A6EF2FD48587E79DFA1AD44778CE32F5F5DBB7AC9327675B9ACF7EDB5771C0 C186172DFE9F4419347F8241645CE909D0E0464C8A3FC4CB1AD882B2AF688582 58DB217291BD040676BAE8D5BDA74E620FC42DAE0D2C422ACFE6F7A2821FCB32 5E0992B332CB371ECF8FB8DD6F025117B80B43CBD070C05D36C772B76D5A61ED 28A4042D2AF5D1E16C9065E11C38CFE7D76A59EBA28546CB624F296FEA767FF7 45DC8E057A39D72B3961117A11159964B6EBC46B22F9C28A3C63E1C29C6F982F 61340FD9E4B45A2B34B0D9AE2D17246CFD71A33320049D7A87F42A22339FEE22 57D508AC8AC61F59F9CCED65A30201B878E5110BDEC2D77F990F39AC088EDF61 B01C99CD2B68EEDD767833B40622D43DF72D98F699A1309F8EF74D69E311FC6B 0E08D62033C180F121AD8C7DAD0CEC608DE22EAEAD3EA82B8FEF0017D79CC0DD DA7182D0CDE958C3D0B33E3781015BEAF9CAC9F5A6D5C572AD7618D1B3992DB0 AE761939A038771B0004D599B9E01A59330EA1C695F3DD4596965D18C69F264E 76695D47795918440CF56E071577454DFC30693BE5AED4FAF061DAF9822FC7C0 AB2BD1E7C0D4A0CDFE68044669320DA6ECC549B60AB7DB22B2707EADB6A8E131 C6C22C9484C0712D6C7A99CB9E5B10FF2C5B698EF513670C58F73C9A550A7801 35233B6754D7AB60D5CFB0DC1203B0488065A5AECE07A66A6C63FB67261E1306 32DFB5065A9F47B2292E268BE27829C622BC07C2B229B8B91D7AA8A5B01C1AD6 BE2AF11ABA6C5D0D56C74C0DCA2ED0B18542E3BB7917E62DD4F60F9CF1FD4BF5 CF293770C0E7173878D4390BFFA4C761E2D7B063C213B125B19C5BB61925EBDE B1E132EBBD8761596D13B2B5A222D40061163A584CD9EAC7C964EA8255655378 EB40A7D5C40260D65F212DD56AE061FD888934D269B983395525F33E141E0950 6BD5AACCA833B1EC2CD020F25ACA44B6CF6BDC47F05E86CC51764ACB7A23CE2D C128412E144988B1D9B622BE27CC1EAB149652693371298D060B028B88A9055A F630F7A706B733845AE425C6BB37BEC052A62E79E57E6EF7334838F019216ECD EEA009AA50DA6E46CE13A2296B8B27836DCC1DED5C90A562C94D481943211548 83EDA1692457AA32BDCCC7177F3ED45B0407C70560DFBA15946492D35E3BBC34 FBCBC45A3030BCC817FC4D6A870FE506470771B3C2A473B4C9B7C5C687992AB5 020F9E6121E82E2C988B7C2CA9908D491645336C151C49088B39EDB954E5B303 E0251F0864E0C0CF21B51C99E88620B89EB9A5684CBE2A9B2FCDA2476A8477C3 DDEDDAD98DACC947DE8B410646FC4FDA755DFC75A7E7E7C418213929371356EF B55389498BCD5934ABB2769E5B81F9629874189628EEF839A6C4EFE873F4FA4E B75F2894015FE4E935FB43576BE40E792916BDE7ABD487851856088B769E045D 550A0C5A0CE14C6E949BDE88D783C539E1E0F8663D0E47C2D4DA39E0B3B6434B 874D84362CC54C705B1C98ADB8055F6B7F4B4E8E15B62673890285F495B58141 61C8A26D8B0328A7AAAF800005A6440D5ECD22638D32BF5CF6D884F1F8C0EB34 C30B0C3798028E683610D87E600D5E415738BC7F31A317720551CBC0EBAC7168 C63C2E33865732DC9F357FF7DBEA2F6006D02AF26143E7DC1A86054C7B12F91C 3026EBB0EA541A089B625BEDF6F56093E7FA69ADB78ACE7850ECAE51C56EA781 7188B2A815BAF783F01625B35486951FB6CDD3F6CC420EFA85897F162EC06966 A5D63B91836AB7A4C82B256EA56F4E397F2114987094A9DE95BACACD300C7B16 0B0E0AEC273FB222A8DC34C4D08C33508C056769E31345507C7187D26E72922D C2359F053A48152F70722660F84B436C332D2F78CCAC5E237D52BA32B299B266 5B9BE88430503B3C1185275C089F0BB40878166FBC8FC71640E5E271F0850F51 F4CE584889AB53064D1C476384907E2292C3BE49C84A41282BD88241CC082199 96A68A35CC04F19B353CDDE4F095E5DE882BECAE38AD2561240A52A38F4CE61B 2333C321D9B7AFA5D57124F9436DCC4B37091461DF44B96DEE0446B949309CA8 C0AD014F4C8BCD084B5F07B1834F82E79C7F4CFD10883E7DF57D96A6776ADE33 BF0181F1FD0B7243003E60CA48A362266BA57C01E60847E39106626E73F58A45 3304C546390C3C0696FF5DF00A160EB93D6525E91A8B1202D1F0FCE974E99B5D 208EEA37BBD74BC401950C70B188526C41052B07C70FA20EC6B72A61C41A0B3C 1897041561973F55800E99A1E2332D7E54996769312EF3E88899957C562187DC 1C2FAEB1D28D211A530586299DE3EF2403F504FC69867E993F5FCDA77C4E824F 47E660D394D3E7426C6ADA8277EA4D2A3735675AC5873C38BF28E3418B7E5C7B 1749A0FAA1813D8B229A381B5676CDCE819C50DB198B2F56CD8758E5CE92B7DD 68B7E36FBC729927E5162FB7070377894805338209E6E15249AC6CBEB6DD58AC 7020D85919559FCDFD1439432797A544BAD3DDE9840E11CF922E1057C3C1BDB3 21AA45225665F3F386F915CC8BE3397C30A0B75D9CCD5236A28AD844BC568F55 B707C8305B2D913CE113C8BD7EB65C8A94B2AC22AD2F80CDC714831B50BAD3E1 D9811F2ABF007320034793D21BD155C90E6FD597917AF2F5CD56A2A5CEB8AB18 9DAD10994700076E56A177679289F91BE33E1063924CB299D150946012B266A0 35E74F244A2C6C16836C5FE34F4B2843BFE65E0A02C2C12642FCA307C04238CD 4891A6CEE3B15302C41F6DB44103494D966EECBC94B2A76305FCB02CDBE37D91 3A7E5222E627D57B847D778C34428A3A1727829920F5960C3757CD5405C924B0 3C1CE597875DB9D52772C1CFAC4752E1166AE9F6932830ADB4DFD190F9DE5671 82392C93E473B99C43028CF062995D938A4A0524CACCFEA32EA5B29D9BDAB380 B854969E9066C22774E17E178E4FE47F380F8345FB7DB0AC6F2656F5A8325F9C FF61BFB9D56D8EAF29C45FE6A22711715C48F8C73D4F8FD95826C4E717068723 5C0F03B2C930B71F405662684874408E45FC7214027FC0A772795282BD727429 C4F18263F2C5C34B67278008E608050D7E82AA3A37572A78E7909F1C89B33B2C EF8D4AE3646249A8C73D4F3B10CF32E1D62C71E1EA98DC7701EE792296B452C9 A18CC8853629EEB25FAE69DF05A551A7F63B9EB04467A0CC150B8538CB6A8E25 893CB5E8FA0CFC981B1B245AF3D0353B50951AC31E02E015B99149679FAEA697 F67F26C5DB08B2D13354B3A367A8D8112A09F628C429AC9849B83C92085B5702 5D43608E4D6AB53201EB10DCC413294F32B8ACB7E6100521109F9ED6783E74D7 D487BB66AB3E3C7FA5FEE979F613359B3D7F95AB555DA50C49B29EC879227E08 D728C834109F2ECBF2A1C82202E0D5479EB1131FABA75A0EEFCE619269480381 8C394994D9C5D99E58CD62062FEC8A7788E8A8D08BCDA691CC16FB3B1235DCED C1E38011910677C888C32CEEC80B6344220FB3A246EE38210618BDD522CA2147 9D1BAE8148DE0171C269DAE86590586B963B45F45F0FB3A7D6E9E4293619BBD3 662E0A434C0A3E1AA49033C0792DC76E897A2E5B5F1CAA4A9E759747CC44963F 26C9B3D291E636E530D4CB1751C32102F00384B2E96A6EB91C3922363AF65866 9CFCA421102B3D7D2FD4E088821837003C02DDF124C3735CA06045C1431A4021 298EAEE3CF1A1F45C6622000C63016BF19EE47BB23E47FDDC5F312C5C74D5099 5278ECCE99780A8610A0EFB2E8CC71546F98DA673ED2F71F643A8AA9D9E4C853 3301D31355F20C88ADB248CC43E87D0B86BBFB6DECEE9FA3CA81DB0EFCD4F867 9810A1F9E75A129C978E98177F3BCD732A070EB8E35416F416BA4F0570A6BA5F 7B3380CE7EC7088A586F516F2F2DB6E309C8EE33B62CFEDF79946E327895C53F 9B82FD320D0C66E360AFE0A7976E6C93A41F879DB22729A849BD6058C408D373 7740DE2DC4936BD22D0E27A2C344E258524874619D2352946206D07B3FCC8769 B4BD65E03D2E6573BE89EE13F03EEB2ACA9D32EEB0BF92C0BF0F171760F254A2 D0A4BE7EA344BEEAF0AE04AC928CD971E20CA0727045E35A4F3631CA29033306 1043B9EB0D4400402C9191D06ADF4C4ABDAC36C06E33958DD44A98FCEC0208B2 3CA2595EAB6F8B7A89592FDA5CE3B36B9B9D6EEB43387333081E5852456E9585 CE15632C510A430F0914CD150B4985065FAA0B8A4C3FE359DF656BC2D4CF6F8F 2EAC231435DC98202815847B30CC55BFF0658C6AF69A8C74D4262294B95380DF 33C0AF2BCA8FB0AFA5D00849F9ED1E434AFA38893BE06F73A42FAE00A9249629 513A28798BDBB96D9039C3C4E4725B2AC83B3B82F4CDCC18F13D6AAE2B41BDB8 F400A545C977885C06B5BD5AC35D28E086CD17C2D4C5E1FDFE4614008B2383D3 7CE1A77AC8D5D9708E073F9DCC9D29E4B790940DB27F8D4A0A99071CB34D666E 88C753769EA3D59B45FE4699FFDE29275C07F3F2B340D9CE261140C0847D363F D9A078AE00A4F367F9D64CC46FC8FABE9E5F85D735CBE12FAB478CDE8BA5C8DA BD65180AB75BF20E180CD978378E73176BC72C74BD0B5394160FF3D4E4049A24 C2317ACB639F830D82F9405E244F1B470B3B0A05B3B80D5E0410401B179C815F 0601D3D4360648A58AFAEE3E01F6B8F81C9182A94E16AC5D1215DDA744A4173A 0B44C6432701F10B77330F7B3761B5638327488D49DDD85D9CBA41A3059EC174 EA76E2EC4DD8A484118A43B3EFB31E2F09F2E72EECA52E74EB187D93A7EB1B7F B9D2867ED801CCF5B6E93ABA4148AB29D83BA5BA70FC3475170B6587B75CC6BC 669F132F27F27288A784C76D4018B9D19EA5552C2E2E6C02E8297B9CEE8AF613 E04C812B6CC7AE557A1B351733879A5359211D3B245630141F1CEAF68047F09F 0740DD70B68700DCC8607593E0EC24E5DDD551C35DED3CA1B96D7A496C100CE8 CC7B4BDC21BE6284FE26DC3E4E3684B938CC98110BFA472C549A5DD43BBA71FC 22226134A4AFDECB532BC5FD8E81B8A82C360C643023664EB33C5DEFC6297039 391866AB31AEB7A42D6912F56C5FC6A43A27F66ED59038CF0941C96E28129917 D7E682CB0298A279C0AD6AB44F2F2F2F55853BFF7A0336F11A89D753B3ED3970 5B4919327D43BC62B1115C9F607132859B2538289260FCDE24016130D68F5E12 42671812DBBDB2B615D78B5C1C334EB88F99BBE8C8E510ECC51E073FABCE12C5 EC3484E1ED4EE281898867F5A6FC24DEC849445256F66A6D33F4E0F021BD662F A354D944C8E9874FEA07D969C61EB6C684E5D0F064EF1032470F112D9E7F317D 66ED7028BCE77006915BE6369BC25FD5FD6AF56674A6DBAC948A8D25D9BD4279 71C96D35725AFB845FA69189444E8C599E993ECFD2DE5A226BDB8C274639418B B2081D3F283E32197F4011955F42FBE6128A1CAF47F7911017FFD62C91468F0B EC452E486DE4452C5CBC0B228ED80E73F030C9F0661E3CC79642328B9C4B562B F5C55D96B1D2C45B356FF8A1DA4EE3B593FEB64B71F7A5B703F91D98D28CBB56 DD7EB7031292D4A72546DCCFC0323CCE226546B2D958D9D25DD5A11E14B8E094 CE4363A121B8B037ED90187FB257148338DF14DBE24E2BF30B01F67783FCE599 78B930ABBBA4DF1FB387663BB56A9B0D9DD1F189BAAD06389650B969F15663BA D5A7061FC2A30E80999FE4118CA47C053E3672F646258E69870D032C7A27CE88 F35F352247801F8B26D78588CD6FB35E3A7D11AD5B42F05C3DC8BB0F5E6FF98C 5AB9C376E3762E7422564454780F168C1328713BE62D5F18A4E0C1FA2FA1C0A5 324E822D1F3576D9825E8724F6706C9D207EEF990A20240EB7B42C6E0D0FBF53 F7CE1B7A0790A406F04702FD28C221B3275B1D77A1F598DC3FBDF7A6D4C0FA1F D47E69F7EF5E0A9141FBA389E1AC9A4D114F148D5DDD6C1B0E5978ACA64D74FD F88A3628553EAA9897CCE7B4DD539BB23E3EAF0430C7414D6C8D8674F4D12EED 4C928D6D8EFCAB710F7DFE60AAA4CE469F363A8DF31934B5908FD2153FE96D79 FCF09BF1E4EF0354AF8021DDFFA91CC54E2AFCBDD2ECF55CFD778C7190A2FCCA 6E7B4D8379F0E64896A92B3CF91F374F8A6D160F39A60A53E2DB0B5CFB0554D8 F4F2672F4869BC77AD2EEE68F01BB09C283534D13968F987C1F505665493E87C 0AC2EF333A6D3156683610EF33F6E357DE547A318DD97CA2E3E02966F309E90F 6A48CFD13EC7BA1B76319ADC556DC0D0BCB1F31C51BF6E83DB7A4A36AF31C9A1 836447FEE9613D74B4F3767171EF3C2833B2D9AD51C0947114CF7D08507561AA A72B9D2A901C51937958496CDF67C90C8DF039F2334A7CCC6491BB5A3E59C842 5847BAB8E8FD5D4DF16F28DE7BF9E3EDB5D9576FD1C41E41B45BDDE11ECCD181 D3F61A4D96FD289E885F0C71028BABC1DB9B306376873F37190FACF026E40FEA CDE5E5E59B14100B391CFBB13E51EDA8CC89B236AC91CD2FED272BD7DADBEA5D D499F77F702D8B3B9D7C9B68574FB91F8CE5E1597E5AC216B3E89559E3E9081E 17E52C5469F60747829532403A7AC82058FDEC22753301EB52F1CB9AA36E5E19 92F1B93FE4F2BA5CBB74842699DB4BC4A02C908BE46F05BF9C5A2E458BDB90F3 FB08C6F989B6120C1B5F444C11F8D89D3B21A12D31342697AD128ECE77708259 95915E83E18053BE8882F212744A328B21E730F0C4F1D9689C2A0505CCBAAEEC A55A3CCF7CC10B6437831D0409ACA1E345FAE7CC1C835AE039ACA3BC1D4F182D 45BA50BAEAFD36722EB614019F21203A9029B266483B381C9954AB6BF3FBF12E B7342D1106EE7733769BDDF806D6F1A5E6CE738CAC37897C374498E2EB174E1E 4FC9F49998D25D7DD8213D8A5ADBCBFEE4DE3A0703E4E387A86151DEEFBB7E98 BF11FF480BDA1FE2AA2096397218141EC766480A8DED26F6D3994E86452AF214 E41DC926FD31456044F590E6324279D795E49DA459586CC12D04AD547438E1A8 707848067FE8C8FEEF8ECA446D2EA6CF138C91316E01A6E22C10DB8D17B3E7E9 EB1AE0B190573598BC6E88B1132DFEABE334BF5DE95E881A2E62166568BC2EC3 5D1E3C58477B73E62F9A16738CDBDA63ABE6ADB928D59FEB8661CDB924CBFA89 7B1DF88657E2AE3FEA810F3308FCBB94499F3EC92BF39B56E2B38FB875257E31 96D241F19761DCC656986F7E969FFD1F598CCF767B840000 } ; The following conditional evaluation checks to see if a ; database file exists. If not, it creates a file with ; some empty blocks: if not exists? %database.db [write %database.db {[][]}] ; Now the stored data is read into a variable word: database: load %database.db ; Here's the guts of the program. Be sure to read the ; list-view documentation to see how the widget works. 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 ] ] =image rebol_tutorial/listview_database_a.png Here's a stripped down version that's completely functional (only 18 lines of code!). It runs the list-view module from an external file on the hard drive. If the module doesn't exist on the hard drive, it's downloaded directly from the web server (instead of embedding it in the code, as above). So, if the list-view module is not distributed with this script, an available Internet connection is required to run it: Rebol [] 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 [ 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" [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" [save %database.db theview/data] ] In both programs above, clicking on a column header sorts the data by the selected column, ascending or descending. Clicking the diamond in the upper right hand corner returns the data to its unsorted order. Selecting a row of data with the mouse allows each cell to be edited directly in the listview. Because inline editing is possible, no additional GUI widgets are required for data input/output. For example, no text input fields are needed because all data is entered and displayed directly in the listview. That's a very powerful tool which is useful in a wide variety of situations. All of the display and data manipulation features in the example above are enabled by the imported list-view module. It makes programming database front ends really easy in Rebol. You cou use the outline above to create a graphical front end for any type of Rebol database application. You could, for example, make some minor alterations in the column headings to manage the contents of the database used in the earlier looping example. In the same way, it's a great tool to begin designing tables and inserting data into any newly conceived database application. For more information about dealing with lists of data using Rebol's raw tools, see the pages below: http://compkarori.com/vanilla/display/VID+list+style http://compkarori.com/vanilla/display/list+examples Also, be sure to see the links below for examples of how to organize and manipulate data using the native features of Rebol, and some useful third-party modules: http://www.rebol.net/cookbook/recipes/0012.html http://www.dobeash.com/it/rebdb/ http://softinnov.org/rebol/mysql.shtml There are more links to database tools on the rebDB page. Understanding and being able to use such database tools is an important part of creating modern applications of all types. --- Peer-to-Peer Instant Messenger This example allows two users to connect directly via a TCP/IP network port (a user selected IP address and port setting) to exchange messages. Unlike the FTP Chat Room above, this example requires no third party web server to store or transfer data. Text is sent directly between two computers, across an established network socket connection (on either the Internet or a local network). The application can act as either client or server, depending on the user's selection. To work properly, the server machine needs to have an exposed IP address or an open router/firewall port. The client machine can be located behind a router or firewall, without any forwarded incoming ports. Program operation can be demonstrated on a single computer. For instructions, see the help documentation included in the code. The underlying code in this example is based on the Rebol cookbook example at http://www.rebol.net/cookbook/recipes/0028.html. See that page for a detailed explanation of how Rebol opens, connects, and sends data across TCP/IP ports. For information about transferring binary files and data directly across a peer-to-peer connection, see http://www.rebol.net/cookbook/recipes/0058.html. The examples there provide a simple model for adding file sharing abilities to your network applications. The techniques demonstrated in the Rebol networking examples form the basis for building non-native protocols, and can be used to send data through serial ports, to control external hardware devices, etc. For basic information about how to configure routers and network ports for use in this example, see http://portforward.com. That type of configuration is beyond the scope of this tutorial, but is required for every sort of peer-to-peer ("p2p") network application - file sharing, multiuser online games, web cam sharing, instant messaging, web serving, etc. For an even more fundamental explanation about how networks work, and for information about how to configure a typical network setup in MS Windows, see http://com-pute.com/FreeTutorials/Other/NetworkBasics.html. REBOL [Title: "Peer-to-Peer Instant Messenger"] connected: false ; This is a flag variable, used to mark whether or not the ; two machines have already connected. It helps to more ; gracefully handle connection and shutdown actions throughout ; the script. ; The code below traps the close button (just a variation of ; the routine used in the previous database example). It ; assures that all open ports are closed, and sends a message ; to the remote machine that the connection has been terminated. ; Notice that the lines in the disconnect message are sent ; in reverse order. When they're received by the other machine, ; they're printed out one at a time, each line on top of the ; previous - so it appears correctly when viewed on the other ; side. insert-event-func closedown: func [face event] [ either event/type = 'close [ if connected [ insert port trim { ************************************************* AND RECONNECT. YOU MUST RESTART THE APPLICATION TO CONTINUE WITH ANOTHER CHAT, THE REMOTE PARTY HAS DISCONNECTED. ************************************************* } close port if mode/text = "Server Mode" [close listen] ] quit ] [event] ] view/new center-face gui: layout [ across at 5x2 ; this code positions the following items in the GUI ; The text below appears as a menu option in the upper ; left hand corner of the GUI. When it's clicked, the ; text contained in the "display" area is saved to a ; user selected file. text bold "Save Chat" [ filename: to-file request-file/title/file/save trim { Save file as:} "Save" %/c/chat.txt write filename display/text ] ; The text below is another menu option. It displays ; the user's IP address when clicked. It relies on a ; public web server to find the external address ; (whatsmyip.org). The "parse" command is used to ; extract the IP address from the page. Parsing is ; covered in a separate dedicated section later in ; the tutorial. text bold "Lookup IP" [ parse read http://whatsmyip.org/ [ thru copy my-ip to ] parse my-ip [ thru "Your IP is " copy stripped-ip to end ] alert to-string rejoin [ "External: " trim/all stripped-ip " " "Internal: " read join dns:// read dns:// ] ] ; The text below is a third menu option. It displays ; the help text when clicked. text bold "Help" [ alert { Enter the IP address and port number in the fields provided. If you will listen for others to call you, use the rotary button to select "Server Mode" (you must have an exposed IP address and/or an open port to accept an incoming chat). Select "Client Mode" if you will connect to another's chat server (you can do that even if you're behind an unconfigured firewall, router, etc.). Click "Connect" to begin the chat. To test the application on one machine, open two instances of the chat application, leave the IP set to "localhost" on both. Set one instance to run as server, and the other as client, then click connect. You can edit the chat text directly in the display area, and you can save the text to a local file. } ] return ; Below are the widgets used to enter connection info. ; Notice the labels assigned to each item. Later, the ; text contained in these widgets is referred to as ;