Next: Specifier Instancing, Previous: Simple Specifier Usage, Up: Specifiers [Contents][Index]
Having variables vary according the editing context is very useful, and the buffer is the natural “atomic” unit of editing context. In a GUI environment, it can be similarly useful to have variables whose values vary according to display context. The atomic unit of display context is the SXEmacs window. Buffers are cleanly grouped by modes, but windows are not so easily pigeonholed. On the one hand, a window displays a buffer, and thus one possible hierarchy is window, buffer, mode. On the other, a window is a component of a frame. This generates the window, frame, device hierarchy. Finally, there are objects such as toolbars whose properties are described by specifiers. These do not fit naturally into either hierarchy. This problem is as yet not cleanly solved.
Another potential source of conceptual confusion is the instantiation
process. Instantiating a buffer-local variable is simple: at any given
point in time there is a current buffer, and its local values are used
and set whenever the variable is accessed, unless the programmer goes to
some special effort (uses default-value
and set-default
.
However, a specifier object encapsulates a set of specifications,
each of which says what its value should be if a particular condition
applies. Several such conditions might apply simultaneously in a given
window.
For example, one specification might be “The value should be darkseagreen2 on X devices” another might be “The value should be blue in the *Help* buffer”. So what do we do for "the *Help* buffer on an X device"? The answer is simple: give each type of locale a priority and check them in priority order, returning the first instantiator that successfully instantiates a value.
Given a specifier, a logical question is “What is its value in a particular situation?” This involves looking through the specifications to see which ones apply to this particular situation, and perhaps preferring one over another if more than one applies. In specifier terminology, a “particular situation” is called a domain, and determining its value in a particular domain is called instancing. Most of the time, a domain is identified by a particular window. For example, if the redisplay engine is drawing text in the default face in a particular window, it retrieves the specifier for the foreground color of the default face and instances it in the domain given by that window; in other words, it asks the specifier, “What is your value in this window?”.
Note that the redisplay example is in a sense canonical. That is, specifiers are designed to present a uniform and efficient API to redisplay. It is the efficiency constraint that motivates the introduction of specifier tags, and many restrictions on access (for example, a buffer is not a domain, and you cannot instantiate a specifier over a buffer).
More specifically, a specifier contains a set of specifications,
each of which associates a locale (a window object, a buffer
object, a frame object, a device object, or the symbol global
)
with an inst-list, which is a list of one or more
inst-pairs. (For each possible locale, there can be at most one
specification containing that locale.) Each inst-pair is a cons of a
tag set (an unordered list of zero or more symbols, or tags)
and an instantiator (the allowed form of this varies depending on
the type of specifier). In a given specification, there may be more
than one inst-pair with the same tag set; this is unlike for locales.
The tag set is used to restrict the sorts of devices over which the instantiator is valid and to uniquely identify instantiators added by a particular application, so that different applications can work on the same specifier and not interfere with each other. Each tag can have a predicate associated with it, which is a function of one argument (a device) that specifies whether the tag matches that particular device. (If a tag does not have a predicate, it matches all devices.) All tags in a tag set must match a device for the associated inst-pair to be instantiable over that device. (A null tag set is perfectly valid, and trivially matches all devices.)
The valid device types (normally x
, tty
, and
stream
) and device classes (normally color
,
grayscale
, and mono
) can always be used as tags, and match
devices of the associated type or class (see Consoles and Devices).
User-defined tags may be defined, with an optional predicate specified.
An application can create its own tag, use it to mark all its
instantiators, and be fairly confident that it will not interfere with
other applications that modify the same specifier—Functions that add
a specification to a specifier usually only overwrite existing
inst-pairs with the same tag set as was given, and a particular tag or
tag set can be specified when removing instantiators.
When a specifier is instanced in a domain, both the locale and the tag set can be viewed as specifying necessary conditions that must apply in that domain for an instantiator to be considered as a possible result of the instancing. More specific locales always override more general locales (thus, there is no particular ordering of the specifications in a specifier); however, the tag sets are simply considered in the order that the inst-pairs occur in the specification’s inst-list.
Note also that the actual object that results from the instancing (called an instance object) may not be the same as the instantiator from which it was derived. For some specifier types (such as integer specifiers and boolean specifiers), the instantiator will be returned directly as the instance object. For other types, however, this is not the case. For example, for font specifiers, the instantiator is a font-description string and the instance object is a font-instance object, which describes how the font is displayed on a particular device. A font-instance object encapsulates such things as the actual font name used to display the font on that device (a font-description string under X is usually a wildcard specification that may resolve to different font names, with possibly different foundries, widths, etc., on different devices), the extra properties of that font on that device, etc. Furthermore, this conversion (called instantiation) might fail—a font or color might not exist on a particular device, for example.
Next: Specifier Instancing, Previous: Simple Specifier Usage, Up: Specifiers [Contents][Index]