message (lx_message.hpp)
Contents
International Messages
This module defines the interface to the International Message subsystem for internationalization (abbreviated as I18N by people in the trade). Message strings are assigned numbers and read from a database at runtime. The databases are preserved in XML within the config system. The message system looks up the right set of message strings based on the runtime language choice and the application uses that text instead of hard-coded text strings.
Table Access
Language ID's are the codes used by Windows to define the national setting. It can be read directly with a Win32 interface, but will need to be computed differently on other systems. The Windows language codes are nice because they group langauges by variants so that natural fallbacks will be close to understandable by variant subgroups. For example, UK English speakers will get US English if no UK variant messages are available. As nice as these windows-centric magic numbers may have been, they have been supplanted by an international-standards-compliant language and country code system which uses a 2 letter string (dfeined in ISO-639) to pick the language, followed by an optional ISO-3166 country code separated by an underscore.
Common Messages
The "common" message table contains a set of translatable strings that have consistent uses throughout the application. The codes are all old-style numeric, defined here.
(1) SDK: LXiCM_ACT_ERROR, etc. defines
#define LXiCM_ACT_ERROR 100 #define LXiCM_ACT_WARNING 101 #define LXiCM_ACT_OK 102 #define LXiCM_ACT_CANCEL 103 #define LXiCM_ACT_YES 104 #define LXiCM_ACT_NO 105 #define LXiCM_ACT_DONE 106 #define LXiCM_ACT_YESALL 107 #define LXiCM_ACT_RENAME 108 #define LXiCM_ACT_ADD 109 #define LXiCM_ACT_DELETE 110 #define LXiCM_ACT_CLEAR 111 #define LXiCM_ACT_SAVE 112 #define LXiCM_ACT_NOSAVE 113 #define LXiCM_ACT_LOAD 114 #define LXiCM_ACT_CLOSE 117 #define LXiCM_ACT_CONTINUE 118 #define LXiCM_SPC_NONE 125 #define LXiCM_SPC_UNNAMED 126 #define LXiCM_SPC_MIXED 127 #define LXiCM_ACT_INFO 128 #define LXiCM_ACT_QUIT 132 #define LXiCM_ACT_NAME 133 #define LXiCM_ACT_TYPE 134 #define LXiCM_ACT_PROJ 135 #define LXiCM_ACT_AXIS 136 #define LXiCM_SPC_UNKNOWN 137 #define LXiCM_ACT_COPY 138 #define LXiCM_ACT_CUT 139 #define LXiCM_ACT_PASTE 140 #define LXiCM_SPC_NEW 141 #define LXiCM_ACT_NEW 141 #define LXiCM_ACT_VOID 142 #define LXiCM_SPC_VOID 142 #define LXiCM_ACT_ALL 143 #define LXiCM_SPC_ALL 143 #define LXiCM_ACT_EXPORT 144 #define LXiCM_ACT_PROPS 145 #define LXiCM_ACT_HELP 146 #define LXiCM_ACT_FUTURE 147 #define LXiCM_ACT_SHOWM 148 #define LXiCM_ACT_HIDE 149 #define LXiCM_SPC_UNTITLE 150 #define LXiCM_ACT_REPLACE 151 #define LXiCM_ACT_RELOAD 152 #define LXiCM_ACT_DUPLICATE 153 #define LXiCM_SPC_PRIVATE 169 #define LXiCM_SPC_PLUGINS 170 #define LXiCM_SPC_ALLFILES 171 #define LXiCM_SPC_ALLFORMATS 172 #define LXiCM_ACT_SAVEAS 173 #define LXiCM_ACT_BAKE 188 #define LXiCM_ACT_INSTANCE 195 #define LXiCM_SPC_DEFAULT 196 #define LXiCM_ACT_LOADIMAGE 197 #define LXiCM_ACT_NOALL 198 #define LXiCM_ACT_NEWIMAGE 203 #define LXiCM_ACT_LOADSEQ 213 #define LXiCM_ACT_NEWSEQ 214 #define LXiCM_ACT_DEFORM 216 #define LXiCM_ACT_CHOOSECOLOR 217 #define LXiCM_ACT_ADDMAP 218 #define LXiCM_ACT_MOVE 700 #define LXiCM_ACT_PERFORMDROP 701 #define LXiCM_ACT_CREATEPRESET 702 #define LXiCM_ACT_CANCELDROP 703
(2) RESRC: common keys
<hash type="T" key="99">%1</hash> <hash type="T" key="100">Error</hash> <hash type="T" key="101">Warning</hash> <hash type="T" key="102">OK</hash> <hash type="T" key="103">Cancel</hash> <hash type="T" key="104">Yes</hash> <hash type="T" key="105">No</hash> <hash type="T" key="106">Done</hash> <hash type="T" key="107">Yes to All</hash> <hash type="T" key="108">Rename</hash> <hash type="T" key="109">Add</hash> <hash type="T" key="110">Delete</hash> <hash type="T" key="111">Clear</hash> <hash type="T" key="112">Save</hash> <hash type="T" key="113">Don't Save</hash> <hash type="T" key="114">Load</hash> <hash type="T" key="115">Assign</hash> <hash type="T" key="116">Unassign</hash> <hash type="T" key="117">Close</hash> <hash type="T" key="118">Continue</hash> <hash type="T" key="129">Find</hash> <hash type="T" key="130">Search</hash> <hash type="T" key="131">New Group</hash> <hash type="T" key="125">(none)</hash> <hash type="T" key="126">(unnamed)</hash> <hash type="T" key="127">(mixed)</hash> <hash type="T" key="128">Information</hash> <hash type="T" key="132">Quit</hash> <hash type="T" key="133">Name</hash> <hash type="T" key="134">Type</hash> <hash type="T" key="135">Projection</hash> <hash type="T" key="136">Axis</hash> <hash type="T" key="137">(unknown)</hash> <hash type="T" key="138">Copy</hash> <hash type="T" key="139">Cut</hash> <hash type="T" key="140">Paste</hash> <hash type="T" key="141">(new)</hash> <hash type="T" key="142">(void)</hash> <hash type="T" key="143">(all)</hash> <hash type="T" key="144">Export</hash> <hash type="T" key="145">Properties</hash> <hash type="T" key="146">Help</hash> <hash type="T" key="147">In the future</hash> <hash type="T" key="148">Show message dialog</hash> <hash type="T" key="149">Hide message</hash> <hash type="T" key="150">Untitled</hash> <hash type="T" key="151">Replace</hash> <hash type="T" key="152">Reload</hash> <hash type="T" key="153">Duplicate</hash> <hash type="T" key="154">Hide all messages like this one</hash> <hash type="T" key="155">Always "Yes"</hash> <hash type="T" key="156">Always "OK"</hash> <hash type="T" key="157">Always "Yes to All"</hash> <hash type="T" key="158">Always "No"</hash> <hash type="T" key="159">Always "Cancel"</hash> <hash type="T" key="160">"Yes" to all messages like this one</hash> <hash type="T" key="161">"OK" to all messages like this one</hash> <hash type="T" key="162">"Yes to All" to all messages like this one</hash> <hash type="T" key="163">"No" to all messages like this one</hash> <hash type="T" key="164">"Cancel" to all messages like this one</hash> <hash type="T" key="165">Confirmation Request</hash> <hash type="T" key="169">(private)</hash> <hash type="T" key="170">Plugins</hash> <hash type="T" key="171">All Files</hash> <hash type="T" key="172">All Formats</hash> <hash type="T" key="173">Save As</hash> <hash type="T" key="174">Label</hash> <hash type="T" key="175">Command</hash> <hash type="T" key="176">Left</hash> <hash type="T" key="177">Right</hash> <hash type="T" key="178">Middle</hash> <hash type="T" key="179">Button 4</hash> <hash type="T" key="180">Button 5</hash> <hash type="T" key="181">Button 6</hash> <hash type="T" key="182">Button 7</hash> <hash type="T" key="183">Button 8</hash> <hash type="T" key="184">Button 9</hash> <hash type="T" key="185">Button 10</hash> <hash type="T" key="186">Click</hash> <hash type="T" key="187">Double Click</hash> <hash type="T" key="188">Bake</hash> <hash type="T" key="189">[Any Key]</hash> <hash type="T" key="190">[Any Button]</hash> <hash type="T" key="191">[Anywhere]</hash> <hash type="T" key="192">Key assignments applied to all regions within the input map</hash> <hash type="T" key="193">(multiple)</hash> <hash type="T" key="194">(new key)</hash> <hash type="T" key="195">Make Instance</hash> <hash type="T" key="196">(default)</hash> <hash type="T" key="197">(load image)</hash> <hash type="T" key="198">No to All</hash> <hash type="T" key="AlwaysNoToAll">Always "No to All"</hash> <hash type="T" key="199">"No to All" to all messages like this one</hash> <hash type="T" key="200">Click Hold</hash> <hash type="T" key="201">Hold</hash> <hash type="T" key="202">load image</hash> <hash type="T" key="203">(new image)</hash> <hash type="T" key="204">Invert X and Y</hash> <hash type="T" key="205">Invert X</hash> <hash type="T" key="206">Invert Y</hash> <hash type="T" key="207">Invert</hash> <hash type="T" key="208">Wheel Up</hash> <hash type="T" key="209">Wheel Down</hash> <hash type="T" key="210">Wheel Up and Down</hash> <hash type="T" key="211">[Noop]</hash> <hash type="T" key="212">Blocks combos from being used this input maps, and keeps them from falling through to other input maps</hash> <hash type="T" key="213">(load sequence)</hash> <hash type="T" key="214">(new sequence)</hash> <hash type="T" key="215">(delete)</hash> <hash type="T" key="216">Deform</hash> <hash type="T" key="217">Choose Color</hash> <hash type="T" key="218">(add map)</hash> <hash type="T" key="700">Move</hash> <hash type="T" key="701">Perform Drop</hash> <hash type="T" key="702">Create Preset</hash> <hash type="T" key="703">Cancel Drop</hash> <hash type="T" key="cineIncompatible">Operation attempted on incompatible scenes.</hash>
Message Service
MessageService provides access to message tables, which contain localized human-readable versions of internal strings used within the application. All strings presented to the user should come from messagte tables.
(3) SDK: LXu_MESSAGESERVICE, etc. defines
#define LXu_MESSAGESERVICE "86A69B5D-ACFA-11D9-B38C-000A956C2E10" #define LXa_MESSAGESERVICE "messageservice"
As usual, the service starts with a ScriptQuery method.
(4) SDK: MessageService::ScriptQuery
LXxMETHOD( LxResult, ScriptQuery) ( LXtObjectID self, void **ppvObj);
ILxMessage Management
This allocate a new ILxMessage object. This object can then be populated with a message code and message table entry.
(5) SDK: MessageService::Allocate
LXxMETHOD( LxResult, Allocate) ( LXtObjectID self, void **ppvObj);
(6) SDK: CLxUser_MessageService::NewMessage method
bool NewMessage ( CLxLoc_Message &msg) { LXtObjectID obj; LxResult rc; rc = Allocate( &obj ); if( LXx_FAIL( rc ) ) return false; return msg.take( obj ); }
An existing ILxMessage object can be duplicated with this method.
(7) SDK: MessageService::Duplicate
LXxMETHOD( LxResult, Duplicate) ( LXtObjectID self, LXtObjectID msg, void **ppvObj);
(8) SDK: CLxUser_MessageService::DuplicateMessage method
bool DuplicateMessage ( CLxLoc_Message &msg, CLxLoc_Message &source) { LxResult rc; LXtObjectID obj; rc = Duplicate( source, &obj ); if( !LXx_FAIL( rc ) ) return false; return msg.take( obj ); }
Reading from Message Tables
This return a human-readable string from the given message object. This fails if there is no message text, but will also set the buffer to an empty string.
(9) SDK: MessageService::MessageText
LXxMETHOD( LxResult, MessageText) ( LXtObjectID self, LXtObjectID msg, char *buf, unsigned len);
(10) SDK: CLxUser_MessageService::GetText method
LxResult GetText ( CLxLoc_Message &msg, char *buf, unsigned len) { return MessageText( msg, buf, len ); }
This is a safe string "get" using a dummy class to act as callback for the safe string "read" template.
(11) SDK: CLxUser_MessageService::sgs_GetString method
class Tmp_Sgs { public: CLxLoc_MessageService *srv; ILxUnknownID msg; LxResult sgs_GetString (char *buf, unsigned int len) { return srv->MessageText (msg, buf, len); } }; LxResult GetText ( CLxLoc_Message &msg, std::string &text) { Tmp_Sgs tmp; CLxSafeGetString<Tmp_Sgs> sgs; tmp.srv = this; tmp.msg = msg; return sgs.GetString (tmp, text); }
Finally it's possible to get the raw message pattern directly from the message table, without substitution and without using an ILxMessage object. If 'table' is null, the message text will be decoded using the '@table@msg@' format.
(12) SDK: MessageService::RawText
LXxMETHOD( LxResult, RawText) ( LXtObjectID self, const char *table, const char *msg, const char **text);
Argument Types
Argument Type, in this context, refers to the user-displayed TextValueHints used as options for popup-style controls. These are stored under an ArgumentType key in the config, matching the internal name of the argument with a human-readable user name. Popups specify their argument type through some other mechanism, such as a Command's cmdhelp or an item's UI Reg data.
The config format is fairly striaght-forward, with an each ArgumentType containing a username description, and any number of Option with keys matching the internal strings representing each option, and with each Option also containing a username and description. The ArgumentType hash is a combination of the internal name and an associated language code, similar to what is done for message tables.
(13) Argument Type Example
<atom type="CommandHelp"> <hash type="ArgumentType" key="select-mode@en_US"> <atom type="UserName">Selection Mode Specifier</atom> <atom type="Desc">Modes modify selection commands to perform different actions.</atom> <hash type="Option" key="set"> <atom type="UserName">Set Primary</atom> <atom type="Desc">The selection becomes the primary selected element.</atom> </hash> </hash> </atom>
These methods return the username and description of a given argument type.
(14) SDK: MessageService::ArgTypeUserName, etc.
LXxMETHOD( LxResult, ArgTypeUserName) ( LXtObjectID self, const char *argType, char *buf, unsigned len); LXxMETHOD( LxResult, ArgTypeDesc) ( LXtObjectID self, const char *argType, char *buf, unsigned len);
These get a specific user string given an internal option name and an argument type name.
(15) SDK: MessageService::ArgTypeOptionUserName, etc.
LXxMETHOD( LxResult, ArgTypeOptionUserName) ( LXtObjectID self, const char *argType, const char *option, char *buf, unsigned len); LXxMETHOD( LxResult, ArgTypeOptionDesc) ( LXtObjectID self, const char *argType, const char *option, char *buf, unsigned len);
Empty message service Python user class.
(16) PY: empty Service.Message user class
pass
File Types
File types are normally determined by the presence of loaders and savers, although additional types can come from other sources. One-off file types can be defined in resources. The hash key should be the unique name for the specific file format, and is the value passed to the save dialog. The "class" is the value passed to the load dialog for building a list of, types for the same class of data object. This allows multiple format choices when loading, although this is typically not used by plug-ins.
(17) File Type Resource Sample
<atom type="FileSystem"> <hash type="FileType" key="lxir"> <atom type="Class">irrad</atom> <atom type="Pattern">*.lxi</atom> <atom type="Extension">LXI</atom> </hash> <atom>
The user name for the format comes from the "filetypes" message table using the same hash key.
Categories
We often have static data that should be sorted into categories. This includes entities like tools, commands, shaders, other plug-in classes, and so on. The category system allows us to generically classify statically-named entries into a hierarchy.
Config Management
All categories are stored in the config using a format similar to message tables. The "Category" hash identifies the category itself with a key. The hash's children are all "C", with the key being the specific data to be categoried. The value is a slash-delimited path representing the position of the entry in the hierarchy. Since the se are hashes, the entry names are case-sensitive.
(18) SDK: Category Example
<atom type="Categories"> <hash type="Category" key="Commands"> <hash type="C" key="select.more">ui/select</hash> </hash> </atom>
The usernames for the entries in the hiearchy are found in message tables named through the category key and the string ":category", as shown below. The message lookups are the path component itself. Remember that you need to provide usernames for each part of the path.
(19) SDK: Category Path Messages Example
<atom type="Messages"> <hash type="Table" key="Commands:category.en_US"> <hash type="T" key="ui">UI</hash> <hash type="T" key="ui/select">Select</hash> </hash> </atom>
There is also a special "(hidden)" category. Anything put into this category won't be represented at all in the category hierarcy, and is effectively hidden.
Auto-Save Events
Auto-saving is triggered periodically based on user preferences, modified by when the system believes an autosave is safe to perform. Other clients can get in on this by listening to the auto-save event port. There is only one event, AutoSaveNow(), at which point the client can safely perform an auto-save if applicable. This event is sent after the nexus has done its own auto-saving.
(20) SDK: LXa_AUTOSAVELISTENER, etc. defines
#define LXa_AUTOSAVELISTENER "autosavelistener" #define LXu_AUTOSAVELISTENER "04f41d4e-7267-430e-81f4-a89896bf746c"
(21) SDK: AutoSaveListener::AutoSaveNow
LXxMETHOD( LxResult, AutoSaveNow) ( LXtObjectID self);
Empty AutoSaveListener Python user class.
(22) PY: empty AutoSaveListener user class
pass