wxserpent = serpent + wxWidgets
    Roger B. Dannenberg
    Introduction
    Serpent has no built-in graphics or user interface classes, but
      it has been interfaced to parts of the wxWidgets library. Unlike
      wxPython, wxserpent is not intended to include all of wxWidgets,
      so there is only a very limited subset available. However, this is
      enough to construct simple but effective graphical interfaces. For
      example, wxserpent was used to create a graphical editor for audio
      synthesis algorithms, including pop-up help text, icons with
      hot-spots, icons that drag, lines that snap into place, menus, and
      file browsers.
    This document is likely to be incomplete, so you are encouraged
      to read the sources for details, constants, and recent additions.
    When you are getting started, you should run wxserpent in the serpent/wxs
      directory. The init.srp file here will then be loaded
      automatically at startup and create a little window where you can
      type text and have it evaluated by Serpent. You can even type
    
      load "filename"
    
    to load a file. You can of course read init.srp and
      modify it or install your own version here or in another
      directory. Serpent will read from the current directory first, and
      then use the search path.
    Two-level Implementation
    Level 1
    wxserpent interfaces to wxWidgets through a fairly small set of C
      functions. Because wxWidget objects are not primitives in serpent
      (like files for example), the wxWidget objects are accessed via
      "handles" that are simply integers. For example, a window might be
      designated by the integer 1, and a menu might be named by integer
      27. As you can imagine, the C interface code has a simple table
      that maps integers to wxWindows objects and then calls methods on
      these objects.
    This is the low-level, direct interface to wxWindows, and you can
      use it if you like, but one of the problems is that when wxWindows
      events occur (e.g. the user selects a menu item), a single serpent
      method, wxs_handler, is called to handle the event. It is up to
      this function to figure out which graphical object, based on the
      integer identifier, generated the event, and what to do about it.
    Level 2
    The standard way to impose some structure on a user interface
      system like this is to let objects represent things like menus,
      windows, sliders, number boxes, etc. In this scheme, the integer
      handles are hidden, and serpent maintains a table to map integers,
      not to wxWidget objects, but to Serpent objects that serve as
      representations of the graphical objects.
    When a menu item is selected, the wxs_handler method finds the
      corresponding Serpent object and invokes the handle() method.
      Thus, if you use this level, implemented in wxslib/wxserpent.srp,
      creating a graphical object is just a matter of instantiating a
      class.
    Example
    Let's go through a simple example. Suppose you want a slider
      control. Your serpent program should say something like:
    // parameters are: parent, min, max, initial, x, y, w, h
myslider = Slider(default_window.id, 0, 100, 50, 10, 30, 200, 20)
    The first parameter tells where to put the slider: this must be
      the integer "handle" for a window. When wxserpent initializes,
      there is a default window created automatically, and serpent
      creates a corresponding sepent object called default_window.
      The id field contains the integer handle. The next
      parameters specify the minimum, maximum, and initial values for
      the slider. The last four parameters are coordinates: left, top,
      width, and height. Coordinates are in integer pixels measured from
      the upper left corner of the window.
    Now we have a slider, and you should see a slider in the window.
      How do we get values from the slider? One way is to start over and
      make a subclass of Slider where we override the handle
      method, but this is so common, there's an easier way. Let's look
      at some code to get a thorough understanding. Here's wxs_handler:
    def wxs_handler(id, event, x, y):
    var obj = control_map[id]
    if obj:
        if event == WXS_PAINT:
            obj.paint(x)
        else:
            obj.handle(event, x, y)
    When an event occurs (e.g. when the slider button is moved),
      wxs_handler is called with the number of the slider, a code
      indicating the type of event, and the value of the slider as x.
      (The y parameter is not used in slider update events.) The
      wxs_handler function maps id to a Serpent object. If the event is
      a paint request, which is valid for some objects, the paint
      message is sent to the object. Otherwise, the parameters are
      forwarded to the handle method of the object.
    Most objects, including Sliders, inherit the handle method from
      their superclass, Control. Here is a simplified version of the
      method:
        def handle(event, x, y):
        if method:
            if target:
                send(target, method, event, x, y)
            else:
                funcall(method, obj, event, x, y)
        elif parent:
            control_map[parent].handle(event, x, y)
    Every Control has two fields used for message handling: method
      and target. If method is set, then this method will be
      called to handle the event. If target is set to a Serpent object,
      then method will be sent to this target. If target is not set,
      then method is regarded as a global function, and the function is
      called. Note that the function's first parameter is the object
      (the control that gave rise to the event). If method is not set,
      there is no handler defined for this object, so the event is sent
      to the parent of the object. In the case of our slider, recall
      that the parent is the window containing the slider. This
      forward-to-parent mechanism could be used if a parent has many
      controls as children and wants to handle all of them in one place
      (although in practice, it is simple to direct each child object's
      events to a designated target.) 
    OK, so now we're ready to handle some slider events. Try this:
    myslider.method = 'print_slider_value'
