* Inside Mercutio(pdf)
* Licensing Description
* Software License(pdf)
Mercutio and non-U.S. Keyboards
The ProblemUnder conventional Macintosh user interfaces, the shift and option keys changed the lexicality of keypress: an '8' became an asterisk or a bullet symbol. The command key (and later the control key) changed the semantics of a keypress: the letter stays the same, but changes in meaning (instead of typing a letter, it would execute an action).
These distinctions became blurred as developers sought ways to add shortcuts to their interfaces. Option-dragging changed functionality so why shouldn't an option-keypress? The Mercutio MDEF attempts to help address this need. However, mixing the roles of the modifier keys can become problematic, especially when dealing with a variety of keyboard layouts.
Under Mercutio, however, we may not want certain modifiers to be used as lexical modifiers but as semantic modifiers instead. This is the essence of the problem: there is no clean way for Mercutio to tell whether a modifier key is being used to change the lexical meaning of a key (e.g. from 'X' to 'Q') or to change the semantic meaning of a key (e.g. from "type 'Q'" to "do the command associated with 'Q'").
KeyTrans and the KCHR resourceKeyTrans is the toolbox routine that maps from a particular keypress (linked to the physical button pressed on the keyboard, as defined by a KCHR resource) to an ASCII value (as stored in the MENU) resource. KeyTrans is typically responsible for the lexical mapping mentioned above.
Mercutio strips out the modifiers before calling KeyTrans, assuming all modifiers are being used for semantic purposes. (Actually, to maintain Dvorak compatibility, Mercutio first tries it without any modifiers, and if that doesn't work, tries it with the Command-key only. But that's a detail -- the larger problem remains). This becomes problematic for keyboards that may need some of the modifier keys simply in order to type the correct ASCII value.
Example 1Consider the following BBEdit 3.5 example:
Thus, there is currently no way for a Norwegian keyboard user to trigger than key equivalent. This sucks.
Obvious but Faulty SolutionOne possible solution is not to strip out the modifiers and to pass all the user's modifiers to KeyTrans to be mapped to an ASCII value. In the above example, it would have worked: passing command, shift and option into KeyTrans would have returned '\' and we could have matched the keypress. Note, however, that we would be unable to distinguish between Command-\ and Command-Shift-\, since we needed all the modifier keys to be held down just to get to the backslash character.
Furthermore, what happens if it doesn't match on the first try? It may be that only some of the modifiers are lexical and the others are semantic. We could start stripping out the modifiers one by one. So what modifier do I strip out? If cmd-shift-opt doesn't work, do I then check the cmd-shift, the cmd-opt or the shift-opt KCHR? What if there are several matches? I'll need to come up with all the possible combinations of those modifiers, and develop a heuristic for chosing between two matches. Even if I do come up with an algorithm for stripping these out, it won't help with certain KCHR mappings.
Example 2Consider this BBEdit example with the U.S. keyboard (HTML3.0 Table -- use Netscape 1.1 to view):
Better SolutionI think the better solution is to develop a new reverse KCHR resource that maps from an ASCII value to a keypress with modifiers (semantic). I can then check to see if those are held down and if so, check if any additional required modifiers held down to match it against the particular menu item.
The problem is that no reverse KCHR resource exists. I would need to build it on the fly every time MDEF_MenuKey is called (since the user may change the Keyboard at any time) or somehow determine when the keyboard has been changed. If anyone knows how to query the Script Manager to get a value representing the current keyboard layout, let me know. I wasn't able to find it in the Toolbox assistant, and my attempts at fiddling with Script Manager variables has been fruitless. The alternative is to force users to restart the application if they switch keyboards (I'm not exactly sure why they'd be switching keyboards in the middle of an application session, so that may not be too big of an imposition...)
[update June 28, 1996: Joshua Dunfield says KCHRid := GetScript(smCurrentScript, smScriptKeys); should do it]
Another bothersome thing is that the reverse KCHR structure will need to be in a handle since it can be of variable size. Since MDEFs don't get 'dispose' messages, there's the potential of abandoned memory blocks if you unload the MDEF before quitting your application...
Related QuestionsA related issue is what to display in the menu: do I display what the developer has indicated are the key equivalents or do I taylor that to localized keyboards? That is, if the developer indicates that the key equivalent is Command-\, but to get a slash the user needs to hit Shift-Option-7, what do I display in the menu:
Still need to resolve conflicting equivalentsI'm still stuck on is how to resolve conflicting equivalents, by which I mean key equivalents that can't be typed by a user. Let's say Mercutio has this new functionality. Going back to our first example, Mercutio would notice that the Norwegian user must type Shift-Option-7 to get the backslash, and so be looking to match the menu item against the Cmd-Shift-Opt-7 sequence.
What if BBEdit had Command-\ and Command-Shift-\ allocated as key equivalents? On the Norwegian keyboard, the Shift key is already being used to lexically change the '7' into a '\', so it can't be used as a semantic modifiers. Again, we're stuck with a situation where the user can't hit the key equivalent.
The only solution to this that I can think of is to check for conflicts against every single keyboard. In effect, this results in restrictions on what keys developers can assign to menu items. And if I do, I might as well simply tell them "don't use keys that can't be typed without modifiers on foreign keyboards" (i.e. stick with characters as key equivalents) or ship different menu settings for localization. This effectively solves the problem by constraining menu design rather than coming up with a tractable programmatic solution.
Conclusion (?)Since there doesn't seem to be an unambiguous way to solve this problem programmatically, I will leave it as a design problem for the developer. If anyone can come up with a solution I can implement, I'll do it.
Thanks for listening.
This site was built on a Macintosh using BBEdit and CometPage.