This blog will introduce three main new topics:
| 1. | Defining custom Legato functions |
| 2. | Running scripts on GoFiler startup |
| 3. | Running menu functions with Legato |
| | |
The example below runs the normal Polish Table menu function with some default settings, but it changes the column style to a single column instead of using gutter columns.
Our Sample Script
// GoFiler Legato Script - Polish No Gutters
// ------------------------------------------
//
// Rev 9/20/2016
//
// (c) 2016 Novaworks, LLC -- All rights reserved.
//
//
// Notes:
// - None.
int setup ();
int run (int f_id, string mode);
// set up the script and add it to the menu bar. runs on startup automatically.
int setup() {
string fnScript;
string item[10];
int rc;
// define the menu options, like name and description
item["Code"] = "POLISH_TABLE_I";
item["MenuText"] = "Polish No Gutters";
item["Description"] = "Polish Table\r\rPolish an HTML table with no gutters.";
// make sure this option isn't already on the menu
rc = MenuFindFunctionID(item["Code"]);
if (IsNotError(rc)) {
return ERROR_NONE;
}
// add the menu option to the menu
rc = MenuAddFunction(item);
// if we had an error, quit.
if (IsError(rc)) {
return ERROR_NONE;
}
// get the name of this script file
fnScript = GetScriptFilename();
// hook the "run" function of this script to execute when the new menu
// option is activated.
MenuSetHook(item["Code"], fnScript, "run");
return ERROR_NONE;
}
// runs every time the menu button is clicked on
int run(int f_id, string mode){
// the options for our function to run.
string opts;
// only run this on preprocess to avoid running multiple times.
if (mode!="preprocess"){
return ERROR_NONE;
}
// define the options for the polish function
opts = "Defaults: True;";
opts+= "ColumnMode: Single;";
opts+= "NoQuery: True;";
// run the polish function
RunMenuFunction("TABLE_CLEANUP_POLISH",opts);
return ERROR_NONE;
}
// every script requires a main function when it's run from the IDE
int main() {
// make sure the function is set up.
setup();
return ERROR_NONE;
}
Defining Legato User Functions
Whenever you write a Legato function, you must define it before it can be used in a script. The one exception to this is the function main, which must exist in every Legato script. The declaration of the function is known as the function signature. This specifies the output of the function (its return code), the name of the function, and what sorts of data it takes as input parameters. In the example script, note the following lines:
int setup ();
int run (int f_id, string mode);
The first line declares the function called setup, which has the return type of int. The function takes nothing as input. Below it is the function run, which also returns an int, but takes two parameters as input: an int and a string. Once a function is declared, it can be defined later in the script.
Running Scripts on GoFiler Startup
In the previous blog post, we mentioned that Legato scripts can have an extension of .ms or .ls. If the script has a .ms extension, when it is placed into your Scripts folder, it is run when the application starts. During its initialization, GoFiler looks within the .ms file for a user function called setup. If that function is declared and defined, it executes it as part of GoFiler’s startup. If you want to create a menu item for a function, it needs to be defined in a .ms file in GoFiler’s Scripts folder, and it needs to have a setup function that actually adds it to a menu.
Adding New Functions To the Menu With Legato
All GoFiler menu functions have a unique menu code associated with them. For example, the Polish Table menu function uses the code TABLE_CLEANUP_POLISH. Whenever you add new user functions to GoFiler’s menus with Legato, the user function you want to add to the software must also have a unique code.
In the example, we create a new function with the code POLISH_TABLE_I. If that conflicts with a function that has the same code, it won’t be added to the software. We’ll get into the details of how you choose a code in the Script Walkthrough below.
The SDK function RunMenuFunction takes two string parameters (the function code we talked about and a string of options for the function) and executes it. Several of the available menu functions that can be run through Legato are documented in the Appendix A section of the Legato SDK in GoFiler.
Script Walkthrough
Before we begin, you should note that GoFiler has an application setting that locks files from being read when they are open in GoFiler. If you are going to run scripts from the IDE that create menu hooks, as in this example script, you must either turn off this setting in the Application Preferences dialog or close the script file after running it.
Our example script consists of three user functions: setup, run, and main. Because the script is a .ms file, its setup function is run on application startup. Inside the setup function, we define a string for the name of the script file, a string array for the function parameters, and an int for a return code (we use rc as an abbreviation for return code frequently when the return code is an integer). The string array has its three parameters set: the function code, the menu text we want to add to the menu, and the description of the function we’re adding which is visible in the status bar of the application. Notice that we’re using strings as keys for the array. Legato syntax allows for string keys or integers for the array index. In this case, you want to use string keys because the function to which the array is being passed is expecting these keys to be set this way.
item["Code"] = "POLISH_TABLE_I";
item["MenuText"] = "Polish No Gutters";
item["Description"] = "Polish Table\r\rPolish an HTML table with no gutters.";
After defining these options, we run the MenuFindFunctionID SDK function to check to see if the menu code is already in use.
rc = MenuFindFunctionID(item["Code"]);
if (IsNotError(rc)) {
return ERROR_NONE;
}
If a function with a matching code is not found, rc will be set to an error. The error means that the menu code is available! So if we check rc afterwards and find it’s not an error, that means the menu code already exists, and we have to exit our script because we cannot have duplicate codes.
If the code isn’t already in use, we can run the MenuAddFunction SDK function to add our menu item to the menu. By default, the MenuAddFunction function will add our menu item to the Tools menu in the Extensions Toolset of the File Ribbon, but we could use the class key inside the parameters array to add it to any menu on any ribbon. In this script, we’re going to use the default value. In Legato, default values are often used when you don’t define a parameter. You can always check the Legato SDK for a function to see which parameters are required and which are optional.
rc = MenuAddFunction(item);
// if we had an error, quit.
if (IsError(rc)) {
return ERROR_NONE;
}
Once the item is on the menu, we need to “hook” our user function to it. We can get the name of our script and store it in the string fnScript. Then we can use the MenuSetHook SDK function to hook the script to the menu item.
fnScript = GetScriptFilename();
// hook the "run" function of this script to execute when the new menu
// option is activated.
MenuSetHook(item["Code"], fnScript, "run");
return ERROR_NONE;
}
The SDK function SetMenuHook takes the code we created for this new menu item and attaches this file’s run user function. That allows our new function to be executed whenever the menu item is activated.
The run user function is the one that actually does the work of the script.
int run(int f_id, string mode){
// the options for our function to run.
string opts;
// only run this on preprocess to avoid running multiple times.
if (mode!="preprocess"){
return ERROR_NONE;
}
// define the options for the polish function
opts = "Defaults: True;";
opts+= "ColumnMode: Single;";
opts+= "NoQuery: True;";
// run the polish function
RunMenuFunction("TABLE_CLEANUP_POLISH",opts);
return ERROR_NONE;
}
Its two parameters, f_id and mode, are both sent by GoFiler when the menu item is pressed. The f_id is simply the ID of the menu item, but the mode is a very important thing to check. When you click the menu item, GoFiler is actually going to execute the run function twice, once in mode “preprocess” and once again in mode “postprocess”. This is useful if the order in which things happen in the function is important, or you want to perform cleanup operations (to remove temporary files, etc.) after the function has run. In this case, we only need the function to run once, so if the function isn’t in preprocess mode, it just returns without an error. Otherwise the function would polish the table twice.
The last function within the run function is the RunMenuFunction SDK function, which executes the Polish Table operation. The string options defined in the run function are a list of parameters to set how the polish operation will behave. For more information on what options can be used, check Appendix A of the Legato SDK.
The function main is required in all Legato scripts and is run whenever the script is executed from the IDE. In this case, we have it running the setup function to make sure our function gets added to the menu, but the main function doesn’t really have to do anything other than return an integer, since the setup and run functions do all of the real work.
And that’s how we can add a menu item to GoFiler.
Steven Horowitz has been working for Novaworks for over five years as a technical expert with a focus on EDGAR HTML and XBRL. Since the creation of the Legato language in 2015, Steven has been developing scripts to improve the GoFiler user experience. He is currently working toward a Bachelor of Sciences in Software Engineering at RIT and MCC. |
Additional Resources
Novaworks’ Legato Resources
Legato Script Developers LinkedIn Group
Primer: An Introduction to Legato