def print_slider_value(obj, event, x, y):
    display "print_slider_value", obj, event, x, y
    Notice that this is a global function, not a method. It will be
      called because myslider.target is nil (the default). Also notice
      that the handler gets called with 4 parameters even though the y
      parameter will never be used. When you move the slider, you should
      see text that tells you the value.
    
    In some cases, especially menus, you might want to have different
      targets so that one object or function can handle some menu items
      and another object or function can handle others. For example,
      different subsystems might create help topics on the Help menu. It
      wouldn't make much sense to have a single handler for Help. See add_target_method()
      below for a solution.
    
    You now know just about everything you need to know except for
      the details about what objects you can create and how to create a timer. These are described in the
      next section. You should also be warned about error-handling. It's
      not pretty. See below.
    
    Default Handlers
    Normally, you will install handlers for every object that
      produces events you want to handle. If there is no handler, events
      propagate to containing windows (either panels or top-level
      windows), so there is a chance to catch events there as well. If
      you do not provide any handler, there may be a built-in handler,
      for example, for the File:Quit menu or window Close buttons.
    
    Unlike many graphical user interface frameworks, wxserpent
      assumes that if there is a handler to call, then the event is
      handled. There is no need to return true or false to indicate
      whether to propagate the event to the next available handler.
    
    There are a few special cases, e.g. what if you want to catch the
      File:Quit menu item and save a file or get the user to confirm,
      and then you want to invoke the built-in system handler to
      quit the application. Within the handler, you can tell the system
      "pretend like this handler never existed" by calling wxs_event_not_handled().
      See the On Quit/Close
      example for more details.
    
    Color
    Color is specified in 3 ways:
    
      - By string, e.g., "BLACK". See colors.txt
        and color_chart.png.
        Methods that take strings are
        set_color(), set_pen_color(), set_brush_color(),
        set_text_color(), and clear().
      
- By Wxs_color, e.g., Wxs_color(192, 196, 240).
        A simple Serpent object created
        byWxs_color(r, g, b, optional alpha = 255). AWxs_colorobject may be passed wherever a string
        color could be used.
- By RGBA, where red, green, blue, and optional alpha values
        are integers from 0 through 255. Methods that take RGBA
        parameters are set_rgb(), set_pen_rgb(), set_brush_rgb(),
        set_text_rgb(), and clear_rgb().
      
Graphical Objects
    Graphical objects are subclasses of the Control class. There are
      some general methods in Control that most objects can use:
    
      - add_target_method(target, method)
- This method adds a target and method to a list of handlers for
        the control. Normally, the target and method
        fields of a Control are set directly to an object (or nil) and a
        symbol denoting a function or method. However, the target
        and method fields can be arrays, in which case a call
        is made to method[0],  method[1],
        etc. add_target_method(tar, meth) is a convenient way
        to add a target/method pair to a control without removing or
        altering any existing target/method pairs. This method simply
        sets the target and method fields of the
        Control if there is no method. Otherwise, they create
        arrays if necessary and append to them.
 
- delete()
- Delete the wxWidgets control this object represents. You
        should not use a Control after calling delete().
- get_left()
- Get the left (x) position in pixels.
- get_top()
- Get the top (y) position in piexels.
- get_width()
- Get the width in pixels.
- get_height():
- Get the height in pixels.
- get_count():
- Get the number of items in the control (works only with
        Listbox, Combobox, Radiobox, and Choice.)
- set_size(width, height)
- Set the size in pixels.
- set_width(w)
- Set the width in pixels.
- set_height(h)
- Set the height in pixels.
- set_position(x, y)
- Set the position in pixels, relative to the upper left of the
        parent window.
- set_font(size, family, style, weight, underline, name)
- Set font parameters (for text objects only). Font size is in
        points. Font family is from the following constants:
        
          - WXS_FONT_DEFAULT - default font.
- WXS_FONT_DECORATIVE - a decorative font.
- WXS_FONT_ROMAN - a roman (serif) font.
- WXS_FONT_SCRIPT - a handwriting font.
- WXS_FONT_SWISS - a sans serif font.
- WXS_FONT_MODERN - a fixed pitch font.
 Font style is from the following:
          - WXS_FONT_NORMAL - plain.
- WXS_FONT_SLANT - see wxWidgets documentation.
- WXS_FONT_ITALIC - italic.
 Font weight is from the following:
          - WXS_FONT_NORMAL - normal weight.
- WXS_FONT_LIGHT - see wxWidgets documentation.
- WXS_FONT_BOLD - bold.
 The underline parameter is boolean. The name parameter is a
        string containing a font face name (see wxWidgets documentation
        for the wxFont class; the empty string is an acceptable value
        for the name parameter). In addition to these constants, the
        WXS_FONT_NORMAL is 0. For example, to change the font size to 24
        (large), you can use object.set_font(24, 0, 0, 0, nil, "").
