Copyright ©2001-2007 by OCSoftware. All Rights Reserved.
Power User Features
Overview
OCSmart Hacks allow a number of special services, which, at least at the moment, for diverse reasons are available to power users only. Feel free to exploit what is described in this document, but only if you understand it well and are sure you can return to the original state in case anything goes wrong.
Disclaimer: although I shall try, I cannot promise to give a full e-mail support for features described here.
Installation
OCSmart Hacks are installed as an Input Manager: by design, a code which allows you to use special ways to enter characters. In fact though Hacks are not an Input Manager; they just exploit the fact Input Managers are automatically loaded into (Cocoa) applications. There are other ways to achieve the same result, but this one is by far the most convenient both for the programmer and the user.
It of course means that you can install OCSmart Hacks for all users into an appropriate Library folder. Since that needs administrator access, the application, when installing, intentionally does not do that: if you know how to and want to, DIY. If you don't know how to, it is much better for you not to mess with the shared Library.
Keyboard Watcher
Note: at this moment, the service is considered internal, since (a) I am not sure it would help anybody but myself ☺, and (b) it lacks a proper GUI and its flexibility is somewhat limited. Therefore, if you are using it, do please let me know; if I get a reasonable number of such requests, I shall make it in some 1.x release a fully supported feature with its own Preferences pane, much better documentation, complete services (like a possibility to display the keyboard name too, user-definable keyboards, names, and sounds, possibility to set automatically a desired keyboard for some applications), and so on.
The Keyboard Watcher–if switched on–constantly keeps watch over the currently selected keyboard. Whenever the keyboard changes (or, optionally, the current application or the current window changes), a ghost icon of the current keyboard is for a moment shown near to the text insertion point (like this)

