Next: , Previous: , Up: Specifiers   [Contents][Index]


48.2 Simple Specifier Usage

A useful specifier application is adding a button to a toolbar. SXEmacs provides several toolbars, one along each edge of the frame. Normally only one is used at a time, the default. The default toolbar is actually a specifier object which is the value of default-toolbar. See Toolbar Intro.

The specification of a toolbar is simple: it is a list of buttons. Each button is a vector with four elements: an icon, a command, the enabled flag, and a help string. Let’s retrieve the instance of the toolbar you see in the selected frame.

(specifier-instance default-toolbar)

The value returned is, as promised, a list of vectors. Now let’s build up a button, and add it to the toolbar. Our button will invoke the last defined keyboard macro. This is an alternative to name-last-kbd-macro for creating a persistent macro, rather than an alias for C-x e.

A toolbar button icon can be quite sophisticated, with different images for button up, button down, and disabled states, and a similar set with captions. We’ll use a very simple icon, but we have to jump through a few non-obvious hoops designed to support the sophisticated applications. The rest of the button descriptor is straightforward.

(setq toolbar-my-kbd-macro-button
  `[ (list (make-glyph "MyKbdMac"))
     (lambda () (interactive) (execute-kbd-macro ,last-kbd-macro))
     t
     "Execute a previously defined keyboard macro." ])

(set-specifier default-toolbar
               (cons toolbar-my-kbd-macro-button
                     (specifier-specs default-toolbar 'global))
               'global)

To remove the button, just substitute the function delete for the cons above.

What is the difference between specifier-instance, which we used in the example of retrieving the toolbar descriptor, and specifier-specs, which was used in the toolbar manipulating code? specifier-specs retrieves a copy of the instantiator, which is abstract and does not depend on context. specifier-instance, on the other hand, actually instantiates the specification, and returns the result for the given context. Another way to express this is: specifier-specs takes a locale as an argument, while specifier-instance takes a domain. The reason for providing specifier-instance is that sometimes you wish to see the object that SXEmacs will actually use. specifier-specs, on the other hand, shows you what the programmer (or user) requested. When a program manipulates specifications, clearly it’s the latter that is desirable.

In the case of the toolbar descriptor, it turns out that these are the same: the instancing process is trivial. However, many specifications have non-trivial instancing. Compare the results of the following forms on my system. (The ‘(cdr (first ...))’ form is due to my use of Mule. On non-Mule SXEmacsen, just use specifier-specs.)

(cdr (first (specifier-specs (face-font 'default) 'global)))
=> "-*--14-*jisx0208*-0"

(specifier-instance (face-font 'default))
#<font-instance "-*--14-*jisx0208*-0" on #<x-device on ":0.0" 0x970> 0xe0028b 0x176b>

In this case, specifier-instance returns an opaque object; programs can’t work on it, they can only pass it around. Worse, in some environments the instantiation will fail, resulting in a different value (when another instantiation succeeds), or worse yet, an error, if all attempts to instance the specifier fail. specifier-instance is context-dependent, even for the exact same specification. specifier-specs is deterministic, and only depends on the specifications.

Note that in the toolbar-changing code we operate in the global locale. This means that narrower locales, if they have specifications, will shadow our changes. (Specifier instancing does not merge specifications. It selects the "highest-priority successful specification" and instances that.)

In fact, in our example, it seems pretty likely that different buffers should have different buttons. (The icon can be the same, but the keyboard macro you create in a Dired buffer is highly unlikely to be useful in a LaTeX buffer!) Here’s one way to implement this:

(setq toolbar-my-kbd-macro-button
  `[ (list (make-glyph "MyKbdMac"))
     (lambda () (interactive) (execute-kbd-macro ,last-kbd-macro))
     t
     "Execute a previously defined keyboard macro." ])

(set-specifier default-toolbar
               (cons toolbar-my-kbd-macro-button
                     (cond ((specifier-specs default-toolbar
                                             (current-buffer)))
                           ((specifier-specs default-toolbar
                                             'global)))
               (current-buffer))

Finally, a cautionary note: the use of specifier-specs in the code above is for expository purposes. Don’t use it in production code. In fact, the set-specifier form above is likely to fail occasionally, because you can add many specifications for the same locale.

In these cases, specifier-specs will return a list. A further refinement is that a specification may be associated with a set of specifier tags. If the list of specifier tags is non-nil, then specifier-specs will return a cons of the tag set and the instantiator. Evidently specifier-specs is a bit unreliable. (For toolbars, the code above should work 99% of the time, because toolbars are rarely changed. Since instantiation is trivial, multiple specs are not useful—the first one always succeeds.)

In fact, specifier-specs is intended to be used to display specs to humans with a minimum of clutter. The robust way to access specifications is via specifier-spec-list. See Adding Specifications, for the definition of spec-list. See Retrieving Specifications, for documentation of specifier-specs and specifier-spec-list. To get the desired effect, replace the form (specifier-spec default-toolbar 'global) with

(cdr (second (first (specifier-spec-list default-toolbar 'global))))

(It should be obvious why the example uses the lazy unreliable method!)


Next: , Previous: , Up: Specifiers   [Contents][Index]