This week we are going to move forward with our creation of a tool to allow us to fill out a Form 8-K cover page. Last blog we started our process by creating a script that showed how to use the HTML templates to create a custom template that runs a Legato script. This week we are going to expand on that concept, redesigning the script to be able to work with multiple form types and adding in the ability to add expandable lists into each form.
Friday, June 21. 2019
LDC #140: Using Legato to Ease the Creation of 8-K Cover Pages: Part 2
There are two large changes made to the script. Let’s talk about those before diving into the code.
First, we must make a change to allow the form selection to become extensible. We have added a new global table to the script
where we will store all of the information for the dialog instead of it being hard coded into the dialog like last time. This
table has a bunch of columns that have all the different information that we need to store: the element name, the element label,
the element value, the type of information being stored, and a column of additional information if the row is storing repeatable
data. For each form type that we want to add to the script, we can change the information that is added to the table. When we
call the dialog, it now reads through the table in order to populate the information that we need.
Secondly, the dialog code has been made to be much smarter. It now parses through the table when initialized, adding rows based on the number of rows in the table. In addition, we have added logic to the dialog to understand when sets of data are being requested. In the case of the Form 8-K cover page, we can have any number of securities added to it, which consist of a name, trading symbol, and the name of the exchange on which the company is traded. Since we can only have one list on a cover page, we only have global variables for that one list. If you wanted to, you could add the logic to allow multiple lists, but this ends up overcomplicating our blog script and is out of scope for what we want to achieve.
Finally, before we look at the code itself, let’s discuss the changes we made to the HTML template.
There’s only one, but it’s important. We added in a group marker where we will place the 12(b) or 12(g) security information.
Each security type has a different table, so we mark the location of the table with our own flag “fksecgroup”. This
allows us to add the table corresponding to the security type that the user picks out, so we can add as many rows as the user
fills out or just add a blank table row. This makes it easy to modify after the wizard is done executing.
Now let’s look at the full code for this weeks script:
/***************************************************************************************************************** Cover Page From Template ------------------------ Revision: 05-31-19 JCK Initial creation 06-14-19 JCK Addition of security groups Notes: - Part 2 Extensible/Repeatable groups (c) 2019 Novaworks, LLC. All Rights Reserved. *****************************************************************************************************************/ #define COLUMN_ELEMENT 0 #define COLUMN_VALUE 1 #define COLUMN_FORMAT 2 #define COLUMN_LABEL 3 #define COLUMN_SECURITY 4 #define COLUMN_SIZE 0 #define COLUMN_NUMBER 1 #define COLUMN_SLABEL 2 #define COLUMN_START 3 string info[][]; string content; string formtype; string securities[]; int securitiesstart, securitiesend; boolean twelvebsec; string create_content (string name, string options); string run8k (boolean fromHook); int main (); int XDX_load (); int XDX_ok (); void refresh_securities (); int main() { run8k(false); return ERROR_NONE; } string create_content ( string name, string options ) { return run8k(true); } string run8k(boolean fromHook) { formtype = "8-K"; twelvebsec = true; ArrayClear(info); info[0][COLUMN_ELEMENT] = "DocumentPeriodEndDate"; info[1][COLUMN_ELEMENT] = "EntityFileNumber"; info[2][COLUMN_ELEMENT] = "EntityRegistrantName"; info[3][COLUMN_ELEMENT] = "EntityIncorporationStateCountryCode"; info[4][COLUMN_ELEMENT] = "EntityTaxIdentifcationNumber"; info[5][COLUMN_ELEMENT] = "EntityAddressAddressLine1"; info[6][COLUMN_ELEMENT] = "EntityAddressAddressLine2"; info[7][COLUMN_ELEMENT] = "EntityAddressAddressLine3"; info[8][COLUMN_ELEMENT] = "EntityAddressCityOrTown"; info[9][COLUMN_ELEMENT] = "EntityAddressStateOrProvince"; info[10][COLUMN_ELEMENT] = "EntityAddressCountry"; info[11][COLUMN_ELEMENT] = "EntityAddressPostalZipCode"; info[12][COLUMN_ELEMENT] = "LocalPhoneNumber"; info[13][COLUMN_ELEMENT] = "EntityInformationFormerLegalOrRegisteredName"; info[14][COLUMN_ELEMENT] = "WrittenCommunications"; info[15][COLUMN_ELEMENT] = "SolicitingMaterial"; info[16][COLUMN_ELEMENT] = "PreCommencementTenderOffer"; info[17][COLUMN_ELEMENT] = "PreCommencementIssuerTenderOffer"; info[18][COLUMN_ELEMENT] = "EntityEmergingGrowthCompany"; info[19][COLUMN_ELEMENT] = "EntityExTransitionPeriod"; info[20][COLUMN_ELEMENT] = "fksectype"; info[21][COLUMN_ELEMENT] = "fksecgroup"; info[0][COLUMN_VALUE] = ""; info[1][COLUMN_VALUE] = ""; info[2][COLUMN_VALUE] = ""; info[3][COLUMN_VALUE] = ""; info[4][COLUMN_VALUE] = ""; info[5][COLUMN_VALUE] = ""; info[6][COLUMN_VALUE] = ""; info[7][COLUMN_VALUE] = ""; info[8][COLUMN_VALUE] = ""; info[9][COLUMN_VALUE] = ""; info[10][COLUMN_VALUE] = ""; info[11][COLUMN_VALUE] = ""; info[12][COLUMN_VALUE] = ""; info[13][COLUMN_VALUE] = ""; info[14][COLUMN_VALUE] = ""; info[15][COLUMN_VALUE] = ""; info[16][COLUMN_VALUE] = ""; info[17][COLUMN_VALUE] = ""; info[18][COLUMN_VALUE] = ""; info[19][COLUMN_VALUE] = ""; info[20][COLUMN_VALUE] = ""; info[21][COLUMN_VALUE] = ""; info[0][COLUMN_FORMAT] = "string"; info[1][COLUMN_FORMAT] = "string"; info[2][COLUMN_FORMAT] = "string"; info[3][COLUMN_FORMAT] = "string"; info[4][COLUMN_FORMAT] = "string"; info[5][COLUMN_FORMAT] = "string"; info[6][COLUMN_FORMAT] = "string"; info[7][COLUMN_FORMAT] = "string"; info[8][COLUMN_FORMAT] = "string"; info[9][COLUMN_FORMAT] = "string"; info[10][COLUMN_FORMAT] = "string"; info[11][COLUMN_FORMAT] = "string"; info[12][COLUMN_FORMAT] = "string"; info[13][COLUMN_FORMAT] = "string"; info[14][COLUMN_FORMAT] = "checkbox"; info[15][COLUMN_FORMAT] = "checkbox"; info[16][COLUMN_FORMAT] = "checkbox"; info[17][COLUMN_FORMAT] = "checkbox"; info[18][COLUMN_FORMAT] = "checkbox"; info[19][COLUMN_FORMAT] = "checkbox"; info[20][COLUMN_FORMAT] = "checkbox"; info[21][COLUMN_FORMAT] = "securities"; info[0][COLUMN_LABEL] = "Date Of Report"; info[1][COLUMN_LABEL] = "Commission File Number"; info[2][COLUMN_LABEL] = "Exact Name of Registrant"; info[3][COLUMN_LABEL] = "State or Other Jurisdiction of Organization"; info[4][COLUMN_LABEL] = "I.R.S. Employer Identification No."; info[5][COLUMN_LABEL] = "Address Line 1"; info[6][COLUMN_LABEL] = "Address Line 2"; info[7][COLUMN_LABEL] = "Address Line 3"; info[8][COLUMN_LABEL] = "City or Town"; info[9][COLUMN_LABEL] = "State or Province"; info[10][COLUMN_LABEL] = "Country of Executive Offices"; info[11][COLUMN_LABEL] = "Zip Code"; info[12][COLUMN_LABEL] = "Telephone Number"; info[13][COLUMN_LABEL] = "\"Former Name, if Changed\""; info[14][COLUMN_LABEL] = "Written communications pursuant to Rule 425 under the Securities Act"; info[15][COLUMN_LABEL] = "Soliciting material pursuant to Rule 14a-12 under the Exchange Act"; info[16][COLUMN_LABEL] = "Pre-commencement communications pursuant to Rule 14d-2(b) under the Exchange Act"; info[17][COLUMN_LABEL] = "Pre-commencement communications pursuant to Rule 13e-4(c) under the Exchange Act"; info[18][COLUMN_LABEL] = "Emerging growth company"; info[19][COLUMN_LABEL] = "Elected Not to Use Extended Transition Period"; info[20][COLUMN_LABEL] = "Check box if 12(g) Security"; info[21][COLUMN_LABEL] = "Title of Class, Trading Symbol, Name of Exchange"; info[21][COLUMN_SECURITY]= "3, 20, SecurityTitle, TradingSymbol, SecurityExchangeName"; content = ""; string filename; if (fromHook == false) { filename = BrowseSaveFile("Select 8-K Save Location", "HTML Files (*.htm, *.html)|*.htm;*.html"); if (filename == "") { return ""; } if (GetExtension(filename) != ".htm" && GetExtension(filename) != ".html" && filename != "") { filename += ".htm"; } } int rc; //Start Dialog rc = DialogBox("XDXTemplateWizDlg", "XDX_"); if (rc == ERROR_CANCEL) { return "ERROR_CANCEL"; } if (fromHook == false) { int size, count; size = ArrayGetAxisDepth(info, AXIS_ROW); count = 0; while (count < size) { AddMessage("%20s : %s", info[count][COLUMN_LABEL], info[count][COLUMN_VALUE]); count++; } } content = FileToString(GetScriptFolder() + "8ktemplate.htm"); if (content == "") { MessageBox('x', "Cannot find template file.\r\n\r\nFile should be saved in the same folder as the script."); return "ERROR_CANCEL"; } content = ReplaceInString(content, "##&DocumentPeriodEndDate;##", info[FindInTable(info, "DocumentPeriodEndDate", COLUMN_ELEMENT)][COLUMN_VALUE]); content = ReplaceInString(content, "##&EntityFileNumber;##", info[FindInTable(info, "EntityFileNumber", COLUMN_ELEMENT)][COLUMN_VALUE]); if (info[FindInTable(info, "EntityRegistrantName", COLUMN_ELEMENT)][COLUMN_VALUE] != "") { content = ReplaceInString(content, "##&EntityRegistrantName;##", info[FindInTable(info, "EntityRegistrantName", COLUMN_ELEMENT)][COLUMN_VALUE]); } else { content = ReplaceInString(content, "##&EntityRegistrantName;##", " "); } if (info[FindInTable(info, "EntityIncorporationStateCountryCode", COLUMN_ELEMENT)][COLUMN_VALUE] != "") { content = ReplaceInString(content, "##&EntityIncorporationStateCountryCode;##", info[FindInTable(info, "EntityIncorporationStateCountryCode", COLUMN_ELEMENT)][COLUMN_VALUE]); } else { content = ReplaceInString(content, "##&EntityIncorporationStateCountryCode;##", " "); } if (info[FindInTable(info, "EntityTaxIdentifcationNumber", COLUMN_ELEMENT)][COLUMN_VALUE] != "") { content = ReplaceInString(content, "##&EntityTaxIdentifcationNumber;##", info[FindInTable(info, "EntityTaxIdentifcationNumber", COLUMN_ELEMENT)][COLUMN_VALUE]); } else { content = ReplaceInString(content, "##&EntityTaxIdentifcationNumber;##", " "); } content = ReplaceInString(content, "##&EntityAddressAddressLine1;##", info[FindInTable(info, "EntityAddressAddressLine1", COLUMN_ELEMENT)][COLUMN_VALUE]); if (info[FindInTable(info, "EntityAddressAddressLine2", COLUMN_ELEMENT)][COLUMN_VALUE] != ""){ content = ReplaceInString(content, "##&EntityAddressAddressLine2;##", "<P STYLE=\"font-size: 10pt; text-align: center; margin-top: 12pt; margin-bottom: 0pt\"><B>" + info[FindInTable(info, "EntityAddressAddressLine2", COLUMN_ELEMENT)][COLUMN_VALUE] + "</B></P>"); } else { content = ReplaceInString(content, "##&EntityAddressAddressLine2;##", ""); } if (info[FindInTable(info, "EntityAddressAddressLine3", COLUMN_ELEMENT)][COLUMN_VALUE] != "") { content = ReplaceInString(content, "##&EntityAddressAddressLine3;##", "<P STYLE=\"font-size: 10pt; text-align: center; margin-top: 12pt; margin-bottom: 0pt\"><B>" + info[FindInTable(info, "EntityAddressAddressLine3", COLUMN_ELEMENT)][COLUMN_VALUE] + "</B></P>"); } else { content = ReplaceInString(content, "##&EntityAddressAddressLine3;##", ""); } if (info[FindInTable(info, "EntityAddressCityOrTown", COLUMN_ELEMENT)][COLUMN_VALUE] == "") { if (info[FindInTable(info, "EntityAddressStateOrProvince", COLUMN_ELEMENT)][COLUMN_VALUE] == "") { if (info[FindInTable(info, "EntityAddressCountry", COLUMN_ELEMENT)][COLUMN_VALUE] == "") { content = ReplaceInString(content, "##&EntityAddressCountry;##", " "); } } } content = ReplaceInString(content, "##&EntityAddressCityOrTown;##", info[FindInTable(info, "EntityAddressCityOrTown", COLUMN_ELEMENT)][COLUMN_VALUE]); content = ReplaceInString(content, "##&EntityAddressStateOrProvince;##", info[FindInTable(info, "EntityAddressStateOrProvince", COLUMN_ELEMENT)][COLUMN_VALUE]); content = ReplaceInString(content, "##&EntityAddressCountry;##", info[FindInTable(info, "EntityAddressCountry", COLUMN_ELEMENT)][COLUMN_VALUE]); content = ReplaceInString(content, "##&EntityAddressPostalZipCode;##", info[FindInTable(info, "EntityAddressPostalZipCode", COLUMN_ELEMENT)][COLUMN_VALUE]); if (info[FindInTable(info, "LocalPhoneNumber", COLUMN_ELEMENT)][COLUMN_VALUE] != "") { content = ReplaceInString(content, "##&LocalPhoneNumber;##", info[FindInTable(info, "LocalPhoneNumber", COLUMN_ELEMENT)][COLUMN_VALUE]); } else { content = ReplaceInString(content, "##&LocalPhoneNumber;##", " "); } if (info[FindInTable(info, "EntityInformationFormerLegalOrRegisteredName", COLUMN_ELEMENT)][COLUMN_VALUE] != "") { content = ReplaceInString(content, "##&EntityInformationFormerLegalOrRegisteredName;##", info[FindInTable(info, "EntityInformationFormerLegalOrRegisteredName", COLUMN_ELEMENT)][COLUMN_VALUE]); } else { content = ReplaceInString(content, "##&EntityInformationFormerLegalOrRegisteredName;##", " "); } if (IsTrue(info[FindInTable(info, "PreCommencementIssuerTenderOffer", COLUMN_ELEMENT)][COLUMN_VALUE])) { content = ReplaceInString(content, "##&PreCommencementIssuerTenderOffer;##", "☒"); } else { content = ReplaceInString(content, "##&PreCommencementIssuerTenderOffer;##", "☐"); } if (IsTrue(info[FindInTable(info, "PreCommencementTenderOffer", COLUMN_ELEMENT)][COLUMN_VALUE])) { content = ReplaceInString(content, "##&PreCommencementTenderOffer;##", "☒"); } else { content = ReplaceInString(content, "##&PreCommencementTenderOffer;##", "☐"); } if (IsTrue(info[FindInTable(info, "SolicitingMaterial", COLUMN_ELEMENT)][COLUMN_VALUE])) { content = ReplaceInString(content, "##&SolicitingMaterial;##", "☒"); } else { content = ReplaceInString(content, "##&SolicitingMaterial;##", "☐"); } if (IsTrue(info[FindInTable(info, "WrittenCommunications", COLUMN_ELEMENT)][COLUMN_VALUE])) { content = ReplaceInString(content, "##&WrittenCommunications;##", "☒"); } else { content = ReplaceInString(content, "##&WrittenCommunications;##", "☐"); } if (IsTrue(info[FindInTable(info, "EntityEmergingGrowthCompany", COLUMN_ELEMENT)][COLUMN_VALUE])) { content = ReplaceInString(content, "##&EntityEmergingGrowthCompany;##", "☒"); } else { content = ReplaceInString(content, "##&EntityEmergingGrowthCompany;##", "☐"); } if (IsTrue(info[FindInTable(info, "EntityExTransitionPeriod", COLUMN_ELEMENT)][COLUMN_VALUE])) { content = ReplaceInString(content, "##&EntityExTransitionPeriod;##", "☒"); } else { content = ReplaceInString(content, "##&EntityExTransitionPeriod;##", "☐"); } count = 0; string secgroup[]; secgroup = ExplodeString(info[FindInTable(info, "fksecgroup", COLUMN_ELEMENT)][COLUMN_VALUE], ", "); string title, symbol, exname, temp, together; together = ""; if (twelvebsec) { together = "<P STYLE=\"font-size: 10pt; text-align: left; text-indent: 0; margin-top: 6pt; margin-bottom: 6pt\">Securities registered pursuant to Section 12(b) of the Act:</P>\r\n\r\n<TABLE BORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\" STYLE=\"width: 100%\">\r\n<TR STYLE=\"vertical-align: top\">\r\n <TD STYLE=\"font-size: 10pt; text-align: center; margin-top: 6pt; margin-bottom: 6pt; width: 33%; border-bottom: Black 1pt solid\">Title of Class</TD>\r\n <TD STYLE=\"font-size: 10pt; text-align: center; margin-top: 6pt; margin-bottom: 6pt; width: 34%; border-bottom: Black 1pt solid\">Trading Symbol(s)</TD>\r\n <TD STYLE=\"font-size: 10pt; text-align: center; margin-top: 6pt; margin-bottom: 6pt; width: 33%; border-bottom: Black 1pt solid\">Name of Exchange</TD>\r\n</TR>"; } else { //MessageBox("Number of entries: %d", DecimalToInteger(securities[0][COLUMN_NUMBER])); together = "<P STYLE=\"font-size: 10pt; text-align: left; text-indent: 0; margin-top: 6pt; margin-bottom: 6pt\">Securities registered pursuant to Section 12(g) of the Act:</P>\r\n\r\n<TABLE BORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\" STYLE=\"width: 100%\">\r\n<TR STYLE=\"vertical-align: top\">\r\n <TD STYLE=\"font-size: 10pt; text-align: center; margin-top: 6pt; margin-bottom: 6pt; border-bottom: Black 1pt solid\">Title of Class</TD>\r\n</TR>"; } while (count < DecimalToInteger(securities[COLUMN_NUMBER])) { title = secgroup[count*3]; symbol = secgroup[count*3+1]; exname = secgroup[count*3+2]; if (twelvebsec) { temp = "\r\n<TR STYLE=\"vertical-align: top\">\r\n <TD STYLE=\"font-size: 10pt; text-align: center\">##&SecurityTitle;##</TD>\r\n <TD STYLE=\"font-size: 10pt; text-align: center\">##&TradingSymbol;##</TD>\r\n <TD STYLE=\"font-size: 10pt; text-align: center\">##&SecurityExchangeName;##</TD>\r\n</TR>"; } else { temp = "\r\n<TR STYLE=\"vertical-align: top\">\r\n <TD STYLE=\"font-size: 10pt; text-align: center\">##&SecurityTitle;##</TD>\r\n</TR>"; } if (title != "") { temp = ReplaceInString(temp, "##&SecurityTitle;##", title); } else { temp = ReplaceInString(temp, "##&SecurityTitle;##", " "); } if (symbol != "") { temp = ReplaceInString(temp, "##&TradingSymbol;##", symbol); } else { temp = ReplaceInString(temp, "##&TradingSymbol;##", " "); } if (exname != "") { temp = ReplaceInString(temp, "##&SecurityExchangeName;##", exname); } else { temp = ReplaceInString(temp, "##&SecurityExchangeName;##", " "); } if (title != "" || symbol != "" || exname != "" || DecimalToInteger(securities[COLUMN_NUMBER]) == 1) { together += temp; } count++; } together += "\r\n</TABLE>"; content = ReplaceInString(content, "##&fksecgroup;##", together); if (fromHook == false) { StringToFile(content, filename); } return content; } #define PVX_OF_LIST 101 #define XDX_TemplateWizDlg 100 #define PVX_ADD_GROUP 102 #beginresource XDXTemplateWizDlg DIALOGEX 0, 0, 400, 500 STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION CAPTION "Wizard" FONT 8, "MS Shell Dlg" { CONTROL "Properties", -1, "static", SS_LEFT | WS_CHILD | WS_VISIBLE, 4, 4, 40, 8, 0 CONTROL "", -1, "static", SS_ETCHEDFRAME | WS_CHILD | WS_VISIBLE, 40, 9, 350, 1, 0 CONTROL "", PVX_OF_LIST, "data_control", LBS_NOTIFY | WS_VSCROLL | WS_BORDER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 12, 18, 378, 450, 0 CONTROL "Add Group", PVX_ADD_GROUP, "BUTTON", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 15, 476, 50, 14 CONTROL "OK", IDOK, "BUTTON", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 275, 476, 50, 14 CONTROL "Cancel", IDCANCEL, "BUTTON", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 335, 476, 50, 14 } #endresource int XDX_load() { DataControlSetColumnPositions(PVX_OF_LIST, 150, 200); DataControlSetColumnHeadings(PVX_OF_LIST, "Name", "Value"); DataControlSetColumnFlags(PVX_OF_LIST, DS_CC_READ_ONLY); DataControlSetGridMode(PVX_OF_LIST, DCC_GRID_BOTH); DataControlSetLocalEditMode(PVX_OF_LIST, DCC_EDIT_MODE_AUTO); DataControlSetSelectMode(PVX_OF_LIST, DCC_SELECT_MODE_NONE); DataControlSetDefaultRowHeight(PVX_OF_LIST, 18); ControlDisable(PVX_ADD_GROUP); int size, count, ssize, scount, xpos; string sinfo[]; string slabel[]; size = ArrayGetAxisDepth(info, AXIS_ROW); count = 0; while (count < size) { switch (info[count][COLUMN_FORMAT]) { case "checkbox": DataControlAddString(PVX_OF_LIST, info[count][COLUMN_LABEL]); DataControlSetCellType(PVX_OF_LIST, xpos, 1, DS_CF_DISPLAY_CHECKBOX_AUTO); DataControlSetCellState(PVX_OF_LIST, xpos, 0, FALSE); xpos++; break; case "securities": sinfo = ExplodeString(info[count][COLUMN_SECURITY], ", "); slabel = ExplodeString(info[count][COLUMN_LABEL], ", "); ssize = DecimalToInteger(sinfo[0]); securities[COLUMN_SIZE] = FormatString("%d", ssize); securities[COLUMN_NUMBER] = "1"; securities[COLUMN_SLABEL] = info[count][COLUMN_LABEL]; securities[COLUMN_START] = sinfo[1]; scount = 0; securitiesstart = xpos; while (scount < ssize) { DataControlAddString(PVX_OF_LIST, FormatString("%s %d", slabel[scount], securities[COLUMN_NUMBER])); DataControlSetCellState(PVX_OF_LIST, xpos, 0, FALSE); xpos++; scount++; } securitiesend = xpos; break; case "string": DataControlAddString(PVX_OF_LIST, info[count][COLUMN_LABEL]); DataControlSetCellState(PVX_OF_LIST, xpos, 0, FALSE); xpos++; break; } count++; } return ERROR_NONE; } int XDX_ok() { int size, count, posx; size = ArrayGetAxisDepth(info, AXIS_ROW); count = 0; posx = 0; int centries; string together; int numentries; while (count < size) { switch (info[count][COLUMN_FORMAT]) { case "checkbox": info[count][COLUMN_VALUE] = DataControlGetCellText(PVX_OF_LIST, posx, 1); posx++; break; case "securities": numentries = DecimalToInteger(securities[COLUMN_NUMBER])*DecimalToInteger(securities[COLUMN_SIZE]); together = UTFToEntities(DataControlGetCellText(PVX_OF_LIST, posx, 1)); posx++; centries = 1; while (centries < numentries) { together = together + ", " + UTFToEntities(DataControlGetCellText(PVX_OF_LIST, posx, 1)); posx++; centries++; } info[count][COLUMN_VALUE] = together; break; default: info[count][COLUMN_VALUE] = UTFToEntities(DataControlGetCellText(PVX_OF_LIST, posx, 1)); posx++; break; } count++; } return ERROR_NONE; } void XDX_action(int c_id, int c_action) { int pos[]; if (c_action == DCN_EDIT_CHANGE) { pos = DataControlGetCaretPosition(PVX_OF_LIST); if (pos["Row"] == FindInTable(info, "fksectype", COLUMN_ELEMENT)) { if (DataControlGetCellText(PVX_OF_LIST, pos["Row"], 1) == "1") { twelvebsec = false; } else { twelvebsec = true; } refresh_securities(); } return; } if (c_action == DCN_SELECT_CHANGE) { pos = DataControlGetCaretPosition(PVX_OF_LIST); if (pos["Row"] >= securitiesstart && pos["Row"] <= securitiesend) { ControlEnable(PVX_ADD_GROUP); } else { ControlDisable(PVX_ADD_GROUP); } return; } if (c_id == PVX_ADD_GROUP) { int rc; rc = DataControlInsertRow(PVX_OF_LIST, securitiesend, DecimalToInteger(securities[COLUMN_SIZE])); securities[COLUMN_NUMBER] = FormatString("%d", DecimalToInteger(securities[COLUMN_NUMBER])+1); string slabel[]; slabel = ExplodeString(securities[COLUMN_SLABEL], ", "); int count; count = 0; while (count < DecimalToInteger(securities[COLUMN_SIZE])) { DataControlSetCellText(PVX_OF_LIST, securitiesend+count, 0, FormatString("%s %s", slabel[count], securities[COLUMN_NUMBER])); DataControlSetCellState(PVX_OF_LIST, securitiesend+count, 0, FALSE); count++; } securitiesend += DecimalToInteger(securities[COLUMN_SIZE]); refresh_securities(); } } void refresh_securities() { int numentries, centries, posx; numentries = DecimalToInteger(securities[COLUMN_NUMBER])*DecimalToInteger(securities[COLUMN_SIZE]); posx = securitiesstart; centries = 0; if (twelvebsec == false) { while (centries < numentries) { if (centries % 3 != 0) { DataControlSetCellState(PVX_OF_LIST, posx, 1, FALSE); } posx++; centries++; } } else { while (centries < numentries) { if (centries % 3 != 0) { DataControlSetCellState(PVX_OF_LIST, posx, 1, TRUE); } posx++; centries++; } } }
We start with a large section of definitions. These defines are used to clarify the columns that are in the global tables. This ends up making the code much more readable, even though it extends the size of the script. In addition to defining the columns, we also declare our global table and the other global variables that we will use to keep track of information. The info table will be our big table that defines and stores all of our needed information from the user. The securities array will be used by the dialog to keep track of information for any possible lists. The securitiessstart and securitiesend variables manage the position of the securities list inside of the dialog. Our main() and create_content() functions are unchanged from last week, as we still only have the Form 8-K in this wizard.
string run8k(boolean fromHook) { formtype = "8-K"; twelvebsec = true; ArrayClear(info); info[0][COLUMN_ELEMENT] = "DocumentPeriodEndDate"; info[1][COLUMN_ELEMENT] = "EntityFileNumber"; info[2][COLUMN_ELEMENT] = "EntityRegistrantName"; info[3][COLUMN_ELEMENT] = "EntityIncorporationStateCountryCode"; info[4][COLUMN_ELEMENT] = "EntityTaxIdentifcationNumber"; info[5][COLUMN_ELEMENT] = "EntityAddressAddressLine1"; info[6][COLUMN_ELEMENT] = "EntityAddressAddressLine2"; info[7][COLUMN_ELEMENT] = "EntityAddressAddressLine3"; info[8][COLUMN_ELEMENT] = "EntityAddressCityOrTown"; info[9][COLUMN_ELEMENT] = "EntityAddressStateOrProvince"; info[10][COLUMN_ELEMENT] = "EntityAddressCountry"; info[11][COLUMN_ELEMENT] = "EntityAddressPostalZipCode"; info[12][COLUMN_ELEMENT] = "LocalPhoneNumber"; info[13][COLUMN_ELEMENT] = "EntityInformationFormerLegalOrRegisteredName"; info[14][COLUMN_ELEMENT] = "WrittenCommunications"; info[15][COLUMN_ELEMENT] = "SolicitingMaterial"; info[16][COLUMN_ELEMENT] = "PreCommencementTenderOffer"; info[17][COLUMN_ELEMENT] = "PreCommencementIssuerTenderOffer"; info[18][COLUMN_ELEMENT] = "EntityEmergingGrowthCompany"; info[19][COLUMN_ELEMENT] = "EntityExTransitionPeriod"; info[20][COLUMN_ELEMENT] = "fksectype"; info[21][COLUMN_ELEMENT] = "fksecgroup"; info[0][COLUMN_VALUE] = ""; info[1][COLUMN_VALUE] = ""; info[2][COLUMN_VALUE] = ""; info[3][COLUMN_VALUE] = ""; info[4][COLUMN_VALUE] = ""; info[5][COLUMN_VALUE] = ""; info[6][COLUMN_VALUE] = ""; info[7][COLUMN_VALUE] = ""; info[8][COLUMN_VALUE] = ""; info[9][COLUMN_VALUE] = ""; info[10][COLUMN_VALUE] = ""; info[11][COLUMN_VALUE] = ""; info[12][COLUMN_VALUE] = ""; info[13][COLUMN_VALUE] = ""; info[14][COLUMN_VALUE] = ""; info[15][COLUMN_VALUE] = ""; info[16][COLUMN_VALUE] = ""; info[17][COLUMN_VALUE] = ""; info[18][COLUMN_VALUE] = ""; info[19][COLUMN_VALUE] = ""; info[20][COLUMN_VALUE] = ""; info[21][COLUMN_VALUE] = ""; info[0][COLUMN_FORMAT] = "string"; info[1][COLUMN_FORMAT] = "string"; info[2][COLUMN_FORMAT] = "string"; info[3][COLUMN_FORMAT] = "string"; info[4][COLUMN_FORMAT] = "string"; info[5][COLUMN_FORMAT] = "string"; info[6][COLUMN_FORMAT] = "string"; info[7][COLUMN_FORMAT] = "string"; info[8][COLUMN_FORMAT] = "string"; info[9][COLUMN_FORMAT] = "string"; info[10][COLUMN_FORMAT] = "string"; info[11][COLUMN_FORMAT] = "string"; info[12][COLUMN_FORMAT] = "string"; info[13][COLUMN_FORMAT] = "string"; info[14][COLUMN_FORMAT] = "checkbox"; info[15][COLUMN_FORMAT] = "checkbox"; info[16][COLUMN_FORMAT] = "checkbox"; info[17][COLUMN_FORMAT] = "checkbox"; info[18][COLUMN_FORMAT] = "checkbox"; info[19][COLUMN_FORMAT] = "checkbox"; info[20][COLUMN_FORMAT] = "checkbox"; info[21][COLUMN_FORMAT] = "securities"; info[0][COLUMN_LABEL] = "Date Of Report"; info[1][COLUMN_LABEL] = "Commission File Number"; info[2][COLUMN_LABEL] = "Exact Name of Registrant"; info[3][COLUMN_LABEL] = "State or Other Jurisdiction of Organization"; info[4][COLUMN_LABEL] = "I.R.S. Employer Identification No."; info[5][COLUMN_LABEL] = "Address Line 1"; info[6][COLUMN_LABEL] = "Address Line 2"; info[7][COLUMN_LABEL] = "Address Line 3"; info[8][COLUMN_LABEL] = "City or Town"; info[9][COLUMN_LABEL] = "State or Province"; info[10][COLUMN_LABEL] = "Country of Executive Offices"; info[11][COLUMN_LABEL] = "Zip Code"; info[12][COLUMN_LABEL] = "Telephone Number"; info[13][COLUMN_LABEL] = "\"Former Name, if Changed\""; info[14][COLUMN_LABEL] = "Written communications pursuant to Rule 425 under the Securities Act"; info[15][COLUMN_LABEL] = "Soliciting material pursuant to Rule 14a-12 under the Exchange Act"; info[16][COLUMN_LABEL] = "Pre-commencement communications pursuant to Rule 14d-2(b) under the Exchange Act"; info[17][COLUMN_LABEL] = "Pre-commencement communications pursuant to Rule 13e-4(c) under the Exchange Act"; info[18][COLUMN_LABEL] = "Emerging growth company"; info[19][COLUMN_LABEL] = "Elected Not to Use Extended Transition Period"; info[20][COLUMN_LABEL] = "Check box if 12(g) Security"; info[21][COLUMN_LABEL] = "Title of Class, Trading Symbol, Name of Exchange"; info[21][COLUMN_SECURITY]= "3, 20, SecurityTitle, TradingSymbol, SecurityExchangeName"; content = ""; string filename; if (fromHook == false) { filename = BrowseSaveFile("Select 8-K Save Location", "HTML Files (*.htm, *.html)|*.htm;*.html"); if (filename == "") { return ""; } if (GetExtension(filename) != ".htm" && GetExtension(filename) != ".html" && filename != "") { filename += ".htm"; } } int rc; //Start Dialog rc = DialogBox("XDXTemplateWizDlg", "XDX_"); if (rc == ERROR_CANCEL) { return "ERROR_CANCEL"; }
Our run8k() function has undergone a large overhaul as now we have to declare all of our information for the dialog to use. We go through and add a row to the info table for each item on the HTML template that we are replacing. We define the item name, set the value to be empty, set the format, set the label, and then for our single list, we add some additional information used to populate the dialog. The formats that we use are “string”, “checkbox”, and “securities.” String elements ask for a single string, checkbox elements will have a checkbox displayed, and securities elements end up requesting multiple strings that will be combined together into a single replace function on our template. Securities elements have an additional column of information: a comma separated string. This string has these values: first the size, second the row of the info table in which the securities list is located, and finally all of the elements in each security.
After we fill the info table, we go into the same logic as the last version: we ask for a file name using BrowseSaveFile() if we are running from the IDE, and then we go into the dialog. The only difference here is that we check the return code from the dialog function. If the code is ERROR_CANCEL, we return the string “ERROR_CANCEL”. This string tells GoFiler that the user cancelled the wizard, and GoFiler will close the Page View window that was opened and would otherwise been populated with our return string.
#define PVX_OF_LIST 101 #define XDX_TemplateWizDlg 100 #define PVX_ADD_GROUP 102 #beginresource XDXTemplateWizDlg DIALOGEX 0, 0, 400, 500 STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION CAPTION "Wizard" FONT 8, "MS Shell Dlg" { CONTROL "Properties", -1, "static", SS_LEFT | WS_CHILD | WS_VISIBLE, 4, 4, 40, 8, 0 CONTROL "", -1, "static", SS_ETCHEDFRAME | WS_CHILD | WS_VISIBLE, 40, 9, 350, 1, 0 CONTROL "", PVX_OF_LIST, "data_control", LBS_NOTIFY | WS_VSCROLL | WS_BORDER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 12, 18, 378, 450, 0 CONTROL "Add Group", PVX_ADD_GROUP, "BUTTON", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 15, 476, 50, 14 CONTROL "OK", IDOK, "BUTTON", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 275, 476, 50, 14 CONTROL "Cancel", IDCANCEL, "BUTTON", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 335, 476, 50, 14 } #endresource
Our dialog has a couple of additions this week. We have added in a Cancel button so that a user can decide not to go through with finishing the wizard, and we have added a button to add a securities group. We will enable and disable this button based upon if a user is inside of the securities group, and when clicked it will add a new set of rows to the dialog based on the elements in the securities list.
int XDX_load() { DataControlSetColumnPositions(PVX_OF_LIST, 150, 200); DataControlSetColumnHeadings(PVX_OF_LIST, "Name", "Value"); DataControlSetColumnFlags(PVX_OF_LIST, DS_CC_READ_ONLY); DataControlSetGridMode(PVX_OF_LIST, DCC_GRID_BOTH); DataControlSetLocalEditMode(PVX_OF_LIST, DCC_EDIT_MODE_AUTO); DataControlSetSelectMode(PVX_OF_LIST, DCC_SELECT_MODE_NONE); DataControlSetDefaultRowHeight(PVX_OF_LIST, 18); ControlDisable(PVX_ADD_GROUP); int size, count, ssize, scount, xpos; string sinfo[]; string slabel[]; size = ArrayGetAxisDepth(info, AXIS_ROW); count = 0;
The beginning of our XDX_load() function is also similar to last week. We define our column sizes using DataControlSetColumnPositions() and names using DataControlSetColumnHeadings(), set the column mode to read only using DataControlSetColumnFlags(), set the grid mode to show both horizontal and vertical lines using DataControlSetGridMode(), and tell the data control to allow editing using DataControlSetLocalEditMode() but to disallow any selection using DataControlSetSelectMode(). We set the default row height to be slightly larger than normal for easier readability using DataControlSetDefaultRowHeight() and also disable the PVX_ADD_GROUP control to start by using the ControlDisable() function. We then declare a collection of variables that we will use for populating the rows of the dialog. We keep track of the size of the info table and the current number of rows in the dialog - we have to do this separately because securities can have multiple rows while only being one row in the info table. We then find how many rows are in the info table by using ArrayGetAxisDepth() on the row axis and initialize our counter to begin a loop.
while (count < size) { switch (info[count][COLUMN_FORMAT]) { case "checkbox": DataControlAddString(PVX_OF_LIST, info[count][COLUMN_LABEL]); DataControlSetCellType(PVX_OF_LIST, xpos, 1, DS_CF_DISPLAY_CHECKBOX_AUTO); DataControlSetCellState(PVX_OF_LIST, xpos, 0, FALSE); xpos++; break; case "securities": sinfo = ExplodeString(info[count][COLUMN_SECURITY], ", "); slabel = ExplodeString(info[count][COLUMN_LABEL], ", "); ssize = DecimalToInteger(sinfo[0]); securities[COLUMN_SIZE] = FormatString("%d", ssize); securities[COLUMN_NUMBER] = "1"; securities[COLUMN_SLABEL] = info[count][COLUMN_LABEL]; securities[COLUMN_START] = sinfo[1]; scount = 0; securitiesstart = xpos; while (scount < ssize) { DataControlAddString(PVX_OF_LIST, FormatString("%s %d", slabel[scount], securities[COLUMN_NUMBER])); DataControlSetCellState(PVX_OF_LIST, xpos, 0, FALSE); xpos++; scount++; } securitiesend = xpos; break; case "string": DataControlAddString(PVX_OF_LIST, info[count][COLUMN_LABEL]); DataControlSetCellState(PVX_OF_LIST, xpos, 0, FALSE); xpos++; break; } count++; } return ERROR_NONE; }
The first thing our loop does is check the format of the entry. If it is “checkbox”, we add the label as a row using DataControlAddString(), set the second column to be a checkbox using DataControlSetCellType() with the DS_CF_DISPLAY_CHECKBOX_AUTO flag, and then disable the first column by using DataControlSetCellState(). We then increment our position in the data control or xpos.
If our entry format is “securities” we use ExplodeString() on the security and label columns of the info table to get the information for the row. We get the size of the row (in our securities list is it three entries) from the first parameter of the securities information array. We have to use DecimalToInteger() on this because we use the size to go through a loop and it is stored as a string in the info table, so we need to convert it to an integer before we use it as a size. We store the size of the securities list (how many rows/elements are in each security), the number “1” as a string, the full label string in the securities information array (which will have to be exploded again each time we use it), and the position of the security in the information array and we store all of this information in the global securities array. We also store the current row position in the dialog into our securitiesstart and securitiesend variables so that we can keep track of these locations for later. We then enter a loop for each element in the security, adding a row to the dialog for each element with the first column being the label stored for that element and then the number of the security list - in this case it is 1. We do this using the DataControlAddString() function to add the row and FormatString() to add together the current element label and securities number so that the string in the row is correct. We then disable the first column of the added row using DataControlSetCellState(), increment our position in the control, and then continue to loop through each element in the securities list. Once all of the elements have been added we store our current position as the ending position of the security.
This may seem complicated, but in the end turns out pretty simple. Our info[count][COLUMN_LABEL] is “Title of Class, Trading Symbol, Name of Exchange.” This means that the first row that will be added to the dialog when this part of the script executes will be “Title of Class 1” because we are taking the first part of the string before the comma and adding the current number of securities on the dialog (“1”) to it. This means that you can change the label column to say anything and that is the information that will be added when the script is executed.
Finally, if our entry format is “string” we add the label as a row using DataControlAddString(), disable the first column using DataControlSetCellState(), and increment our current position in the dialog. We then make sure the loop continues, and once the loop is finished we exit the setup function.
Since we have added the “Add Group” button we need to implement an XDX_action() function to handle any button presses. This function is split into three sections:
void XDX_action(int c_id, int c_action) { int pos[]; if (c_action == DCN_EDIT_CHANGE) { pos = DataControlGetCaretPosition(PVX_OF_LIST); if (pos["Row"] == FindInTable(info, "fksectype", COLUMN_ELEMENT)) { if (DataControlGetCellText(PVX_OF_LIST, pos["Row"], 1) == "1") { twelvebsec = false; } else { twelvebsec = true; } refresh_securities(); } return; }
This first section executes when a checkbox is checked or a cell value is changed. We check to see if the row that is being edited is the same row as our “fksectype” element, this being our element we created to keep track of the security type, checking to see if the user says this is a 12(g) security. We do this by getting the position of the caret in the dialog and comparing it to the position of our element in the array. We get the current position by using the DataControlGetCaretPosition() function. This returns an array with two named keys: “Row” and “Column”. We then get the row of our “fksectype” element in the info table by using FindInTable(). If the two positions are the same, we check to see the value of the text, and if it is equal to “1” (checked) we change our global to say that we a not a 12(b) security. Otherwise we set our twelvebsec value to true, and no matter what we use refresh_securities() to make sure that our dialog is up to date. We’ll talk about what refresh_securities() does in a little while.
if (c_action == DCN_SELECT_CHANGE) { pos = DataControlGetCaretPosition(PVX_OF_LIST); if (pos["Row"] >= securitiesstart && pos["Row"] <= securitiesend) { ControlEnable(PVX_ADD_GROUP); } else { ControlDisable(PVX_ADD_GROUP); } return; }
The next part runs when there is a DCN_SELECT_CHANGE action. This is run every time that a selection change happens in the data control. This means whenever the user clicks into a new row this action gets fired. We get the current selection by using the DataControlGetCaretPosition() function. We care about what row the user is currently in, so we check to see if the current row is inside the securities section by seeing if the current row is larger than the starting position and smaller than the ending position. If the selection is in the securities section we enable the PVX_ADD_GROUP control, which is our “Add Group” button. Otherwise we disable the control. We then leave the function. This sections of code makes sure that the “Add Group” button can not be used unless the user is inside of the securities group.
if (c_id == PVX_ADD_GROUP) { int rc; rc = DataControlInsertRow(PVX_OF_LIST, securitiesend, DecimalToInteger(securities[COLUMN_SIZE])); securities[COLUMN_NUMBER] = FormatString("%d", DecimalToInteger(securities[COLUMN_NUMBER])+1); string slabel[]; slabel = ExplodeString(securities[COLUMN_SLABEL], ", "); int count; count = 0; while (count < DecimalToInteger(securities[COLUMN_SIZE])) { DataControlSetCellText(PVX_OF_LIST, securitiesend+count, 0, FormatString("%s %s", slabel[count], securities[COLUMN_NUMBER])); DataControlSetCellState(PVX_OF_LIST, securitiesend+count, 0, FALSE); count++; } securitiesend += DecimalToInteger(securities[COLUMN_SIZE]); refresh_securities(); }
When the “Add Group” button is clicked, we use DataControlInsertRow() to insert a number of rows equal to the size of each security entry after the end of the security list. We then modify the number label. Similar to how the first security has “Title of Class 1” we want the second set to be “Title of Class 2”. So we take our number string and make it itself +1. In order to do so we convert it from a string to a integer by using the DecimalToInteger() function, add 1 to it, and then use that as a parameter in FormatString() to store this value back in our string table. We use this trick a few times as everything we are storing is being stored as a string, even though some of the values are used as integers.
Once again we use ExplodeString() to get the labels of the securities, and then go through each newly added row, setting the text of the first column as a label using the DataControlSetText() function and then disabling that cell using the DataControlSetCellState() function. Note that in order to reference the correct row we are using the value of where the securities list used to end added to our count of the loop. This means if the newly added rows are rows 21, 22, and 23 (size of three), the previous end of the securities list was 21. Our loop then adds the values to 21, 22, and 23. Finally we increment the end position by adding the size to it. We then use refresh_securities() to make sure our fields are enabled or disabled properly based upon if the user has selected that we are in a 12(g) or 12(b) securities list.
Now let’s take a look at the refresh_securities() function:
void refresh_securities() { int numentries, centries, posx; numentries = DecimalToInteger(securities[COLUMN_NUMBER])*DecimalToInteger(securities[COLUMN_SIZE]); posx = securitiesstart; centries = 0; if (twelvebsec == false) { while (centries < numentries) { if (centries % 3 != 0) { DataControlSetCellState(PVX_OF_LIST, posx, 1, FALSE); } posx++; centries++; } } else { while (centries < numentries) { if (centries % 3 != 0) { DataControlSetCellState(PVX_OF_LIST, posx, 1, TRUE); } posx++; centries++; } } }
In this function we end up going through each row in the dialog that is part of the securities list. We get the size of the list by multiplying the number of securities in the dialog by the number of elements in each security. We then check to see what our global variable twelvebsec is set to. If it is false (meaning we are a 12(g) security) we need to make all of our securities fields except for the name disabled. So we go through each element and check to see if the position in the list is the first in the set of three. We do this by finding the modulus of the position in the list compared to the size of the list (in this case three). If it is the first entry (the modulus is zero) it stays enabled, otherwise the field is disabled using the DataControlSetCellState() function. We do the opposite if we are a 12(b) security, as we need to make sure that all of our cells are enabled.
The last thing we need to do now is add the information the user has filled out to the info table when the OK button is pressed:
int XDX_ok() { int size, count, posx, tempcount; size = ArrayGetAxisDepth(info, AXIS_ROW); count = 0; posx = 0; int centries; string together; int numentries; while (count < size) { switch (info[count][COLUMN_FORMAT]) { case "checkbox": info[count][COLUMN_VALUE] = DataControlGetCellText(PVX_OF_LIST, posx, 1); posx++; break; case "securities": numentries = DecimalToInteger(securities[COLUMN_NUMBER])*DecimalToInteger(securities[COLUMN_SIZE]); together = UTFToEntities(DataControlGetCellText(PVX_OF_LIST, posx, 1)); posx++; centries = 1; while (centries < numentries) { together = together + ", " + UTFToEntities(DataControlGetCellText(PVX_OF_LIST, posx, 1)); posx++; centries++; } info[count][COLUMN_VALUE] = together; break; default: info[count][COLUMN_VALUE] = UTFToEntities(DataControlGetCellText(PVX_OF_LIST, posx, 1)); posx++; break; } count++; } return ERROR_NONE; }
This is basically the opposite of the load function. We are going to go through each row of the info table and process it based on it’s format.
If it is a checkbox we are just getting the text from the second column and storing it in the COLUMN_VALUE column.
If the row is a securities row we see how many rows on the dialog are associated with our securities by multiplying together the size and the number of security entries on the dialog. We then get the first value and use that as a base of our temporary string that will be stored in the info array after our loop. With our first value already found we enter a loop through the rest of the rows in the securities and we get the value of the row using the DataControlGetCellText() and UTFToEntities() functions and then we add the value to the string with a comma in front of the value before incrementing our position and our counter. We put the comma in front so that we have a comma separated string. This is also the reason why we get the first value before entering the loop. This means that we don’t have to have an additional check in the loop to make sure that we are not the first value and so should not have the comma in front of the string. After the loop finishes we store the comma separated string into the value column of our info table.
If our row is a anything but a security or a checkbox we get the value of the cell using DataControlGetCellText() and running it through the UTFToEntities() function before we store it in our value column.
After this function finishes our global info variable now has entries in the COLUMN_VALUE column for each row in the dialog. This allows us to go through the table the same way that we did in the first post. First, though, is our quick debug code which has been given a facelift.
if (fromHook == false) { int size, count; size = ArrayGetAxisDepth(info, AXIS_ROW); count = 0; while (count < size) { AddMessage("%20s : %s", info[count][COLUMN_LABEL], info[count][COLUMN_VALUE]); count++; } }
If we are running from the IDE we go through each row in the info table and print it out to the default message log. This is a great way to make sure that the information we are looking for gets put into the correct place in the table. It also means that rows added to the info table are automatically printed out to the message log. Next we have the beginning of our ReplaceInString() function calls.
content = FileToString(GetScriptFolder() + "8ktemplate.htm"); if (content == "") { MessageBox('x', "Cannot find template file.\r\n\r\nFile should be saved in the same folder as the script."); return "ERROR_CANCEL"; } content = ReplaceInString(content, "##&DocumentPeriodEndDate;##", info[FindInTable(info, "DocumentPeriodEndDate", COLUMN_ELEMENT)][COLUMN_VALUE]); content = ReplaceInString(content, "##&EntityFileNumber;##", info[FindInTable(info, "EntityFileNumber", COLUMN_ELEMENT)][COLUMN_VALUE]); if (info[FindInTable(info, "EntityRegistrantName", COLUMN_ELEMENT)][COLUMN_VALUE] != "") { content = ReplaceInString(content, "##&EntityRegistrantName;##", info[FindInTable(info, "EntityRegistrantName", COLUMN_ELEMENT)][COLUMN_VALUE]); } else { content = ReplaceInString(content, "##&EntityRegistrantName;##", " "); } if (info[FindInTable(info, "EntityIncorporationStateCountryCode", COLUMN_ELEMENT)][COLUMN_VALUE] != "") { content = ReplaceInString(content, "##&EntityIncorporationStateCountryCode;##", info[FindInTable(info, "EntityIncorporationStateCountryCode", COLUMN_ELEMENT)][COLUMN_VALUE]); } else { content = ReplaceInString(content, "##&EntityIncorporationStateCountryCode;##", " "); } if (info[FindInTable(info, "EntityTaxIdentifcationNumber", COLUMN_ELEMENT)][COLUMN_VALUE] != "") { content = ReplaceInString(content, "##&EntityTaxIdentifcationNumber;##", info[FindInTable(info, "EntityTaxIdentifcationNumber", COLUMN_ELEMENT)][COLUMN_VALUE]); } else { content = ReplaceInString(content, "##&EntityTaxIdentifcationNumber;##", " "); } content = ReplaceInString(content, "##&EntityAddressAddressLine1;##", info[FindInTable(info, "EntityAddressAddressLine1", COLUMN_ELEMENT)][COLUMN_VALUE]); if (info[FindInTable(info, "EntityAddressAddressLine2", COLUMN_ELEMENT)][COLUMN_VALUE] != ""){ content = ReplaceInString(content, "##&EntityAddressAddressLine2;##", "<P STYLE=\"font-size: 10pt; text-align: center; margin-top: 12pt; margin-bottom: 0pt\"><B>" + info[FindInTable(info, "EntityAddressAddressLine2", COLUMN_ELEMENT)][COLUMN_VALUE] + "</B></P>"); } else { content = ReplaceInString(content, "##&EntityAddressAddressLine2;##", ""); } if (info[FindInTable(info, "EntityAddressAddressLine3", COLUMN_ELEMENT)][COLUMN_VALUE] != "") { content = ReplaceInString(content, "##&EntityAddressAddressLine3;##", "<P STYLE=\"font-size: 10pt; text-align: center; margin-top: 12pt; margin-bottom: 0pt\"><B>" + info[FindInTable(info, "EntityAddressAddressLine3", COLUMN_ELEMENT)][COLUMN_VALUE] + "</B></P>"); } else { content = ReplaceInString(content, "##&EntityAddressAddressLine3;##", ""); } if (info[FindInTable(info, "EntityAddressCityOrTown", COLUMN_ELEMENT)][COLUMN_VALUE] == "") { if (info[FindInTable(info, "EntityAddressStateOrProvince", COLUMN_ELEMENT)][COLUMN_VALUE] == "") { if (info[FindInTable(info, "EntityAddressCountry", COLUMN_ELEMENT)][COLUMN_VALUE] == "") { content = ReplaceInString(content, "##&EntityAddressCountry;##", " "); } } } content = ReplaceInString(content, "##&EntityAddressCityOrTown;##", info[FindInTable(info, "EntityAddressCityOrTown", COLUMN_ELEMENT)][COLUMN_VALUE]); content = ReplaceInString(content, "##&EntityAddressStateOrProvince;##", info[FindInTable(info, "EntityAddressStateOrProvince", COLUMN_ELEMENT)][COLUMN_VALUE]); content = ReplaceInString(content, "##&EntityAddressCountry;##", info[FindInTable(info, "EntityAddressCountry", COLUMN_ELEMENT)][COLUMN_VALUE]); content = ReplaceInString(content, "##&EntityAddressPostalZipCode;##", info[FindInTable(info, "EntityAddressPostalZipCode", COLUMN_ELEMENT)][COLUMN_VALUE]); if (info[FindInTable(info, "LocalPhoneNumber", COLUMN_ELEMENT)][COLUMN_VALUE] != "") { content = ReplaceInString(content, "##&LocalPhoneNumber;##", info[FindInTable(info, "LocalPhoneNumber", COLUMN_ELEMENT)][COLUMN_VALUE]); } else { content = ReplaceInString(content, "##&LocalPhoneNumber;##", " "); } if (info[FindInTable(info, "EntityInformationFormerLegalOrRegisteredName", COLUMN_ELEMENT)][COLUMN_VALUE] != "") { content = ReplaceInString(content, "##&EntityInformationFormerLegalOrRegisteredName;##", info[FindInTable(info, "EntityInformationFormerLegalOrRegisteredName", COLUMN_ELEMENT)][COLUMN_VALUE]); } else { content = ReplaceInString(content, "##&EntityInformationFormerLegalOrRegisteredName;##", " "); } if (IsTrue(info[FindInTable(info, "PreCommencementIssuerTenderOffer", COLUMN_ELEMENT)][COLUMN_VALUE])) { content = ReplaceInString(content, "##&PreCommencementIssuerTenderOffer;##", "☒"); } else { content = ReplaceInString(content, "##&PreCommencementIssuerTenderOffer;##", "☐"); } if (IsTrue(info[FindInTable(info, "PreCommencementTenderOffer", COLUMN_ELEMENT)][COLUMN_VALUE])) { content = ReplaceInString(content, "##&PreCommencementTenderOffer;##", "☒"); } else { content = ReplaceInString(content, "##&PreCommencementTenderOffer;##", "☐"); } if (IsTrue(info[FindInTable(info, "SolicitingMaterial", COLUMN_ELEMENT)][COLUMN_VALUE])) { content = ReplaceInString(content, "##&SolicitingMaterial;##", "☒"); } else { content = ReplaceInString(content, "##&SolicitingMaterial;##", "☐"); } if (IsTrue(info[FindInTable(info, "WrittenCommunications", COLUMN_ELEMENT)][COLUMN_VALUE])) { content = ReplaceInString(content, "##&WrittenCommunications;##", "☒"); } else { content = ReplaceInString(content, "##&WrittenCommunications;##", "☐"); } if (IsTrue(info[FindInTable(info, "EntityEmergingGrowthCompany", COLUMN_ELEMENT)][COLUMN_VALUE])) { content = ReplaceInString(content, "##&EntityEmergingGrowthCompany;##", "☒"); } else { content = ReplaceInString(content, "##&EntityEmergingGrowthCompany;##", "☐"); } if (IsTrue(info[FindInTable(info, "EntityExTransitionPeriod", COLUMN_ELEMENT)][COLUMN_VALUE])) { content = ReplaceInString(content, "##&EntityExTransitionPeriod;##", "☒"); } else { content = ReplaceInString(content, "##&EntityExTransitionPeriod;##", "☐"); }
If you read the previous part of this series you might notice this section looks almost identical except for two important changes that I would like to highlight. First we have some additional error checking for the template file and instead of having individual variables for each element we now are retrieving them from the info table. After we initially use FileToString() to retrieve the template we check to see if the string is empty. If it is the culprit is that the template file does not exist or we cannot access the template file. If that is the case we put out a MessageBox() and then return an “ERROR_CANCEL” so that the user does not get a weird error from GoFiler. Secondly, we use “info[FindInTable(info, “Element Name”, COLUMN_ELEMENT)][COLUMN_VALUE]” in order to retrieve the value for each element because this guarantees that even if the row number position in the table changes we are getting the correct element value when we put the value into the HTML template content string. The rest of this block of code is explained more in-depth in the previous post. This last section, our security list, is completely new code, however.
count = 0; string secgroup[]; secgroup = ExplodeString(info[FindInTable(info, "fksecgroup", COLUMN_ELEMENT)][COLUMN_VALUE], ", "); string title, symbol, exname, temp, together; together = ""; if (twelvebsec) { together = "<P STYLE=\"font-size: 10pt; text-align: left; text-indent: 0; margin-top: 6pt; margin-bottom: 6pt\">Securities registered pursuant to Section 12(b) of the Act:</P>\r\n\r\n<TABLE BORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\" STYLE=\"width: 100%\">\r\n<TR STYLE=\"vertical-align: top\">\r\n <TD STYLE=\"font-size: 10pt; text-align: center; margin-top: 6pt; margin-bottom: 6pt; width: 33%; border-bottom: Black 1pt solid\">Title of Class</TD>\r\n <TD STYLE=\"font-size: 10pt; text-align: center; margin-top: 6pt; margin-bottom: 6pt; width: 34%; border-bottom: Black 1pt solid\">Trading Symbol(s)</TD>\r\n <TD STYLE=\"font-size: 10pt; text-align: center; margin-top: 6pt; margin-bottom: 6pt; width: 33%; border-bottom: Black 1pt solid\">Name of Exchange</TD>\r\n</TR>"; } else { //MessageBox("Number of entries: %d", DecimalToInteger(securities[0][COLUMN_NUMBER])); together = "<P STYLE=\"font-size: 10pt; text-align: left; text-indent: 0; margin-top: 6pt; margin-bottom: 6pt\">Securities registered pursuant to Section 12(g) of the Act:</P>\r\n\r\n<TABLE BORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\" STYLE=\"width: 100%\">\r\n<TR STYLE=\"vertical-align: top\">\r\n <TD STYLE=\"font-size: 10pt; text-align: center; margin-top: 6pt; margin-bottom: 6pt; border-bottom: Black 1pt solid\">Title of Class</TD>\r\n</TR>"; } while (count < DecimalToInteger(securities[COLUMN_NUMBER])) { title = secgroup[count*3]; symbol = secgroup[count*3+1]; exname = secgroup[count*3+2]; if (twelvebsec) { temp = "\r\n<TR STYLE=\"vertical-align: top\">\r\n <TD STYLE=\"font-size: 10pt; text-align: center\">##&SecurityTitle;##</TD>\r\n <TD STYLE=\"font-size: 10pt; text-align: center\">##&TradingSymbol;##</TD>\r\n <TD STYLE=\"font-size: 10pt; text-align: center\">##&SecurityExchangeName;##</TD>\r\n</TR>"; } else { temp = "\r\n<TR STYLE=\"vertical-align: top\">\r\n <TD STYLE=\"font-size: 10pt; text-align: center\">##&SecurityTitle;##</TD>\r\n</TR>"; } if (title != "") { temp = ReplaceInString(temp, "##&SecurityTitle;##", title); } else { temp = ReplaceInString(temp, "##&SecurityTitle;##", " "); } if (symbol != "") { temp = ReplaceInString(temp, "##&TradingSymbol;##", symbol); } else { temp = ReplaceInString(temp, "##&TradingSymbol;##", " "); } if (exname != "") { temp = ReplaceInString(temp, "##&SecurityExchangeName;##", exname); } else { temp = ReplaceInString(temp, "##&SecurityExchangeName;##", " "); } if (title != "" || symbol != "" || exname != "" || DecimalToInteger(securities[COLUMN_NUMBER]) == 1) { together += temp; } count++; } together += "\r\n</TABLE>"; content = ReplaceInString(content, "##&fksecgroup;##", together); if (fromHook == false) { StringToFile(content, filename); } return content; }
We take the value of our security (a CSV string that we put together in our XDX_ok() function) and explode it into an array. We also determine our base HTML template based upon if we are a 12(b) or 12(g) securities list. There is a separate template for both, as the 12(b) list has three columns in a table while the 12(g) list only has one column. We determine the base HTML template and set that as our together variable. This base HTML code is the label for the table and the header row of our table. We will then add each completed row to together before we replace our fake group with it.
For each security entry in the list we take the next three entries of the value array and store them into individual variables. Since there are three variables in each security entry we are getting the count*3, count*3+1, and count*3+2 position variables. This means that for the third security entry in the list we are getting the variables in position 6, 7, and 8 from our CSV string for the three variables of the security. After getting the variables we set the HTML template row based upon our security type. This HTML template row is just a string of HTML with a couple of flags. The interesting thing to note here is that the 12(g) template only has ##&SecurityTitle;## in it, which means it doesn’t matter what the other variables are set to. We then go through and replace these flags with the variables that we just retrieved. If any of the variables are blank (if the user left that row blank) we put in a non-breaking space instead. Finally we check to see if there are any values in the row, or if there is only one row of data from the dialog. If there are any variables set or if this is the only entry in the list we add our row to our together placeholder. The check here means that we are not adding rows that have been left completely blank in the dialog to the HTML template, unless there was only securities entry in the dialog, in which case we are always adding that row. This ensures if a user clicks ‘OK’ without filling anything out they will at least get a blank row with non-breaking spaces in it to fill out later. After we have made it through every security entry on the dialog we replace the fake group on the HTML template with our together variable.
The last thing to do it check if we are running from the IDE. If we are, we save our HTML content string to the filename that the user picked earlier. Then, no matter what, we return the content of our template. This will put our string into a Page View window for the user to then save and edit.
At this point our HTML cover page template wizard is complete. With a little bit of additional work we can extend this wizard to work with other templates as well, like the 10-Q or 10-K cover pages. However, this wizard only takes care of the HTML portion of the cover page. With the upcoming requirements to include iXBRL on cover pages this means that the process is really only halfway complete. Have no fear, however, as in the next week or two we will be updating this wizard further to add in XDX tagging into this cover sheet, allowing you to have one-step cover page creation and tagging. See you then!
Joshua Kwiatkowski is a developer at Novaworks, primarily working on Novaworks’ cloud-based solution, GoFiler Online. He is a graduate of the Rochester Institute of Technology with a Bachelor of Science degree in Game Design and Development. He has been with the company since 2013. |
Additional Resources