The following is new documentation for the grid-widget. It will be in the Allegro CL 7.0 release now being worked on, but it applies to the current (6.2) release as well. There will be several slides on the grid-widget beased on this description in the 3rd day of the intermediate course. This description is written to replace doc/pages/classes/common-graphics/grid-widget.htm. Since this is plain text rathe rthan HTML, there are no links. Items in angle brackets (< and >) are suggested links. David Margolies Franz Inc. --------------------------------------------------------------------------- grid-widget [the class] The class of all grid-widget controls. This is a very versatile (and therefore relatively complex) widget that is useful for implementing things such as spreadsheets and tables. Grid cells can either use built-in cell widgets or implement custom drawing and mouse/keyboard behavior. A grid-widget may be divided into multiple sections, each of which may be independently scrollable and resizable. Within each section are rows and columns that are collectively known as subsections. (Sections are sometimes referred to as "main sections" to distinguish them from subsections.) Rows and columns may be interactively resized, moved (to change their order), selected, and deleted, whenever each of those options is enabled. Rows and columns may be added and removed programmatically at any time. A row or column can have its own individual stylistic properties, and can also be replicated to produce a set of identical visual rows or columns that are represented by a single row or column lisp object. Each section, row, and column is implemented as a . A grid cell does not have a lisp object to represent it because the number of cells increases quadratically as the rows and columns increase. Instead, a cell is accessed by methods that specialize on both the row and column whose intersection defines the cell. Writing Grid-Widget Code Using a grid-widget consists of constructing the grid-widget from sections and subsections, specifying various stylistic properties, and (the trickier part) writing methods that control how various grid cells read application data to display, draw themselves based on that data, respond to mouse clicks and keypresses, and finally write user-modified data back to the application. Constructing a Grid-Widget Normally you will first define subclasses of various grid classes so that you can specialize methods on them. The basic classes to subclass are grid-widget, , , , and . When you create a grid-widget instance, specify its property as a list of instances of your grid-row-section subclasses, and its property as a list of your own grid-column-sections. When making each grid-row-section or grid-column-section, specify its property as a list of instances of your grid-row or grid-column subclasses (respectively). Optionally, you can also specify the property of a row or column to make that single row or column lisp object produce multiple identical visual rows or columns, which are distinguished by an index only. Subsections can also be added and removed later by calling , , , and . Call as usual to create each object. Grid sections and subsections have various properties that determine how they appear and behave mechanically. These may be specified when creating each object and/or changed anytime later. Some of these properties apply to both sections and subsections, including , , , , , and . Properties that apply only to sections include , , , , , and . Properties that apply only to subsections include , , and . Manipulating Application Data in a Grid-Widget In addition to writing code to constuct a grid-widget and specify its mechanical behavior, you need to write methods that specify how it displays application data and optionally modifies it. (Further below we discuss some higher-level alternatives that can remove the need to write some or all of these methods.) Here are the basic generic functions to specialize in order to display data in a grid-widget: fetches an application data value to be represented in a grid cell, based on the and that were passed to it. draws something in a grid cell, typically to represent a value that it retrieved by calling . draw-cell is called automatically whenever a grid-cell is uncovered and therefore needs to be redrawn. If you know that an application value has changed and needs to be redrawn in its grid cell, you still should not call draw-cell yourself; instead, you should force the cell to redraw itself by calling , or force a whole section to update by calling , or the whole grid-widget by calling . There is a default draw-cell method that calls princ-to-string on the value returned by read-cell-value, and draws that string in the cell. If this is sufficient, then you do not need to write your own draw-cell methods. And here are the basic generic functions to specialize if you also want to allow the user to modify the data interactively in the grid, rather than simply viewing it: is called when the user presses a mouse button over a cell, and is called when the user presses a keyboard key while a cell has the keyboard focus. Methods can be written on these generic functions that decide how to respond to the gestures and change the value that is represented in a cell, and then to write the modified value back into the application, typically by calling . You may also specialize and to respond in some way as the mouse cursor enters and leaves various cells. modifies an application's data based on changes that a user has made with interactive gestures in a grid cell. Higher-Level Alternatives The above generic functions are all that you need to specialize in order to write your data-manipulating grid code from scratch. But there is some additional higher-level grid functionality that can reduce the number of grid methods that must be written for typical applications. These alternatives are applicable to any application that uses grid rows to represent data objects and grid columns to represent their properties. For example, one of the Navigator grid examples uses grid rows to represent employees, and grid columns to represent properties such as employee name and department. We will discuss that example here. This higher-level functionality falls into two camps: reader and writer properties, and cell widgets. Reader and Writer Properties The first higher-level alternative lets you avoid writing and methods. In an application such as the employee example, you would probably have an employee class that already has accessor functions such as employee-name and employee-department. When representing employees and their properties in a grid-widget, you could specify the property of each as the employee that the row represents, and specify the property of each as the existing accessor function (such as employee-name or employee-department) for the property that the column represents. If you're allowing the user to modify data values in the grid, then you could also specify a function such as (setf employee-name) as the property of the column. It just so happens that the default method calls the grid column's data-reader function on the row's data-object, if both of those exist. And the default method calls the column's data-writer function on the data-object. This means that by specifying data-object, data-reader, and data-writer properties for your grid subsections, you can avoid writing custom read-cell-value and write-cell-value methods for various types of cells. You can still write your own methods that call the default read-cell-value method, though, and write custom and methods that call the default write-cell-value method. See also and . Built-In Grid Cell Widgets The other higher-level alternative lets you avoid writing custom , , and methods. It consists of several types of built-in "cell widgets" that know how to draw themselves and how to respond to user gestures to modify the represented value. When using these built-in cell widgets, you can still write your own read-cell-value and write-cell-value methods that will be called automatically by the built-in cell widget code. Or use BOTH of these higher-level alternatives together to avoid writing any custom methods. Since there isn't an object for each grid cell, the built-in cell widgets are defined as mixin classes that you combine with a class. The defined classes are , , , , and . The pseudo-widget will appear in any cell whose column is an instance of one of these mixin classes and whose row is an instance of the class. This design lets you specify a type of widget for some subset of the cells of a grid column. This is appropriate for applications that use a grid column to represent a property, since typically the same widget would be appropriate for editing a particular property regardless of which data object (grid row) is being edited. Typically you would have a header row or row-section that does not have the , and then a number of "body" rows that do have it, so that the widget does not appear in the header row. See also row-header-column and column-header-row. Controlling the Built-In Draw-Cell Methods If you are using a built-in method that's supplied by a cell widget, or the default method that displays any cell value as a string, then you can still exert some control over how a cell draws itself. You do this by writing trivial methods that return specific aspects of the drawing style. The generic functions to specialize include , , , , , , , , , and . Each built-in draw-cell method will call some subset of these generic functions and use the values that they return. Bypassing Read-Cell-Value and Write-Cell-Value Altogether Perhaps it will be clear at this point that and exist primarily because the higher-level grid features uses them to separate the functionality that they provide from the methods that you still have to write. So if you're not using either higher-level alternative, then a method actually would not need to call a separate read-cell-value method, and could instead fetch its application data to draw directly. Similarly, a or method would not need to call a separate write-cell-value method, and could instead modify the application's data directly. The "simple color editor" example in is an example of this. Adding a Grid-Widget to a Form A grid-widget can be added to a
interactively as with other widgets, but it is probably simpler to write all of its code programmatically, due to the need to define subclasses and properties for sub-components. To usefully add a grid-widget to a form interactively, you probably should first define the subclasses for your grid-widget, sections and subsections, and give them default-initargs that specify the properties that you would like for their instances, and then call to add your customized grid-widget class to the widget palette. Additional Grid-Widget Help Please refer to the multiple grid-widget examples in for complete example code. And to look up additional grid functionality that was not covered here, try invoking the command and doing a in that dialog on the string "grid-widget". -----------------------------------------------------------------