Next: Specifiers In-Depth, Previous: Introduction to Specifiers, Up: Specifiers [Contents][Index]
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: Specifiers In-Depth, Previous: Introduction to Specifiers, Up: Specifiers [Contents][Index]