- set_color(color)
- Set the color of an object. Colors are designated by strings,
        e.g. "RED", or an Wxs_color object.
        See colors.txt for a list
        of available colors and their interpretation. Also, see colorchart.png for a color chart
        (image).
- set_rgb(r, g, b)
- Set the color of an object using red/green/blue values from 0
        to 255.
 
- set_value(x)
- Set the value. The folowing take an integer value: Gauge,
        Spinctrl, Slider, Radiobox, and Checkbox. Checkbox takes 0 or false
        (unchecked), or true or 1 (checked). The following take a string value:
        Textctrl, Choice, Combobox, Listbox. In the case of Choice and
        Listbox, the string must match an existing choice or nothing
        happens.
- set_string(string)
- Set the value as a string if the object is a Textctrl,
        Multitext, Statictext, Combobox, Listbox, or Choice. Also
        sets the title of a Window.
      
- set_and_act(x)
- Works like set_value(), but calls the handler as if the user
        had entered the value through the user interface. Note that
        set_value() and set_string() do not invoke handlers; they merely
        update the visible representation on the screen.
- value()
- Get the value of this control (not actually defined in
        Control, but defined by most subclasses).
-  
Positioning and Sizing
    Most controls take pameters x, y,
    w, h to establish position and
    size. When creating control panels with many controls, it is
    convenient to use relative positioning rather than using absolute
    coordinates. Normally, x, y,
    w, h are integers, but some special
    symbols can be passed instead:
    
      - To align a control with the previously created control,
      either horizontally or vertically, pass
      'S'(Same) forxory;
- To position a control below the previously created control,
      pass 'S'(Same) forxand'D'(Down) as theyparameter;
- To position a control to the right of the previously created
      control, pass 'R'(Right) forxand'S'(Same) fory;
- To match either the width, height, or both, pass
      'S'forworhparameters;
