Sometimes it is desirable to display tables of information and allow user to edit the data on the sheet directly. Novaworks’ products employ a table data editing platform that in many ways operates like MS Excel™ and provides a high level of flexibility. It is used for XBRL editing, EDGAR Forms, and much more. In this article, I will introduce the data control.
Friday, September 14. 2018
LDC #102: Dialog Boxes Part V — Introduction to the Data Control
Introduction
The data control was originally designed to support EDGAR forms and XBRL editing with a high level of programmability. Overtime, control development has led to four distinct classes of operation:
The lowest level of the editing tools is the Data Sheet class. This class is accessible from Legato as the Data Sheet Object. It provides a sheet matrix supporting primary as well as meta data for each cell.
The next level is the Data Control. It can be directly used as a dialog control by using the class name “data_control”. It is incorporated within in the next two classes. The data control is what visualizes the data sheet and allows for basic editing.
Incorporating one or more Data Controls is Data View. Data View provides a spreadsheet-like interface that is used to support XBRL, EDGAR Forms, and much more. It incorporates sheet management (tabs as a complete workbook), editing and other processing. XBRL View directly incorporates Data View to control facts, contexts, elements and presentations.
Finally, Forms View incorporates Data View and provides for controlled level event-driven access to allow scripts to be written to create any type of data editing platform.
Within Legato, one might notice an overlap of many functions across these classes or objects. For example, the Data Sheet, Data Control and Data View objects all allow cell data to be referenced by name. Except for high level edit control for features such as undo, the API functions are identical.
Our focus will be to introduce the data control. The default setup for data control is to emulate the Windows list box class with a significant expansion of functionality. Let’s start with looking at the structure of the data.
The Matrix
For a list box, we have essentially a one column table. Windows list boxes allow for pseudo columns, but the implementation is very limited. Essentially pseudo columns are created in the single column with tab characters. If a synthetic cell gets too big, it will push the remaining data past the tab stop.
Data control takes a spreadsheet approach:
Each cell is an independent unit visually constrained by the optical dimensions of the columns and rows (or multiple columns if cells are merged). Data can be accessed cell by cell or like a list box in rows.
Each cell has many properties. We will cover those in a later article. For this article, we will treat cells as simple UTF text.
Data Control on a Dialog
For our example, I created a small script that loads a CSV file. For the data I picked a list of the top selling albums. With this, we can also demonstrate some of the display features:
The dialog appears in three sections: the list, the details and a test area to show some of what is going on behind the scenes with a menu to run some data control functions.
By default, a data control will behave like a list box, with single row selection as shown above. However, unlike a list box, the select mode can be changed to individual cells. Also, you will notice that the fields, such as ‘Producer’, are too large, but they do not upset the column layout. In addition, a column legend has been added that is also active. It can be used to select a sorting method on a column and also the partitions can be dragged to expose or hide columns.
Here is the example code for our data control. Note that the CSV file must be downloaded or copied and saved (it is included below the code):
//////////////////////////////////////////////// // Dialog #beginresource #define DC_DATA_CONTROL 201 #define DC_YEAR 202 #define DC_SALES 203 #define DC_ALBUM 204 #define DC_ARTIST 205 #define DC_PRODUCER 206 #define DC_COMMENT 207 #define DC_STATUS_DISPLAY 208 #define DC_ACTION_DISPLAY 209 #define DC_TESTS 210 DataControlDlg DIALOGEX 0, 0, 400, 210 EXSTYLE WS_EX_DLGMODALFRAME STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Data Control Example" FONT 8, "MS Shell Dlg" { CONTROL "", DC_DATA_CONTROL, "data_control", LBS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP | WS_VSCROLL | WS_HSCROLL, 10, 10, 380, 110, 0 CONTROL "Year:", -1, "static", WS_CHILD | WS_VISIBLE, 10, 126, 34, 8 CONTROL "", DC_YEAR, "static", WS_CHILD | WS_VISIBLE, 45, 126, 20, 8 CONTROL "Sales:", -1, "static", WS_CHILD | WS_VISIBLE, 95, 126, 34, 8 CONTROL "", DC_SALES, "static", WS_CHILD | WS_VISIBLE, 130, 126, 70, 8 CONTROL "Album:", -1, "static", WS_CHILD | WS_VISIBLE, 200, 126, 34, 8 CONTROL "", DC_ALBUM, "static", WS_CHILD | WS_VISIBLE, 236, 126, 150, 8 CONTROL "Artist:", -1, "static", WS_CHILD | WS_VISIBLE, 10, 140, 34, 8 CONTROL "", DC_ARTIST, "static", WS_CHILD | WS_VISIBLE, 45, 140, 100, 8 CONTROL "Producer:", -1, "static", WS_CHILD | WS_VISIBLE, 200, 140, 34, 8 CONTROL "", DC_PRODUCER, "static", WS_CHILD | WS_VISIBLE, 236, 140, 150, 8 CONTROL "Comment:", -1, "static", WS_CHILD | WS_VISIBLE, 10, 154, 34, 8 CONTROL "", DC_COMMENT, "static", WS_CHILD | WS_VISIBLE, 45, 154, 300, 16 CONTROL "", -1, "static", SS_ETCHEDFRAME | WS_CHILD | WS_VISIBLE, 10, 176, 375, 1 CONTROL "", DC_STATUS_DISPLAY, "static", WS_CHILD | WS_VISIBLE, 10, 185, 280, 8 CONTROL "", DC_ACTION_DISPLAY, "static", WS_CHILD | WS_VISIBLE, 10, 195, 280, 8 CONTROL "Tests >>", DC_TESTS, "BUTTON", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 340, 185, 45, 12 } #endresource //////////////////////////////////////////////// void dc_show_stats (); int dc_function (string); string menu[200]; int r_swap_pos; int h_rx, h_cx, h_x; //////////////////////////////////////////////// // Control int main() { int ix; menu[ix++] = "Reset Sort"; menu[ix++] = "Sort B Ascend"; menu[ix++] = "Sort B Descend"; menu[ix++] = "Get Sort Stats"; menu[ix++] = "separator"; menu[ix++] = "Swap Rows (0 ...)"; menu[ix++] = "separator"; menu[ix++] = "Select Color Default"; menu[ix++] = "Select Color Odd"; menu[ix++] = "Select Color Inactive"; menu[ix++] = "separator"; menu[ix++] = "Get Click Position"; menu[ix++] = "separator"; menu[ix++] = "Highlight Cell"; menu[ix++] = "Highlight Row"; menu[ix++] = "Highlight Off"; menu[ix++] = "separator"; menu[ix++] = "Make Bold (Cell B)"; menu[ix++] = "Make Italic (Cell B)"; menu[ix++] = "Make Bold/Italic (Cell B)"; menu[ix++] = "Make Normal (Cell B)"; menu[ix++] = "separator"; menu[ix++] = "Enable (Cell B)"; menu[ix++] = "Disable (Cell B)"; menu[ix++] = "Hide (Cell B)"; DialogBox("DataControlDlg", "dc_"); return ERROR_NONE; } //////////////////////////////////////////////// // Dialog Load int dc_load() { string table[][]; DataControlSetColumnPositions(DC_DATA_CONTROL, 23, 122, 150, 210, 270, 350); DataControlSetColumnHeadings(DC_DATA_CONTROL, "Year", "Album Name", "Sales", "Artist", "Producer", "Comment"); DataControlSetColumnFlags(DC_DATA_CONTROL, DS_CC_SORT, DS_CC_SORT, DS_CC_SORT_NUMBERS, DS_CC_SORT, DS_CC_SORT, DS_CC_SORT); DataControlSetDefaultRowHeight(DC_DATA_CONTROL, 14); // Row Height DataControlSetSelectColor(DC_DATA_CONTROL, "#BAD3FC", "black"); // Select Color DataControlSetLegendColor(DC_DATA_CONTROL, "#BAD3FC", "black"); // Legend Colors table = CSVReadTable(GetScriptFolder() + "data_control a.csv"); DataControlAddTable(DC_DATA_CONTROL, table); dc_show_stats(); return ERROR_NONE; } //////////////////////////////////////////////// // Action void dc_action(int id, int code) { int rect[10]; int rc; if (id == DC_DATA_CONTROL) { if (code == DCN_SELECT_CHANGE) { dc_show_stats(); EditSetText(DC_ACTION_DISPLAY, "Select Change"); } if (code == DCN_SORT_CHANGE) { dc_show_stats(); EditSetText(DC_ACTION_DISPLAY, "Sort Change"); } return 0; } if (id == DC_TESTS) { EditSetText(DC_ACTION_DISPLAY, ""); rect = ControlGetPosition(id); rc = MenuTrackAdHocPopup(menu, rect["right"], rect["top"]); if (rc < 0) { return 0; } dc_function(menu[rc]); } } //////////////////////////////////////////////// // Dialog Support void dc_show_stats() { int pos[2]; int rows, cols; int s_cx, s_mode; int sx; sx = DataControlGetRowSelection(DC_DATA_CONTROL); if (sx >= 0) { EditSetText(DC_YEAR, DataControlGetCellText(DC_DATA_CONTROL, sx, 0)); EditSetText(DC_SALES, DataControlGetCellText(DC_DATA_CONTROL, sx, 2) + " million units"); EditSetText(DC_ALBUM, DataControlGetCellText(DC_DATA_CONTROL, sx, 1)); EditSetText(DC_ARTIST, DataControlGetCellText(DC_DATA_CONTROL, sx, 3)); EditSetText(DC_PRODUCER, DataControlGetCellText(DC_DATA_CONTROL, sx, 4)); EditSetText(DC_COMMENT, DataControlGetCellText(DC_DATA_CONTROL, sx, 5)); } rows = DataControlGetRowCount(DC_DATA_CONTROL); cols = DataControlGetColumnCount(DC_DATA_CONTROL); pos = DataControlGetCaretPosition(DC_DATA_CONTROL); s_cx = DataControlGetSortColumn(DC_DATA_CONTROL); s_mode = DataControlGetSortMode(DC_DATA_CONTROL); EditSetText(DC_STATUS_DISPLAY, "Size: %d x %d Pos: %d:%d Sort cx: %s mode: %d", rows, cols, pos["Row"], pos["Col"], s_cx, s_mode); } //////////////////////////////////////////////// int dc_function(string name) { // Test a Function int pos[2]; int rc; int s_cx, s_mode; if (name == "Reset Sort") { rc = DataControlSetSortColumn(DC_DATA_CONTROL); dc_show_stats(); EditSetText(DC_ACTION_DISPLAY, "DataControlSetSortColumn returns 0x%08X", rc); return ERROR_NONE; } if (name == "Sort B Ascend") { rc = DataControlSetSortColumn(DC_DATA_CONTROL, 1); dc_show_stats(); EditSetText(DC_ACTION_DISPLAY, "DataControlSetSortColumn returns 0x%08X", rc); return ERROR_NONE; } if (name == "Sort B Descend") { rc = DataControlSetSortColumn(DC_DATA_CONTROL, 1, 1); dc_show_stats(); EditSetText(DC_ACTION_DISPLAY, "DataControlSetSortColumn returns 0x%08X", rc); return ERROR_NONE; } if (name == "Get Sort Stats") { s_cx = DataControlGetSortColumn(DC_DATA_CONTROL); s_mode = DataControlGetSortMode(DC_DATA_CONTROL); dc_show_stats(); EditSetText(DC_ACTION_DISPLAY, "DataControlGetSortColumn - Mode returns 0x%08X 0x%08X", s_cx, s_mode); return ERROR_NONE; } if (name == "Swap Rows (0 ...)") { rc = DataControlSwapRows(DC_DATA_CONTROL, r_swap_pos, r_swap_pos+1); dc_show_stats(); EditSetText(DC_ACTION_DISPLAY, "DataControlSwapRows returns 0x%08X (base %d)", rc, r_swap_pos); r_swap_pos++; return ERROR_NONE; } if (name == "Select Color Default") { rc = DataControlSetSelectColor(DC_DATA_CONTROL); dc_show_stats(); EditSetText(DC_ACTION_DISPLAY, "DataControlSetSelectColor returns 0x%08X", rc); return ERROR_NONE; } if (name == "Select Color Odd") { rc = DataControlSetSelectColor(DC_DATA_CONTROL, "Purple"); dc_show_stats(); EditSetText(DC_ACTION_DISPLAY, "DataControlSetSelectColor returns 0x%08X", rc); return ERROR_NONE; } if (name == "Select Color Inactive") { rc = DataControlSetSelectColor(DC_DATA_CONTROL, 0x00999999, 0x00111188, 0x00DDDDDD); // rc = DataControlSetSelectColor(DC_DATA_CONTROL, "#999", "#811", "#DDD"); dc_show_stats(); EditSetText(DC_ACTION_DISPLAY, "DataControlSetSelectColor returns 0x%08X", rc); return ERROR_NONE; } if (name == "Get Click Position") { pos = DataControlGetClickPosition(DC_DATA_CONTROL); rc = GetLastError(); dc_show_stats(); EditSetText(DC_ACTION_DISPLAY, "DataControlGetClickPosition row %d col %d (0x%08X)", pos[0], pos[1], rc); return ERROR_NONE; } if (name == "Highlight Cell") { h_rx++; h_cx++; h_x++; rc = DataControlSetCellHighlight(DC_DATA_CONTROL, h_rx, h_cx, h_x); dc_show_stats(); EditSetText(DC_ACTION_DISPLAY, "DataControlSetCellHighlight returns 0x%08X (%d:%d - x %d)", rc, h_rx, h_cx, h_x); return ERROR_NONE; } if (name == "Highlight Row") { h_rx++; h_cx++; h_x++; h_cx = -1; rc = DataControlSetCellHighlight(DC_DATA_CONTROL, h_rx, h_cx, h_x); dc_show_stats(); EditSetText(DC_ACTION_DISPLAY, "DataControlSetCellHighlight returns 0x%08X (%d:%d - x %d)", rc, h_rx, h_cx, h_x); return ERROR_NONE; } if (name == "Highlight Off") { rc = DataControlSetHighlightMode(DC_DATA_CONTROL, FALSE); dc_show_stats(); EditSetText(DC_ACTION_DISPLAY, "DataControlSetHighlightMode returns 0x%08X", rc); return ERROR_NONE; } if (name == "Make Bold (Cell B)") { rc = DataControlGetRowSelection(DC_DATA_CONTROL); if (rc < 0) { EditSetText(DC_ACTION_DISPLAY, "Need Selection"); return ERROR_NONE; } rc = DataControlSetCellStyle(DC_DATA_CONTROL, rc, 1, TRUE); dc_show_stats(); EditSetText(DC_ACTION_DISPLAY, "DataControlSetCellStyle returns 0x%08X", rc); return ERROR_NONE; } if (name == "Make Italic (Cell B)") { rc = DataControlGetRowSelection(DC_DATA_CONTROL); if (rc < 0) { EditSetText(DC_ACTION_DISPLAY, "Need Selection"); return ERROR_NONE; } rc = DataControlSetCellStyle(DC_DATA_CONTROL, rc, 1, FALSE, TRUE); dc_show_stats(); EditSetText(DC_ACTION_DISPLAY, "DataControlSetCellStyle returns 0x%08X", rc); return ERROR_NONE; } if (name == "Make Bold/Italic (Cell B)") { rc = DataControlGetRowSelection(DC_DATA_CONTROL); if (rc < 0) { EditSetText(DC_ACTION_DISPLAY, "Need Selection"); return ERROR_NONE; } rc = DataControlSetCellStyle(DC_DATA_CONTROL, rc, 1, TRUE, TRUE); dc_show_stats(); EditSetText(DC_ACTION_DISPLAY, "DataControlSetCellStyle returns 0x%08X", rc); return ERROR_NONE; } if (name == "Make Normal (Cell B)") { rc = DataControlGetRowSelection(DC_DATA_CONTROL); if (rc < 0) { EditSetText(DC_ACTION_DISPLAY, "Need Selection"); return ERROR_NONE; } rc = DataControlSetCellStyle(DC_DATA_CONTROL, rc, 1); dc_show_stats(); EditSetText(DC_ACTION_DISPLAY, "DataControlSetCellStyle returns 0x%08X", rc); return ERROR_NONE; } if (name == "Enable (Cell B)") { rc = DataControlGetRowSelection(DC_DATA_CONTROL); if (rc < 0) { EditSetText(DC_ACTION_DISPLAY, "Need Selection"); return ERROR_NONE; } rc = DataControlSetCellState(DC_DATA_CONTROL, rc, 1, TRUE); dc_show_stats(); EditSetText(DC_ACTION_DISPLAY, "DataControlSetCellState returns 0x%08X", rc); return ERROR_NONE; } if (name == "Disable (Cell B)") { rc = DataControlGetRowSelection(DC_DATA_CONTROL); if (rc < 0) { EditSetText(DC_ACTION_DISPLAY, "Need Selection"); return ERROR_NONE; } rc = DataControlSetCellState(DC_DATA_CONTROL, rc, 1, FALSE); dc_show_stats(); EditSetText(DC_ACTION_DISPLAY, "DataControlSetCellState returns 0x%08X", rc); return ERROR_NONE; } if (name == "Hide (Cell B)") { rc = DataControlGetRowSelection(DC_DATA_CONTROL); if (rc < 0) { EditSetText(DC_ACTION_DISPLAY, "Need Selection"); return ERROR_NONE; } rc = DataControlSetCellState(DC_DATA_CONTROL, rc, 1, (-1)); dc_show_stats(); EditSetText(DC_ACTION_DISPLAY, "DataControlSetCellState returns 0x%08X", rc); return ERROR_NONE; } return ERROR_NONE; }
The Sample CSV - “data_control a.csv”
"1982","Thriller","110","Michael Jackson","Michael Jackson, Quincy Jones","‘Thriller’ is the highest selling album of all-time, with numerous songs on the album going to #1." "1980","Back in Black","49","AC/DC","Robert John “Mutt” Lange","With Brian Johnson taking over as the front man, this was the first AC/DC album after Bon Scott’s death." "1973","Dark Side of the Moon","43","Pink Floyd","Pink Floyd","Though Syd Barrett was gone, this album stayed on Billboard for a record 14 consecutive years until 1988." "1977","Bat out of Hell","43","Meat Loaf","Todd Rundgren","An appearance on ‘Saturday Night Live’ helped the album receive a huge following in the USA." "1976","Their Greatest Hits 1971-1975","42","Eagles","Glyn Johns, Bill Szymczyk","This was the first album to reach ‘platinum’ status and is the highest selling album of all-time in the USA." "1987","Dirty Dancing Soundtrack","42","Various Artists","Jimmy Ienner","Many of the oldies from the movie were inspired from film producer Eleanor Bergstein’s personal collection." "1992","The Bodyguard Soundtrack","42","Whitney Houston, Various Artists","Whitney Houston, Clive Davis","Whitney Houston’s cover of Dolly Parton’s “I Will Always Love You” is the highlight of this soundtrack." "1977","Rumours","40","Fleetwood Mac","Fleetwood Mac, Ken Caillat, Richard Dashut","The name of the album a reflection of band members writing about each other." "1977","Saturday Night Fever Soundtrack","40","Bee Gees, Various Artists","Arif Mardin","With most recording done by the Bee Gees, this is the number one best-selling soundtrack of all-time." "1999","Millennium","40","Backstreet Boys","Max Martin, Kristian Lundin","Debuting on Billboard at #1, Millennium sold almost half a million copies in the US on its first day."
The Dialog Resource
Let’s start by looking at the resource definition:
DataControlDlg DIALOGEX 0, 0, 400, 210 EXSTYLE WS_EX_DLGMODALFRAME STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU CAPTION "Data Control Example" FONT 8, "MS Shell Dlg" { CONTROL "", DC_DATA_CONTROL, "data_control", LBS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP | WS_VSCROLL | WS_HSCROLL, 10, 10, 380, 110, 0 CONTROL "Year:", -1, "static", WS_CHILD | WS_VISIBLE, 10, 126, 34, 8 CONTROL "", DC_YEAR, "static", WS_CHILD | WS_VISIBLE, 45, 126, 20, 8 CONTROL "Sales:", -1, "static", WS_CHILD | WS_VISIBLE, 95, 126, 34, 8 CONTROL "", DC_SALES, "static", WS_CHILD | WS_VISIBLE, 130, 126, 70, 8 CONTROL "Album:", -1, "static", WS_CHILD | WS_VISIBLE, 200, 126, 34, 8 CONTROL "", DC_ALBUM, "static", WS_CHILD | WS_VISIBLE, 236, 126, 150, 8 CONTROL "Artist:", -1, "static", WS_CHILD | WS_VISIBLE, 10, 140, 34, 8 CONTROL "", DC_ARTIST, "static", WS_CHILD | WS_VISIBLE, 45, 140, 100, 8 CONTROL "Producer:", -1, "static", WS_CHILD | WS_VISIBLE, 200, 140, 34, 8 CONTROL "", DC_PRODUCER, "static", WS_CHILD | WS_VISIBLE, 236, 140, 150, 8 CONTROL "Comment:", -1, "static", WS_CHILD | WS_VISIBLE, 10, 154, 34, 8 CONTROL "", DC_COMMENT, "static", WS_CHILD | WS_VISIBLE, 45, 154, 300, 16 CONTROL "", -1, "static", SS_ETCHEDFRAME | WS_CHILD | WS_VISIBLE, 10, 176, 375, 1 CONTROL "", DC_STATUS_DISPLAY, "static", WS_CHILD | WS_VISIBLE, 10, 185, 280, 8 CONTROL "", DC_ACTION_DISPLAY, "static", WS_CHILD | WS_VISIBLE, 10, 195, 280, 8 CONTROL "Tests >>", DC_TESTS, "BUTTON", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 340, 185, 45, 12 }
The first control is the data control specified with the class name data_control. Note the use of the LBS_NOTIFY style. Data controls accept most list box styles. This style expands the number of messages send to the action procedure. The remainder of the definitions include mostly static class controls as text and one etched line. Then there is the ‘Test’ button. All of these control types have been discussed in my previous dialog articles. The only other style item worth mentioning is that the data control height is as specified. List boxes by default snap their height to an interval of the default row height.
Initialize and Load
The dc_load() function performs two basic functions: sets up the data control and loads the data control. If the setup here is not performed, the data control will appear more like a list box with the column position defaulting to a set interval.
int dc_load() { string table[][]; DataControlSetColumnPositions(DC_DATA_CONTROL, 23, 122, 150, 210, 270, 350); DataControlSetColumnHeadings(DC_DATA_CONTROL, "Year", "Album Name", "Sales", "Artist", "Producer", "Comment"); DataControlSetColumnFlags(DC_DATA_CONTROL, DS_CC_SORT, DS_CC_SORT, DS_CC_SORT_NUMBERS, DS_CC_SORT, DS_CC_SORT, DS_CC_SORT); DataControlSetDefaultRowHeight(DC_DATA_CONTROL, 14); // Row Height DataControlSetSelectColor(DC_DATA_CONTROL, "#BAD3FC", "black"); // Select Color DataControlSetLegendColor(DC_DATA_CONTROL, "#BAD3FC", "black"); // Legend Colors table = CSVReadTable(GetScriptFolder() + "data_control a.csv"); DataControlAddTable(DC_DATA_CONTROL, table); dc_show_stats(); return ERROR_NONE; }
The first thing to review is setting up the columns. The DataControlSetColumnPositions SDK function operates just like the ListBoxSetTabPositions function. The control ID is specified and then a series of positions are provided. The positions are the right edge of the column. There is no easy way to determine the values other than guessing, testing, and adjusting. Future Legato updates may include an API function to retrieve the current column positions.
The next function used is the DataControlSetColumnHeadings function. Its operation is pretty obvious; it sets strings as the column captions. However, the column heading (also known as a legend) will not appear unless this function is executed. By default, each column is fixed with no additional functionality. The next function, the DataControlSetColumnFlags function, tells the control how to process clicks as well as some default style information for each column. Each column’s style information is specified with bits expressing desired behavior:
Definition | Bitwise | Description | |||||
Operation | |||||||
DS_CC_NO_RESIZE | 0x00000001 | User Cannot Resize | |||||
DS_CC_ALLOW_DRAG | 0x00000002 | User Can Drag and Drop Columns | |||||
DS_CC_ALLOW_SELECT | 0x00000004 | User Can Select | |||||
DS_CC_ALLOW_RENAME | 0x00000008 | User Can Rename | |||||
DS_CC_ALLOW_PRESS | 0x00000010 | User Can Press Button (sort) | |||||
Contents | |||||||
DS_CC_DEFAULT_NAME | 0x00000020 | Default Name (A, B, C) | |||||
Select (Highlight) | |||||||
DS_CC_HIGHLIGHT | 0x00000080 | Column is Highlighted (legend) | |||||
Column Text Align | |||||||
DS_CC_TEXT_MODE_MASK | 0x00000C00 | Text Mode Mask (Left, Right, etc) | |||||
DS_CC_TEXT_DEFAULT | 0x00000000 | Default (Left Text) | |||||
DS_CC_TEXT_LEFT | 0x00000400 | Left Text | |||||
DS_CC_TEXT_CENTER | 0x00000800 | Center Text | |||||
DS_CC_TEXT_RIGHT | 0x00000C00 | Right Text | |||||
Protection | |||||||
DS_CC_READ_ONLY | 0x00001000 | Column is Read-Only | |||||
DS_CC_PROTECTED | 0x00002000 | Column is Protected (password) | |||||
Sorting | |||||||
DS_CC_SORT_TYPE_MASK | 0x000F0000 | Sort Type Mask | |||||
DS_CC_SORT_NONE | 0x00000000 | Sort Not Allowed | |||||
DS_CC_SORT_TEXT | 0x00010000 | Sort Text (w/ case sensitivity) | |||||
DS_CC_SORT_TEXT_NO_CASE | 0x00020000 | Sort Text (no case sensitivity) | |||||
DS_CC_SORT_TEXT_NUMERIC | 0x00030000 | Sort Text/Numbers (as File List) | |||||
DS_CC_SORT_NUMERIC | 0x00040000 | Sort Treat as Auto Number | |||||
DS_CC_SORT_DATE_AUTO | 0x00050000 | Sort Treat as Auto Date | |||||
Control | |||||||
DS_CC_HIDDEN | 0x00100000 | Column is Hidden | |||||
DS_CC_DISABLED | 0x00200000 | Column is Disabled | |||||
DS_CC_SORT_MASK | 0x00C00000 | Column Sort Mask | |||||
DS_CC_SORT_ACTIVE | 0x00400000 | Column Sort is Active (only one) | |||||
DS_CC_SORT_DESCEND | 0x00800000 | Sort Descending/Ascending | |||||
Reserved | |||||||
DS_CC_RESERVED | 0xFF000000 | Reserved (internal flags) | |||||
Caller Combinations | |||||||
DS_CC_SORT | 0x00020410 | General Sort, Left Align | |||||
DS_CC_SORT_DATE | 0x00050410 | Date Sort, Left Align | |||||
DS_CC_SORT_FILES | 0x00030410 | Filename Sort, Left Align | |||||
DS_CC_SORT_NUMBERS | 0x00040C10 | Sort as Numeric, Right Align | |||||
DS_CC_SORT_RIGHT | 0x00020C10 | General Sort, Right Align |
We have each column heading flags set to sort the column on click with a minor difference on the Sales columns. This column has sort with the numeric option and right align.
How many columns are there? Well, each of the above functions can expand the number of columns. Also, if data is added that contains more columns than the current sheet, the sheet will also automatically expand.
The next group of functions set up style. The DataControlSetDefaultRowHeight function sets the default row height. The default is 16 pixels. Each row can have its own height which will override the default height, but exploring that is beyond the scope of this article. Next, we set some colors. The DataControlSetSelectColor and DataControlSetLegendColor functions override the default Windows theme color.
Finally, we put information into the control. For this example, we will load a string table, but first we have to load the table using the CSVReadTable function. If this function fails, the control will be empty. In a production/public environment, it would be advisable to load the data, check and display errors concerning the load process. Then we would construct the CSV list before loading its information to the control. For our purposes, we can be simpler. Our sniglet of code:
table = CSVReadTable(GetScriptFolder() + "data_control a.csv"); DataControlAddTable(DC_DATA_CONTROL, table);
There are other ways to add data including:
int = DataControlAddString ( int id, string data );
int = DataControlInsertString ( int id, int row, string data );
Both Add and Insert functions accept tabbed or CSV data for the row to be inserted. Individual cells can also be replaced.
int = DataControlSetCellText ( int id, int row, int column, string data );
There are many other available setup options, such as select mode, that we will cover in a later blog.
The Status Update
The last thing called during the load procedure is the dc_show_stats() routine, which updates the dialog based on conditions in the data control. This serves two purposes: we can load some detail in the lower section and then add some debug/test stuff to the bottom area. Obviously the latter would not be on the dialog for a normal implementation but is shown here are part of the function and class exploration.
void dc_show_stats() { int pos[2]; int rows, cols; int s_cx, s_mode; int sx; sx = DataControlGetRowSelection(DC_DATA_CONTROL); if (sx >= 0) { EditSetText(DC_YEAR, DataControlGetCellText(DC_DATA_CONTROL, sx, 0)); EditSetText(DC_SALES, DataControlGetCellText(DC_DATA_CONTROL, sx, 2) + " million units"); EditSetText(DC_ALBUM, DataControlGetCellText(DC_DATA_CONTROL, sx, 1)); EditSetText(DC_ARTIST, DataControlGetCellText(DC_DATA_CONTROL, sx, 3)); EditSetText(DC_PRODUCER, DataControlGetCellText(DC_DATA_CONTROL, sx, 4)); EditSetText(DC_COMMENT, DataControlGetCellText(DC_DATA_CONTROL, sx, 5)); } rows = DataControlGetRowCount(DC_DATA_CONTROL); cols = DataControlGetColumnCount(DC_DATA_CONTROL); pos = DataControlGetCaretPosition(DC_DATA_CONTROL); s_cx = DataControlGetSortColumn(DC_DATA_CONTROL); s_mode = DataControlGetSortMode(DC_DATA_CONTROL); EditSetText(DC_STATUS_DISPLAY, "Size: %d x %d Pos: %d:%d Sort cx: %s mode: %d", rows, cols, pos["Row"], pos["Col"], s_cx, s_mode); }
The first section retrieves the last select position from the data control. Note that the DataControlGetRowSelection function only works in single row select mode. If the return select/row index is not -1, it means a row has been selected, in which case, one by one, we retrieve each cell on that row and place it on to the dialog page.
The latter part covers our exploration of functions. The first two are used frequently:
int = DataControlGetRowCount ( int id );
int = DataControlGetColumnCount ( int id );
Their operation is pretty obvious except the column count is always one on an empty control.
int [] = DataControlGetCaretPosition ( int id );
The DataControlGetCaretPosition function returns the position as an array with the key names “Row” and “Col”, or 0 and 1, respectively. The column and row position are always processed and returned even if the select mode is set to rows or columns. As the user clicks on a row, it must be over a specific column and as such the column position is also set.
There are a couple of options to retrieve and set sorting modes.
int = DataControlGetSortColumn ( int id );
int = DataControlGetSortMode ( int id );
int = DataControlSetSortColumn ( int id, int column, [int mode] );
The column being sorted can be retrieved as well as its sorting mode. The mode is either ascending or descending. Note that, when data is inserted or added to the list, the list is not resorted.
Playing with Commands
The menu contains a series of functions to illustrate how a number of data control functions operate. You can play with the menu items and code. In the Legato IDE, you can press F1 to look at the documentation for each function.
Conclusion
This blog covered the basics of setting up and loading a data control. In my next blog, I will explain editing and retrieving data. Have fun with the example. Remember as stated above you must have the script and the csv file saved in the same folder for the example to function.
Scott Theis is the President of Novaworks and the principal developer of the Legato scripting language. He has extensive expertise with EDGAR, HTML, XBRL, and other programming languages. |
Additional Resources
Legato Script Developers LinkedIn Group
Primer: An Introduction to Legato