and the current keyboard name is read via the computer default sound output (using the default system voice).
The service is controlled using these default values (see below, "Special Defaults", for more information on how defaults are used with OCSmart Hacks):
First Aid
Since OCSmart Hacks are a loadable bundle which must co-operate, let's say, quite intimately with other applications' implementations, there is always a considerable probability of some mischief and, alas, there is no way to dodge discrepancies absolutely. Although very improbable, it is therefore in principle possible that some application clashes with Hacks so hard it cannot be even properly launched (and therefore you cannot use the standard "Disable" button).
First, in such a case please check the OCSmart Hacks Known Problems page at the OCSoftware site: there may be a workaround known. If it is not so, first try to disable the tear-off menus (for if an application scans its own menus dynamically, it may bump into those "Tear Off" items added by Hacks).
To make manual adjustments, you need to know the application's signature. You may know it–it is generally the application's Preferences file name (but for the .plist suffix). If you don't know the signature, launch the Terminal application, and in its window try to launch the application using a command
.../appname.app/Contents/MacOS/appname -OCSHShowSignature YES
where the ellipsis is the path and appname the application name: for example, with TextEdit it would be
/Applications/TextEdit.app/Contents/MacOS/TextEdit -OCSHShowSignature YES
you shall see a line which shows the application signature in parentheses (and perhaps other lines, too–depends on the application)–here we learn that the TextEdit's signature happens to be com.apple.TextEdit:
2004-07-31 17:44:56.279 TextEdit[10828] # OCSmartHacksLoader (com.apple.TextEdit): application signature
If the application happens not to quit immediately (it probably would, since it is the very reason we do this all!), you can kill it by pressing Control-c in the Terminal window at this moment.
Now, to disable tear-off menus, enter
defaults write signature OCSHDisableTearoffMenus 1
and try to re-launch the application. If it works all right, the problem was indeed in menus; you can now use all the other OCSmart Hacks features (and you even can try to enable tear-off menus again, and set the OCSHOldStyleTearoffMenus feature described below: that may allow you even to use tear-off menus, albeit not with the original comfort).
If the worst comes to the worst, you have to disable Hacks for the application altogether. If so happens, enter
defaults write signature OCSHDisableHacks 1
When done, store please a copy of what you did in the Terminal into a file (using the Terminal command File ▸ Save Text As...), and send me the file along with the report of the problem.
Special Defaults
There are some defaults intentionally not available from the GUI. If you want to play with them, you need to know how to use the defaults CLI command.
Note also that there is no "OCSmartHack" defaults domain. Instead, domains of the hosting applications are used (and, where appropriate, the global domain as well, of course). Therefore, in case you want to, say, set OCSHMinimalLogLevelShown to 5 within any host, suppressing so any Hacks printout, you would use "defaults write -globalDomain OCSHMinimalLogLevelShown 5"; to suppress logs for iCal Helper only, you would use "defaults write com.apple.iCal.helper OCSHMinimalLogLevelShown 5" (the host signature is shown in the first pane of the OCSmart Hacks window, and now is also part of the Loader logs if applicable).
Automatic Creation and Maximization of Documents
Normally, the automatic document creation control is enabled (i.e., settable by the "Create automatically untitled window" radio buttons in the OCSmart Hacks panel) only if
You can override the first two conditions if you want to, if you can cope with consequences: for example, if you override the first one, the override may work and may not, depending on the application code. If you override the second one, the application may quit at the most inconvenient moments (just since it thinks its last window was closed). To override the first condition, set OCSHDocumentBasedOverride to YES; to override the second, set OCSHWindowCentricOverride to YES. It is intentionally not possible to override the third condition, for it may seriously mess with the application code, and the benefits would be negligible.
Note also that the normal installation of OCSmart Hacks sets up OCSHDontOpenUntitledFile in the global domain to 0. You may want to delete the default (issuing a command "defaults delete -globalDomain OCSHDontOpenUntitledFile" in Terminal); if you do so, applications will ask whether to create an untitled window a similar way they ask now whether to maximize a docked one (see Creation and Maximization of Windows Help page).
Normally, the automatic docked window maximization control is enabled (i.e., settable by the "Maximize automatically docked window" radio buttons in the OCSmart Hacks panel) only if
You can override both the conditions if you want to, if you can cope with consequences: for example, if you override the first one, the override may work and may not, depending on the application code. To override the first condition, set OCSHDocumentBasedOverride to YES.
If you override the second one, problems may occur if the application code happens to depend on being called when the application is activated: it is not possible to foresee which applications would have problems and which not, it must be tested. Nevertheless, it may be well worth trying, for the automatic maximization is extremely annoying, and a number of applications alas does that (the most excellent example would probably be Safari, which–so far–runs with overridden maximization without any problem).
To override the fact the application uses its own code, set OCSHOwnCodeForMaximizationOverride to YES. That causes the applications' code which determines whether to maximize a docked window or not never to be called. If that brings problems, you may try to set OCSHOwnCodeForMaximizationOverrideCallOriginal to YES: in that case, the original code will be called, but its return value will be changed so that the system does what is indicated by the "Maximize automatically docked window" radio buttons. If you use this option, note that the original code may maximize the window itself, without using the standard system code (which is, incidentally, exactly what Safari does).
HTML Generation
The HTML generation uses two sets of rules. Type Parse rules scan the formatted text, and using its characteristics, emit logical type names (like, "Bold", "Italics", "UnorderedList". HTML Generation rules then use the current set of logical type names to generate appropriate tags. The mechanism is pretty flexible, though by far not as much as I would like: to be frank, I've rigged it to support my current needs. In future, there should be a rule editor (rather a separate application than a direct part of Hacks), and a set of conventions of linking rules (to be able to choose, say, different HTML Generation rules for given Type Parse ones, but only of a set of those which use the same logical types).
The rules, just like the HTML presets, are Cocoa property lists, normally stored as XML files. The HTML presets contain, among other information, links to rules. To use your own rules, you have to
There is a default pair of rules inside Hacks bundle; you can copy them out and use them as a reference and templates for your own rules. Here is the rough rules description:
Disclaimer: this is my internal documentation. You will most probably not understand it. Besides, there is no quarantee the format will not change in future.
Type Parse Rules specify how the RTF types are parsed into logical ones
The array contains logical expressions. Each of them is applied on every text chunk. If an expression matches the chunk (and its attributes), its '+' logical type value--if any--is emitted. If an expressions does not match, its '-' logical type value--if any--is emitted. The complete set thus emits a set of logical type names.
Each logical expression in the array is a dictionary with these keys:
"?": a conditional expression (see below) to match
"??": an array of conditional expressions which all must match (if "?" is not used)
"+": a string, the logical type to add if the expression matches
"-": a string, the logical type to add if the expression does not match
"//": reserved for a string comment, in future showable in Parse Rules editor when any
A conditional expression is either a keypath (the result is sent intValue if not a string and if responds to, and checked against zero; if does not respond, checked against nil), or an array. Its first item is the operator; the other items are operands
(i) comparation and bitwise operators:
=, ==, !, !=: [1stop isEqual:2ndop] is used appropriately
<, <=, >=, >: [1stop compare:2ndop] is used appropriately
&: ([2ndop intValue] & [1stop intValue]) is checked against zero
contains: [1stop rangeOfString:2ndop].length!=0
Here, the 1stop (object at index 1 in the array) must be a keypath; 2ndop can be either a keypath or an NSString or NSNumber constant.
A keypath is like the standard WebObjects keypaths: it begins by a dot, is applied to the text attributes dictionary via the standard valueForKey: message. Anything what is not a string or does not begin by a dot is a constant; also a string constant may be forced by prepending a '#' (e.g., "##" would be constant "#", "#." would be "." and so on).
(ii) current state operators:
+: checks whether all given logical type names are part of the current state
-: checks whether none of given logical type names is part of the current state
The operands (any number of them) are just the logical type names checked. As for the current state, it means the set of logical type names emitted by the parser for the previous text chunk; see also below.
(iii) special string operators:
pp: no operands, true if at beginning of paragraph; any operand, true if NOT at beginning of paragraph
prefix: there's any number of string operands; matches if the text chunk begins by any of them
Notes:
- since isFixedPitch does not work too well, there is a new NSFont method ocsLooksLikeFixedPitch, which returns YES if 'i' is as wide as 'm'HTML Generation Rules specify how the HTML is generated
The Type Parse Rules emitted a set of logical type names. The parser keeps a 'current' list of the logical type names; whenever the set changes, the changes (both 'adding' and 'removing') are processed using the Generation Rules dictionary.
The dictionary keys are the logical type names. The values can be
- a plain NSString. If so, it is added in <...> if the type was added, in </...> if removed. There can be more tags separated by |
- a dictionary with keys
"": the tag, which will be used <...> and (if no </> key) </...> just like the string above
"</>": if exists, the complete closing tag (incl.<...>), so as can be empty
"-": an empty string value, if exists, bullets/paragraph numbers are removed (one day, a regexp will be possible here, not now)
xxx: the parameter xxx="yyy" to be used in the opening tag. The value specifies the "yyy":
- an NSString: either a constant or a keypath, which provides the value
- an array. Each item is a dictionary or string. Just a list of more commands to be emitted the same way as in the previous two cases
- an array. Each item (but the last) is an array, there must be at least one array item (distinguishes from the previouis case):
- the first one is a _conditional expression_ (see above), or ""
- if the first was "", the second item is an array of conditional expressions
- the last item is an NSString or a dictionary, to be processed just as described above, IF the conditional (or all the conditionals in the array) matches
When using conditionals, note that at the moment of generation the Type Parsing is finished, thus the conditional operators '+' or '-' refer to the current state, as just parsed in. Also, the last item in the conditional array can be directly the NSString or NSDictionary value, to serve as an "else" item, to be used if none of the conds matched. Otherwise, if none of conds matches, nothing will be emitted into the HTML.
Apart of the logical types you create yourself, the type "Paragraph" is sent automatically at paragraph boundaries.
To support local links, there is a special NSString and NSURL method ocsRemoveLocalURLPrefix, which can be used in keypaths. It just removes a "local:" prefix from string, if any
There can be a special key "0". It is not interpreted as a logical type name. Its value (if present) must be an array of logical type names which are to be generated before a paragraph. The order or generation at the beginning of a paragraph thus is (i) logical types which are in this array, (ii) Paragraph, (iii) all the other logical types.