- To specify the default control height, pass 'H'for thehparameter.
    
       | Code | Meaning | Applies To | 
      | 'S' | Same | xywh | 
      | 'D' | Down | y | 
      | 'R' | Right | x | 
      | 'H' | Default Height (25) | h | 
    
     
    In addition, the implementation is very simple and
    transparent. (See wxs_compute_coordinates() in
    wxslib/wxserpent.srp. Global variables wxs_x,
    wxs_y, wxs_w, and wxs_h,
    are set each time a control is created. These same variables are
    used to interpret 'S', 'D',
    'R', and 'H'. For 'R' and
    'D' an extra wxs_space (initially 5) is
    added. If you want 10 pixels of
    extra vertical 
    space when using 'D', you can simply precede the
    control instantiation with wxs_y = wxs_y + 10 or
    wxs_h = wxs_h + 10 and pass
    'D' for y, which will position relative
    to (the updated) wxs_y + wxs_h + wxs_space.
    
    Sometimes, you want to remember where the next control would go,
    but create the control later. You can save values of wxs_x or
    wxs_y. E.g., to create
    two rows of controls, create the first object, and then set
    var left = wxs_x. Create successive objects on the row
    using 'R' (right) for x and 'S'
    (same) for y. To align the second row below the first
    object, use left for x and 'D'
    (down) for y. (This will position the first object of
    the second row vertically according to the last object
    of the first row. If
    objects in the first row have different heights, you may need to
    compute the y based on the maximum height in the
    row.)
    
Classes to Create Controls
    Here is a list of graphical objects you can create (these are all
      subclasses of Control):
    
      - Button(parent, label, x, y, w, h)
- Creates a labeled button. label is a string. The
        handler will be called with event = WXS_BUTTON_CLICKED, and x
        and y should be ignored. To invoke the handler from software, call
        set_and_act() with no arguments.
- Choice(parent, x, y, w, h)
- Creates a choice box. Use the append(label) method to add
        choices (strings) and clear() to remove all labels from the
        list. See also set_value(). The handler will be called with
        event = WXS_CHOICE_SELECTED, x = the selected label (a string),
        and y should be ignored.
- Checkbox(parent, label, x, y, w, h)
- Similar to Button, but this creates a two-state check box. The
        handler will be called with event = WXS_CHECKBOX_CLICKED, x =
        't' (if the box is checked) or nil (if the box is not checked),
        and y should be ignored.
- Combobox(parent, x, y, w, h)
- Similar to a Choice, but you can type in choices as well as
        select them. Use append(label) to add choices and clear() to
        remove all choices. The handler will be called with event =
        WXS_COMBOBOX_SELECTED, x = the selected label (a string), and y
        should be ignored when an item is selected. When the user types into the
        combobox, the handler is called after each character is typed or
        deleted with event = WXS_TEXT_UPDATED, x = the current label (a
        string), and should be ignored.
- Listbox(parent, x, y, w, h, multiple)
- Creates a list box. Use the append(label) method to add to the
        list and clear() to remove all items from the list. The handler
        will be called with event = WXS_LISTBOX_SELECTED, x = the
        selected label (a string), and y should be ignored. If multiple (boolean) is
        set, the Listbox will allow multiple selections. To retrieve
        multiple selections, use the get_count() method to determine the
        size, and is_selected(n) to determine if the nth item is
        selected. The get_string(n) method can be used to get the nth
        string (whether it is selected or not). The method
        get_selections() will return an array of all selected strings. 
- Multitext(parent, x, y, w, h)
- Creates a multi-line editable text box. The value() method
        returns the text. The handler is called after each character is
        typed or deleted with event = WXS_TEXT_UPDATED, x = 0, and y
        should be ignored. Unlike most controls, the handler is called in response to
        set_value() or set_string() (which are equivalent) if the text
        changes. A call to set_and_act() will always call the handler
        and may call it twice. Use value() to retrieve the text.
- Notebook(parent, x, y, w, h)
- Creates a tabbed set of controls with associated tabs. To add
        a page to the notebook, first create a control to be the page, using
        the Notebook as the parent. If you want multiple controls
        on a page, use a Panel as the page and add controls to the
        Panel. Next, use add_page(control, label)
        to add the control to the notebook. The control will be resized
        by the Notebook object. If the actual size is needed, call get_width()
        and get_height() methods on the page control after
        calling add_page(). At present there are no events
        when the user selects a new page, but the Notebook methods value()
        and set_value(i) can be used to get and set the
        page number, which is 0-based.
- Textctrl(parent, text, x, y, w, h)
- Creates a one-line text box. The value() method returns the
        text. The handler works just like Multitext (see above).
- Spinctrl(parent, min, max, initial, x, y, w, h)
- Creates a spin control The handler is called with event =
        WXS_SPINCTRL_UPDATED, x = the spin control value, and y should
        be ignored.
- Statictext(parent, text, x, y, w, h)
- Creates non-editable text.
- Radiobox(parent, label, items, x, y, w, h)
- Creates a radio box, where label is a text label and items is
        an array of strings to label the buttons. The method value()
        returns an integer index (not a string),
        item_string(i) returns the string associated with
        index i, and set_value(i)
        takes an integer index. The handler is called with event =
        WXS_RADIOBOX_SELECTED, x = the integer index of the selected
        button, and y should be ignored.
- Panel(parent, x, y, w, h)
- Creates a panel, essentially a sub-window with its own local
        coordinate system.
- Slider(parent, min, max, initial, x, y, w, h, optional
        horizontal = t)
- Creates a slider. The handler is called with event =
        WXS_SLIDER_UPDATED, x = the slider value (an integer), and y
        should be ignored. The default is a horizontal slider, but if you specify false
        or nil for the optional parameter, a vertical slider is created.
        Sliders can be resized with set_size(), but at least on OS X, it
        does not seem possible to resize smaller than the initial size,
        so create the slider at the smallest size you might need and
        resize it to the size you want.
- Gauge(parent, range, x, y, w, h)
- Creates a gauge, used to display values or progress as a
        horizontal bar. Use set_value(x) to change the
        display. Values of range and x are integer.
- Menu(parent, label) - not recommended for direct use, see
          Window.get_menu method. 
- Add a menu with the given (string) label. The handler is
        called with event = WXS_MENU_SELECTED, x = the index of the
        selected menu item, and y = the checked status: -1 means this is
        not a checkable item, 0 means unchecked, and 1 means checked.
        The following methods are available:
        
          - item(label, help, [checkable, [target, method]])
- Add a menu item with the given (string) label, the
            associated help string, and the checkable attribute (nil or
            true, defaults to nil). Returns an integer that denotes the
            item. (This integer is the value of x when a handler is
            called with event = WXS_MENU_SELECTED.) Menu items including
            separators are normally numbered from zero, but there are
            exceptions such as the OS X Help menu being preloaded with a
            search box. Do not assume any particular assignment of
            these integer item identifiers. If
            target and method are specified and method is not nil (the
            default), then when the item is selected, target and method
            are used to handle the event and the menu target and method,
            if any, are ignored. Returns an id that can be
            used to call set_and_act or set_checked
 
- separator()
- Insert a separator (no item number is returned).
- is_checked(id)
- Return true iff the menu item is checked. The id parameter
            is the integer identifier returned by an item
            method described above. The checked status is also given as
            the y value of a WXS_MENU_SELECTED event, where y = 0 means
            unchecked and y = 1 means checked.
- set_checked(id, flag)
- Set the checked status of an item. 
          
- set_and_act(id, optional flag)
- Set the checked status of an item (if any) and simulate 
            the action of the user selecting this menu item. The first
            parameter, id, is the integer identifier of the menu item
            returned by the item method when the menu item is
            created; flag is the check state: true means set the check
            mark, false means remove the check mark, and no parameter
            means this is not a checkable item. The caller should
            provide the second parameter if and only if the item is
            checkable.
- item_string(i)
- Return the label string for this menu item. If the
            parameter i does not denote an item created with the item
            method, nil is returned. For example, here is how to get the
            label from a selected menu item:
            
              def file_menu_handler(obj, event, x, y):
    var selection = obj.item_string(x)
    print "You selected", selection
 
- delete(i)
- Delete the ith item, where i is
            the item number returned by the item method. Note
            that i is optional. If omitted or nil (false), the
            delete()method inherited from theControlclass will be called, which will
            delete the entire menu. Before deleting the menu, you
            should delete each item usingdelete(i);
            otherwise, the menu items may not be freed.
 
 By default, an "About..." menu item is created. When invoked,
        some version information for wxSerpent is displayed in a dialog
        box. You can set the string displayed in the dialog box,
        changing the default message, by calling:
          - wxs_set_about(msg)
- Set the string displayed in the About... dialog box.
 
- Image(optional w, h)
- Create an image object to be drawn on a canvas. (See the
        draw_image method of the Canvas class below.) Unlike other
        classes in this list, an image does not have a parent window.
        However, it does implement some of the generic methods of
        Control, including get_width, get_height, and delete. You can
        create an image in two ways: you can draw on an image similar to
        a canvas, or you can load an image from a file. 
 
 
- If you initialize with optional width and height parameterrs a
        blank bitmap is created that you can draw on. Drawing works
        like  a Canvas: you subclass Image and provide a custom
        paint(x) method, which can call any of the Canvas drawing
        methods. The paint method cannot be invoked directly. Instead,
        call refresh(t) on an instance of your Image subclass. This will
        immediately call paint (after setting up some internal context
        so that the draw commands will work). Once painted, there is no
        need to paint an image again, so images can be used to store the
        result of an expensive drawing, which can then be copied to a
        canvas in one fast draw_image() operation.
 
 
- If you do not
        initiaize with optional width and height (or pass in nil), you
        must call the create method to load an image from a file:
        
          - load_from(filename)
- Dispose of the previous image, if any, and load a new
            image from a file. Returns either WXS_IMAGE_NO_FILE,
            WXS_IMAGE_CANNOT_READ or WXS_IMAGE_SUCCESS. (This method
            could also return WXS_IMAGE_NO_ID if somehow the Image gets
            an invalid ID number, or WXS_IMAGE_BAD_ID if somehow the
            Image has the ID of a non-bitmap object.) After loading an
            image, you can draw the image on a canvas and query for the
            width or height.
- rescale(width, height, quality)
- Change the size of the image to width and height. The
            quality may be WXS_QUALITY_NORMAL or WXS_QUALITY_HIGH.
- subimage(left, top, width, height) 
- Create a new Image object that contains a subarea
            specified by left, top, width, and height. The subarea must
            be contained within image, otherwise WXS_IMAGE_BAD_SIZE is
            returned. The image must not have been drawn to an OpenGL
            window or canvas (a restriction in the current
            implementation), otherwise WXS_IMAGE_NO_DATA will be
            returned. If the operation is successful, the new Image
            object is returned. Currently, image and control identifiers
            are not recycled, so generating many subimages (e.g. calling
            subimage for every redraw) should be avoided. If you must,
            you can delete a control, window, or image using the
            delete() method. This will free up the allocated resources
            that implement the graphics, but not the corresponding
            Serpent object, so you should then be sure to destroy
            (overwrite) any existing references to the Serpent object,
            which will then be automatically garbage collected. The
            Serpent garbage collector does not perform any
            "finalization" actions, so merely destroying all references
            to the Serpent object without calling delete() first will
            leave all the graphics resources allocated.
 
 
- Canvas(parent, x, y, w, h)
- Glcanvas(parent, x, y, w, h)
- Glwindow(title, x, y, w, h)
 
- Create a canvas, a drawable region. There are three
        variations: Canvas uses a wxWindows wxCanvas object, which has
        better support for fonts and incremental updates, but otherwise
        is slower. Glcanvas is intended to be a direct replacement for
        Canvas using a wxGlCanvas object, which can be an order of
        magnitude faster than Canvas. Glwindow is like Window (see
        below), but with an embedded Glcanvas.
        Before we talk about drawing, note that canvases generate a
          variety of events. Mouse events are WXS_LEFT_DOWN,
          WXS_LEFT_UP, WXS_RIGHT_DOWN, WXS_RIGHT_UP,
          WXS_SHIFT_LEFT_DOWN, WXS_LEFT_DCLICK, WXS_RIGHT_DCLICK,
          WXS_SHIFT_RIGHT_DOWN, WXS_MOVE, WXS_ENTER_WINDOW, and
          WXS_LEAVE_WINDOW. Keyboard events are WXS_KEYDOWN, WXS_KEYUP,
          which report raw up/down key events without processing the
          shift key, etc. For text input, probably WXS_CHAR is the best
          event to look for because the x parameter will hold
          an ASCII character code rather than an unprocessed key code.  There are many methods, but first a word about how this all
          works. To create a Canvas (or Glcanvas or Glwindow), you must
          make a subclass and define the paint(x) method, which may call
          any of the following methods. Except for refresh, the
          following methods can only be called after a paint()
          method begins and before it ends. The affected canvas will be
          the one who's paint method is active. You cannot simply
            call draw_line(0, 0, 100, 100) and expect to see a line on
            your canvas. Instead, you must first call
          refresh(erase). At some point in the future, paint(x) will be
          called, and then you will have the opportunity to draw on your
          canvas.   Each Canvas has a full-size bitmap that contains the full
          canvas image. If the canvas is obscured or for some reason has
          to be redrawn, wxserpent will just copy bits from the bitmap.
          Drawing methods actually draw on the bitmap; then the bitmap
          is copied to the canvas. Although copying the entire bitmap to
          update the canvas can be a lot of work, the bitmap copy is
          performed by optimized machine code, so the overhead may not
          be as great as drawing in the first place. By keeping the
          bitmap around, wxserpent programs can do incremental updates
          to the bitmap, minimizing the amount of Serpent execution.
          This is only possible with a Canvas. Glcanvas and Glwinodow
          may use a buffer to avoid redrawing when the window is
          uncovered, but any updates to the image or resizing require a
          full redraw.
 
  To perform incremental updates, update the state of your
          Canvas subclass and call refresh(false). The parameter says
          not to erase the canvas. In the paint(x) method (which you
          define), if x is false, then refresh was called with false and
          paint(x) should perform an incremental update. On the other
          hand, if x is true, then either refresh(true) was called, the
          Canvas was resized, or some other event occurred that requires
          a full repaint of the entire canvas. In this case, the bitmap
          will be filled with the background color. Glcanvas and
          Glwindow also use the paint(x) method, but the parameter
          should be ignored and the full image should be drawn by the
          paint(x) method.
 
 Unlike most graphics functions described earlier, most of the
          "draw" functions below can take doubles as parameters. These
          are rounded to integers in the case of Canvas, but Glcanvas
          and Glwindow draw with antialiased lines and polygons, thus it
          is possible to use non-integer coordinates.    
          - draw_line(x, y, x2, y2)
- draw a line from x,y to x2,y2. All parameters are integer
            or double.
 
- draw_point(x, y)
- draw a 1-pixel point at x,y. All parameters are integer or
            double.
- draw_rectangle(x, y, w, h)
- draw a rectangle. All parameters are integer or double.
- draw_text(x, y, text)
- draw a string. x and y are integer or double coordinates
            for the left of the string baseline.
 
- draw_ellipse(x, y, w, h)
- draw an ellipse. All parameters are integer or double.
- draw_polygon(points)
- draw a closed polygon. Points are an array: [x0, y0, x1,
            y1, x2, y2, ...]. All values are integer or double.
- draw_image(image, optional x = 0, optional y = 0, optional
            xscale = 1, optional yscale = 1, optional angle = 0)
- draw an instance of Image at location (x, y) (integer or
            double) with scale factors (xscale, yscale) (integer or
            double) and rotated by angle (integer or double in radians,
            not degrees) on the canvas. Note that you pass in the
            Serpent Image object, not the integer id for the object.
            Canvas objects ignore scale factors and angle, but Glcanvas
            and Glwindow do not. In the current implementation, drawing
            an image to a Glcanvas or Glwindow converts the image to an
            OpenGL texture, after which other image operations will
            fail, including subimage(), rescale(), and drawing on an
            ordinary Canvas.
 
- set_pen_color(color, optional alpha = 255)
- color is a string color name (see set_color() above) or
            a Wxs_color object.
            Future lines, points, etc. are in this color. The alpha
            value is an integer or double from 0 (transparent) to 255
            (opaque, the default). For a Canvas, only transparent (zero)
            and opaque (non-zero) are supported.
- set_pen_rgb(r, g, b, optional alpha = 255)
- set the color using integer RGB values from 0 to 255. The
            alpha works as in set_pen_color(). All parameters
            are integer or double.
- set_pen_width(w)
- set the pen width for lines, rectangles, etc. w is integer
            or double.
- set_brush_color(color, optional alpha)
- set the color (a string or Wxs_color) with which to fill rectangles,
            ellipses, etc. See set_color() above. The alpha value is an
            integer or double from 0 (transparent) to 255 (opaque, the
            default). For a Canvas, only transparent (zero) and opaque
            (non-zero) are supported.
- set_brush_rgb(r, g, b, optional alpha = 255)
- specify the brush color with integer or double RGB values
            from 0 to 255 and alpha as described for set_brush_color().
- set_font(size, family, style, weight, underline, name)
- set the font that will be used by subsequent calls to
            draw_text(). See the description of set_font() under
            Graphical Objects, above.
 
- set_text_color(color, optional alpha = 255)
- set the color (a string or Wxs_color object)
            with which to draw text. The
            alpha value is an integer or double from 0 (transparent) to
            255 (opaque, the default). For a Canvas, only transparent
            (zero) and opaque (non-zero) are supported.
- set_text_rgb(r, g, b, optional alpha = 255)
 
- specify the text color with integer or double RGB values
            from 0 to 255 and alpha as described for set_text_color().
- clear(color)
- fill the entire canvas with a solid color
- clear_rgb(r, g, b)
- fill the entire canvas with a solid color
- refresh(erase)
- notify wxWidgets that a change has occurred requiring a
            graphical update. If erase is true, the canvas will be
            erased first.
 
- Scrolled(parent, x, y, w, h)
- Create a scrollable panel. The panel is initialized to the
        size of the container. The virtual (contained) panel size can be
        changed using the following methods. Scrollbars are
        automatically created when a dimension of the contained panel
        exceeds the dimensions of the container (set when the Scrolled
        is created and changeable with set_size).
          
          - set_virtualsize(w, h)
- set the size of the virtual scrollable panel.
- get_virtualwidth()
- get the width of the virtual scrollable panel.
- get_virtualheight()
- get the height of the virtual scrollable panel.
- scroll(x, y)
- Scroll to location x, y
 
- Scrolled_canvas(parent, x, y, w, h)
- Create a scrollable canvas. The canvas is initialized to the
        size of a containing window. The virtual canvas size can be
        changed using the following methods. Scrollbars are
        automatically created when a dimension of the contained canvas
        exceeds the dimensions of the container (set when the
        Scrolled_canvas is created and changeable with set_size). A
        scrolled canvas is a subclass of Canvas. See Canvas above for
        details about defining a subclass and overriding paint in order
        to draw on the scrollable canvas.
          
          - set_virtualsize(w, h)
- set the size of the virtual scrollable panel.
- get_virtualwidth()
- get the width of the virtual scrollable panel.
- get_virtualheight()
- get the height of the virtual scrollable panel.
- scroll(x, y)
- Scroll to location x, y
 
- Window(title, x, y, w, h)
- Creates a top-level window with title (a string) as title.
        When the window size changes, the on_size(x, y) method is
        called. Subclasses of Window can override on_size(), e.g. to
        grow or shrink objects in the window when the size changes.
        Windows have a couple of special methods:   
          - show_status(flag)
- Windows can display a status region. If flag is nil
            (false), do not show the status region. If flag is not nil,
            show the status region. (The current implementation creates
            and clears the status bar regardless of the flag value.)
 
- set_status(status)
- Display status (a string) in the window status region. You
            must have previously created a status bar using show_status
            for this method to have any effect. 
- set_fullscreen(flag)
- Set the window to be full screen (or not) depending on
            flag
- get_menu(label)
- Retrieve the Menu object with the given label for this
            window, or if no such menu exists, create and return Menu
            with that label.
 
 
          
 
 Windows receive some special events:  
          - WXS_SIZE_CHANGED
- The window size has changed.
- WXS_CLOSE_WINDOW
- The window is about to be closed and deleted. However, the
            wxSerpent object representing the window still exists.
            Calling methods of the wxSerpent Window object may do
            nothing or may cause wxSerpent to crash. Simple applications
            will create windows and controls during initialization and
            make no further changes. These applications can ignore
            WXS_CLOSE_WINDOW events, and need not even set the
- <tt>method</tt> of the Window or write a
            handler. Applications that store new windows in globals or
            that use default_window and create new controls or
            change controls or change the size or position of these
            windows should handle WXS_CLOSE_WINDOW. If the handler
            returns true (non-nil), the window will remain open; thus,
            the close button can be deactivated. Otherwise, the handler
            should actively set the stored window references to nil when
            the window closes. Code that updates the window in any way
            should first test if the window reference is nil, and if so,
            either the window can be regenerated or the operation must
            be skipped. 
 
Dialog Boxes for Requesting Input
    You can request the user to enter a file, a number, or text using
    dialog boxes.
    
      - wxs_file_selector(message, path, default, extension, wildcard,
        flags, window_id)
- Opens a dialog to browse for a file. The message is a string
        that is displayed in the dialog box to prompt the user for the
        file. The path is the initial path where the dialog box should
        look for files, where the empty string means the current path.
        The default is the default file name, where the empty string
        means no default file name. The extension specifies the file
        extension (also called the type extension) for default. The
        wildcard specifies what files are displayed in the file selector
        (use "*.*" to list all files). See also ::wxFileSelector in
        wxWidgets documentation for more wildcard syntax, which can
        specify multiple types of file. The flags is a combination of
        WXS_FILE_OPEN, WXS_FILE_SAVE, WXS_FILE_OVERWRITE_PROMPT,
        WXS_FILE_HIDE_READONLY, or WXS_FILE_MUST_EXIST. The window_id is
        the id for the window in which the dialog is opened. The
        result is either a path to the selected file, which does not
        necessarily exist (e.g. without WXS_FILE_MUST_EXIST), or if
        the users cancels the selection, the empty string may be returned.
      
- wxs_dir_selector(message, default, style, window_id)
- Opens a dialog to browse for a directory. The message is a
        string that is displayed in the dialog box to prompt the user
        for the directory. The path is the initial path where the dialog
        box should look, where the empty string means the current path.
        The default is the default directory name, where the empty
        string means no default directory name. Style is passed onto
        wxWidgets but apparently should always be 0. The window_id is
        the id for the window in which the dialog is opened.
- wxs_get_number(message, prompt, caption, number, min, max,
        window_id)
- Opens a dialog to get an integer. The message is a possible
        multi-line string to be displayed above a single line prompt
        string. The dialog window title is specified by the string
        caption. The number is the default value, and the number entered
        by the user must be in the range min to max, both of which
        should be positive. The dialog is centered on the window given
        by window_id. -1 is returned if the user enters an invalid value
        or cancels.
- wxs_get_text(message, caption, default, window_id)
- Opens a dialog to get text from the user. The dialog box
        contains message, a string, and the dialog window title is
        specified by caption, a string. The default return value is
        given by default, a string, and the dialog box is opened at the
        center of the window given by window_id. If the user presses
        Cancel, the empty string is returned.
- wxs_message_box(message, caption, style, window_id)
- Opens a dialog box to display message, a string, in a window
        with the title specified by caption, a string. The style is a
        combination (logical or) of WXS_STYLE_YES_NO, WXS_STYLE_CANCEL,
        WXS_STYLE_OK, WXS_STYLE_EXCLAMATION, WXS_STYLE_ERROR,
        WXS_STYLE_QUESTION, or WXS_STYLE_INFORMATION. The return value
        is one of the following: WXS_MSG_YES, WXS_MSG_NO,
        WXS_MSG_CANCEL, WXS_MSG_OK.
Timer
      In wxserpent, the graphic interface does not really become
      operational until you finish loading the initial program. Recall
      that the default startup action is to load init.srp
      which may load other files. After loading, control is passed to
      the wxWindows library. Depending on how graphical objects were
      created and initialized, various functions and methods will be
      called in response to user actions. If you want other processing
      to take place, your only option is to have wxWindows call a
      function periodically. This can be started by calling wxs_timer_start,
      as described below.
      
        - wxs_timer_start(interval, function)
- Starts calling the function named by the function parameter,
          a symbol, every interval ticks. Under Windows (at least), a
          tick is 1/100 seconds. The function should be defined and
          should take no parameters.
Error Handling and Debugging
    Be sure to see Section 
        “Debugging” in the Serpent page for additional 
        ideas including how to set breakpoints (of a sort).
    Error handling in wxSerpent uses the same simple
      debugger as Serpent. Remember to enable debugging by putting the statement 
    require "debug"
    early in your program.
    In wxserpent, there is no interactive command line, and output is
      directed to a window, so when the debugger waits for input, what
      happens? Blocking input in wxserpent is implemented by calling a
      pop-up dialog box to retrieve type-in. This usually puts a dialog
      box right in front of your application, which gets in your way,
      and if you are in the debugger, typing something to the dialog box
      will only encourage it to ask you another question.
      Type ! to 
      exit, ? for a stack trace, or > to resume (the other debugger 
      commands work too). 
    To make matters worse, printing is disabled during a paint event
    (see description of Canvas) so you will not see any printed
    information while drawing on a canvas object (except under Linux,
    where stdout is echoed to the shell window).
      On Windows, text output including
      debugger output is written only to stdout, which Windows does not
      display! This can make it difficult to use any debugging
      functions. Unless you disable it, the stack trace will go to the
      log file you have set up by calling
    
log_enable("application.log") 
If there is no log file, the debugger will attempt to open “srp.log”
      as a log file before printing the stack trace. If necessary, just
      kill the (wx)serpent program, then type srp.log or
      open the stack trace in a text editor to study the stack trace and
      any other debugging output you might have generated in your
      program.
    You may notice that the log file contains all text directed to
    stdout, whereas the text window opened by the application contains
    only text printed while not painting a canvas. When text is not
    printed, a warning will eventually appear to indicate text output
    was skipped.
    
Among the debugging commands you can see by typing
        “.” (except on windows you might have
        to look in the log file), two common ones to remember are:
    
      - ">" which tells the program to resume -- since Serpent
        is running in order to handle something for wxWidgets, control
        will return to wxWindows. It's possible that you can resume
        normal execution, but chances are good that touching another
        control will tickle the same bug and raise the same error.
- The "!" debugger command should exit the program.
OpenGL Classes
    If wxserpent is compiled with OpenGL functions (see installation
    notes), the following classes are available:
    
      - Glwindow(title, x, y, w, h)
- Creates a window that can be drawn upon with OpenGL. A
        Glwindow is a subclass of Canvas. All of the Canvas drawing
        methods are implemented by Glwindow, and alpha parameters are
        used for transparency. Use refresh and paint exactly as for a
        Canvas. At present, 3D methods and other OpenGL commands are not
        supported.
- Glcanvas(parent, x, y, w, h)
- Works just like Canvas except with OpenGL capabilities.