Triggers: user-defined

Quick Macros can be extended with new trigger types. This topic gives information for programmers about creating user-defined triggers. These triggers can be implemented completely in user-defined functions, or partially in dlls or COM components. They can be added to the Properties dialog, so that other QM users can simply download, import and use the triggers like QM intrinsic triggers.


There are various Windows API functions that allow receiving notifications about various events. Information about these functions can be found in MSDN Library. Other way to create a trigger - periodically check some condition, say every 1 second, using rep and wait. Also, on the Internet you can find COM components that can be used to receive various system notifications in form of COM events. Many notifications are provided by WMI.


The main trigger function should start when QM file is loaded and run continuously. It, for example, can have "QM file loaded" trigger ("Run synchronously" must be unchecked), or started from the trigger management function (read below) using mac.


When your trigger engine is about to start a macro, it should call the dis function to ensure that the macro and user-defined triggers are enabled. Use, for example, this code: if(dis(macro) or dis&16) ret.


To implement some triggers, must be used dlls, because the function that receives notifications is called in context of other processes. But most notifications can be received using only QM user-defined functions. QM language has all power to create any trigger that does not require to be implemented in a dll or driver.


Integrating with QM


Your trigger will be easier to use if you integrate it with QM. To integrate your trigger engine with QM, create a user-defined function ("trigger management function"), and assign it "Trigger management" trigger. QM will call the function to communicate with your trigger engine.


At first, about the trigger string. The format of the trigger string that is displayed in the Trigger field on the toolbar is:


^Trigger_type trigger data


Here Trigger_type is the name of the trigger management function, and trigger data is some trigger-defined string. The trigger data is optional and is used only by the trigger engine.


The trigger management function must begin with:


function# unused1 unused2 action UDTRIGGER&p


The first two parameters are always 0.

action - numeric value that defines the purpose for which the function is called.

p - variable of UDTRIGGER type that is used to exchange various information. Its members that are used with different actions are described below.


QM calls the trigger management function when:


1. When this trigger type is selected in the Properties dialog.

action - 1.

p.iid - QM item id.

p.hwnd - handle of the "Trigger" property page of the Properties dialog.

p.tdata - old trigger data string. It is empty (s.lpstr=0) if the macro does not have a trigger of this type. It is "" if the trigger does not have a trigger data.

The function should create and initialize trigger's property page, and return its window handle. The property page must be modeless dialog, child window of p.hwnd. Use ShowDialog(x x x p.hwnd 1 WS_CHILD). If the trigger engine does not provide a property page, the function must just return 0. Then QM will provide default property page to enter trigger data string.


2. When the Properties dialog is being closed (OK) while this trigger type is selected. Is called only if the trigger engine provides a property page.

action - 2.

p.iid - QM item id.

p.hwnd - handle of the property page that was returned in action 1.

p.tdata - empty.

The function must collect data from controls in the property page, format new trigger data string (if any) and store it into p.tdata. Should not apply the changes at this moment. Must return 0 if trigger is set, 1 if trigger is not set, or 2 to not close the Properties dialog.


3. When new trigger string must be validated. Is called after a trigger is added or changed.

action - 3.

p.iid - QM item id.

p.hwnd - 0.

p.tdata - new trigger data string.

The function must validate the new trigger data string. It can modify the string if needed. Should not apply the changes at this moment. Must return 1 if the string is valid, or 0 otherwise.


4. When all triggers of this type must be applied. Is called when file is opened, a trigger is added/changed/deleted, macro(s) deleted, and in other cases.

action - 4.

p.iids - array of ids of all macros that have a trigger of this trigger type.

p.niids - number of elements in the array.

The function must remove old triggers and apply triggers to the macros whose ids are in the p.iids array. For example, if the trigger engine uses internal trigger tables, it must [re]create the tables. To get trigger strings, use str.getmacro or qmitem.


5. When QM needs to display icon of this trigger type.

action - 5.

The function should return icon handle or 0. The icon is destroyed by QM.


6. When the user requests help for this trigger type.

action - 6.

The function should provide help. For example, it can display a message box, or open a web page. Should return 0.


7, 8. When QM or an item is enabled or disabled.

action - 7 if enabled, 8 if disabled.

p.iid - QM item id or 0.

This notification is rarely useful. The function can just return 0. Return 1 if trigger tables have to be recreated; then QM will call this function (later) with action=4.


Only actions 3 and 4 must be implemented. For other actions, the function can just return 0.


The function is always called in QM main thread. If it starts the trigger engine, it must create new thread for the engine (use mac). Never use QM thread for user-defined triggers, because it can make QM unstable.


The function is not called when it itself is changed in some way (disabled, enabled, deleted, trigger added, removed or changed).


If the function is disabled, it is not called, and does not appear in the Properties dialog. But triggers of that type are not automatically disabled.


You can find a sample user-defined trigger in the forum.