• Solutions
    • FERC XBRL Reporting
    • FDTA Financial Reporting
    • SEC Compliance
    • Windows Clipboard Management
    • Legato Scripting
  • Products
    • GoFiler Suite
    • XBRLworks
    • SEC Exhibit Explorer
    • SEC Extractor
    • Clipboard Scout
    • Legato
  • Education
    • Training
    • SEC and EDGAR Compliance
    • Legato Developers
  • Blog
  • Support
  • Skip to blog entries
  • Skip to archive page
  • Skip to right sidebar

Friday, April 26. 2019

LDC #133: Checkbox and Radio Button Controls Part Three

Automatic operation of checkbox and radio buttons is easy, but what if you want to extend the functionality or do something out of the ordinary? This blog discusses how to manage the control more closely via button notifications. I will also update the checkbox script from Part One of this series to use notifications to add a narrative description to the dialog.


Get the Message


Each type of control sends its own set of messages or notifications. Most controls will send the common messages such as set and kill focus or click. Messages are processed either from the action or notify procedure or both, depending on the specific control. Unfortunately, many control classes share similar notifications; however, the underlying id of the messages are not consistent, so it is important to make sure that the appropriate defined code is being used. In other words, don’t mistake BN_SETFOCUS for EN_SETFOCUS. They indicate the same condition regarding the control but have very different values (6 and 256, respectively). Many control classes have a “notify” style, such as BS_NOTIFY for button controls. Without this style bit being set, the control will only send a notification on click.


Button Messages


In our example, we are assuming that every notification is a click. If BS_NOTIFY is set, then a number of actions may be dispatched:


 Define Code Description 
 BN_CLICKED  0 Sent when the user clicks a button. 
 BN_HILITE  2 Sent when the user selects a button. 
 BN_UNHILITE  3 Sent when the highlight should be removed from a button. This is used principally in owner drawn controls. 
 BN_DISABLE  4 Sent when a button is disabled. This is used principally in owner drawn controls. 
 BN_DOUBLECLICKED  5 Sent when the user double-clicks a button. This notification code is sent automatically for BS_USERBUTTON, BS_RADIOBUTTON, and BS_OWNERDRAW buttons. Other button types send BN_DOUBLECLICKED only if they have the BS_NOTIFY style. 
 BN_PUSHED  BN_HILITE Sent when the push state of a button is set to pushed. This is used principally in owner drawn controls. 
 BN_UNPUSHED  BN_UNHILITE Sent when the push state of a button is set to unpushed. This is used principally in owner drawn controls. 
 BN_DBLCLK  BN_DOUBLECLICKED Same as BN_DOUBLECLICKED. 
 BN_SETFOCUS  6 Sent when a button receives the keyboard focus. The button must have the BS_NOTIFY style to send this notification code. 
 BN_KILLFOCUS  7 Sent when a button loses the keyboard focus. The button must have the BS_NOTIFY style to send this notification code. 

It is critical, once the BS_NOTIFY bit is set, that the action code be checked for the BN_CLICKED notification. Also, do not show message boxes, even for debugging, while processing focus messages. The program will get stuck in an unbreakable loop: the dialog gets focus, the message box kills that focus, and hitting ok on the message box sets focus again, and so forth.


Example 1 — Make a Checkbox Act Like a Radio Button


Our first example is a simple “make-work” program to illustrate direct management of checkboxes. The dialog appears as follows:


Dialog with three checkboxes


Three checkboxes are present with a little message log. The “First” button is set up as a tri-state checkbox while “Second” and “Third” are conventional checkboxes. For illustrative purposes, Second and Third are programmed to act like radio buttons.


Looking at the resources, we see a couple of style keywords that are different than our previous examples:




ManualCheckboxExample01Dlg DIALOGEX 0, 0, 190, 76
EXSTYLE WS_EX_DLGMODALFRAME
STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "Check Box Like Radio Button"
FONT 8, "MS Shell Dlg"
{
 CONTROL "'Radio' Check Box", -1, "static", SS_LEFT | WS_CHILD | WS_VISIBLE, 6, 6, 60, 8, 0
 CONTROL "", -1, "static", SS_ETCHEDFRAME | WS_CHILD | WS_VISIBLE, 70, 11, 112, 1
 CONTROL "&First", CB_FIRST, "button", BS_3STATE | BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 14, 18, 60, 12
 CONTROL "&Second", CB_SECOND, "button", BS_CHECKBOX | BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 74, 18, 60, 12, 0
 CONTROL "&Third", CB_THIRD, "button", BS_CHECKBOX | BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 134, 18, 60, 12, 0
 CONTROL "", CB_MESSAGE, "static", SS_LEFTNOWORDWRAP | WS_CHILD | WS_VISIBLE, 12, 36, 170, 8
 CONTROL "", -1, "static", SS_ETCHEDFRAME | WS_CHILD | WS_VISIBLE, 6, 50, 176, 1
 CONTROL "OK", IDOK, "button", BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 77, 56, 50, 14, 0
 CONTROL "Cancel", IDCANCEL, "button", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 130, 56, 50, 14, 0
}


As shown, BS_3STATE is the style that will make the “First” button behave in a tri-state as unchecked, checked and undefined:


Dialog with three checkboxes, one with three state style


As can be seen, the undefined state also disables the Second and Third checkboxes. The automatic style of tri-state is the BS_AUTO3STATE style. The BS_NOTIFY style is added to show all notifications in the message area, an area that would not be present in a user interface. In the next example, the BS_NOTIFY style will become important to the behavior of the user interface.


Here is the code:





                                                                        // Resource
#beginresource

#define CB_FIRST                        201
#define CB_SECOND                       202
#define CB_THIRD                        203
#define CB_MESSAGE                      204

ManualCheckboxExample01Dlg DIALOGEX 0, 0, 190, 76
EXSTYLE WS_EX_DLGMODALFRAME
STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "Check Box Like Radio Button"
FONT 8, "MS Shell Dlg"
{
 CONTROL "'Radio' Check Box", -1, "static", SS_LEFT | WS_CHILD | WS_VISIBLE, 6, 6, 60, 8, 0
 CONTROL "", -1, "static", SS_ETCHEDFRAME | WS_CHILD | WS_VISIBLE, 70, 11, 112, 1
 CONTROL "&First", CB_FIRST, "button", BS_3STATE | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 14, 18, 60, 12
 CONTROL "&Second", CB_SECOND, "button", BS_CHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 74, 18, 60, 12, 0
 CONTROL "&Third", CB_THIRD, "button", BS_CHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 134, 18, 60, 12, 0
 CONTROL "", CB_MESSAGE, "static", SS_LEFTNOWORDWRAP | WS_CHILD | WS_VISIBLE, 12, 36, 170, 8
 CONTROL "", -1, "static", SS_ETCHEDFRAME | WS_CHILD | WS_VISIBLE, 6, 50, 176, 1
 CONTROL "OK", IDOK, "button", BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 77, 56, 50, 14, 0
 CONTROL "Cancel", IDCANCEL, "button", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 130, 56, 50, 14, 0
}

#endresource

int main() {

    DialogBox("ManualCheckboxExample01Dlg", "cb_");
    return ERROR_NONE;
    }


void cb_action(int c_id, int c_ac) {

    string              s1, s2;
    int                 cv;

                                                                        // Poorman's message snooper

    if ((c_id == CB_FIRST) || (c_id == CB_SECOND) || (c_id == CB_THIRD)) {
      s2 = EditGetText(CB_MESSAGE);
      switch (c_ac) {
        case BN_CLICKED:
          s1 = "BN_CLICKED";
          break;
        case BN_HILITE:
          s1 = "BN_HILITE";
          break;
        case BN_UNHILITE:
          s1 = "BN_UNHILITE";
          break;
        case BN_DISABLE:
          s1 = "BN_DISABLE";
          break;
        case BN_DOUBLECLICKED:
          s1 = "BN_DOUBLECLICKED";
          break;
        case BN_PUSHED:
          s1 = "BN_PUSHED";
          break;
        case BN_UNPUSHED:
          s1 = "BN_UNPUSHED";
          break;
        case BN_DBLCLK:
          s1 = "BN_DBLCLK";
          break;
        case BN_SETFOCUS:
          s1 = "BN_SETFOCUS";
          break;
        case BN_KILLFOCUS:
          s1 = "BN_KILLFOCUS";
          break;
        }
      s2 = s1 + "  " +s2;
      s2 = GetStringSegment(s2, 0, 100);
      EditSetText(CB_MESSAGE, s2);
      }

                                                                        // First Checkbox Action
    if ((c_id == CB_FIRST) && (c_ac == BN_CLICKED)) {
      cv = CheckboxGetState(c_id);
      cv++;
      if (cv == 3) { cv = 0; }
      CheckboxSetState(c_id, cv);
      if (cv == 2) {
        CheckboxSetState(CB_SECOND, 0);
        CheckboxSetState(CB_THIRD, 0);
        ControlDisable(CB_SECOND);
        ControlDisable(CB_THIRD);
        }
      else {
        ControlEnable(CB_SECOND);
        ControlEnable(CB_THIRD);
        }
      }

                                                                        // Second Checkbox Action
    if ((c_id == CB_SECOND) && (c_ac == BN_CLICKED)) {
      CheckboxSetState(CB_SECOND, 1);
      CheckboxSetState(CB_THIRD, 0);
      }

                                                                        // Third Checkbox Action
    if ((c_id == CB_THIRD) && (c_ac == BN_CLICKED)) {
      CheckboxSetState(CB_THIRD, 1);
      CheckboxSetState(CB_SECOND, 0);
      }

    }


For this example, all the work is being done in the action procedure. The first part is something that would only be used for debugging or demonstrating the notifications coming from a control.


There are three processors: the “First” processor counts the clicks as the user moves through the checked, undefined, and back to the unchecked states. This processor also enables and disables the “Second” and “Third” checkboxes. The next two processors are the same for “Second” and “Third”, and they make them act like radio buttons.


When using BS_NOTIFY, it is important to filter the action as show below:



    if ((c_id == CB_FIRST) && (c_ac == BN_CLICKED)) {
      cv = CheckboxGetState(c_id);
      cv++;
      if (cv == 3) { cv = 0; }
      CheckboxSetState(c_id, cv);


In this way, the ‘if’ statement will filter the control ID and the action as well. Below is the code to move through the three states. For two states, the cv variable, which contains the control state, could be XORed with 1:


cv ^= 1;


The “Second” and “Third” function by flipping the state of their counterpart:



    if ((c_id == CB_SECOND) && (c_ac == BN_CLICKED)) {
      CheckboxSetState(CB_SECOND, 1);
      CheckboxSetState(CB_THIRD, 0);
      }


There are a few special precautions to take. First, mixing the auto click style with the manual click processing can cause confusion and odd behavior. Second, using the BS_NOTIFY and not filtering notifications such as focus change which are then mistaken for user clicks.


Improving our Part 1 Check Box Example


The second example uses the BS_NOTIFY style to improve the user interface by adding descriptive “hints” as a static control in the lower portion of the dialog. These hints change depending on which checkbox (and therefore which allergy) is selected. Some code has been added to the program described in Part One to create this modified interface:


Dialog with description that changes with control focus


When the user tabs or changes focus, a description is presented in a new area within the dialog.


Below is the modified script (added code is shown in blue):



                                                                        // Resource
#beginresource

#define CB_BOX_NONE                     201
#define CB_BOX_MILK                     202
#define CB_BOX_EGG                      203
#define CB_BOX_PEANUT                   204
#define CB_BOX_TREE_NUT                 205
#define CB_BOX_WHEAT                    206
#define CB_BOX_SOY                      207
#define CB_BOX_FISH                     208
#define CB_BOX_SHELLFISH                209
#define CB_BOX_SESAME                   210
#define CB_BOX_OTHER                    211
#define CB_OTHER_TITLE                  212
#define CB_OTHER_TEXT                   213
#define CB_OTHER_DESCRIPTION            214

CheckboxExample01Dlg DIALOGEX 0, 0, 240, 160
EXSTYLE WS_EX_DLGMODALFRAME
STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "Check Box Example"
FONT 8, "MS Shell Dlg"
{
 CONTROL "Food Allergies:", -1, "static", SS_LEFT | WS_CHILD | WS_VISIBLE, 6, 6, 60, 8, 0
 CONTROL "", -1, "static", SS_ETCHEDFRAME | WS_CHILD | WS_VISIBLE, 56, 11, 176, 1
 CONTROL "&None", CB_BOX_NONE, "button", BS_AUTOCHECKBOX | BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 14, 18, 60, 12, 0
 CONTROL "&Milk", CB_BOX_MILK, "button", BS_AUTOCHECKBOX | BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 14, 30, 60, 12, 0
 CONTROL "&Egg", CB_BOX_EGG, "button", BS_AUTOCHECKBOX | BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 84, 30, 60, 12, 0
 CONTROL "&Peanut", CB_BOX_PEANUT, "button", BS_AUTOCHECKBOX | BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 154, 30, 60, 12, 0
 CONTROL "&Tree Nut", CB_BOX_TREE_NUT, "button", BS_AUTOCHECKBOX | BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 14, 42, 60, 12, 0
 CONTROL "&Wheat", CB_BOX_WHEAT, "button", BS_AUTOCHECKBOX | BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 84, 42, 60, 12, 0
 CONTROL "&Soy", CB_BOX_SOY, "button", BS_AUTOCHECKBOX | BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 154, 42, 60, 12, 0
 CONTROL "&Fish", CB_BOX_FISH, "button", BS_AUTOCHECKBOX | BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 14, 54, 60, 12, 0
 CONTROL "S&hellfish", CB_BOX_SHELLFISH, "button", BS_AUTOCHECKBOX | BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 84, 54, 60, 12, 0
 CONTROL "Ses&ame", CB_BOX_SESAME, "button", BS_AUTOCHECKBOX | BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 154, 54, 60, 12, 0
 CONTROL "&Other", CB_BOX_OTHER, "button", BS_AUTOCHECKBOX | BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 14, 66, 40, 12, 0
 CONTROL "E&xplain:", CB_OTHER_TITLE, "static", WS_CHILD | WS_VISIBLE, 60, 68, 60, 12, 0
 CONTROL "", CB_OTHER_TEXT, "edit", ES_AUTOHSCROLL | WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP, 60, 82, 160, 12
 CONTROL "", CB_OTHER_DESCRIPTION, "static", WS_CHILD | WS_VISIBLE, 12, 102, 210, 30
 CONTROL "", -1, "static", SS_ETCHEDFRAME | WS_CHILD | WS_VISIBLE, 6, 132, 226, 1
 CONTROL "OK", IDOK, "button", BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 127, 138, 50, 14, 0
 CONTROL "Cancel", IDCANCEL, "button", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 180, 138, 50, 14, 0
}

#endresource

                                                                        // Dialog Data
    string              cb_allergies;
    string              cb_other_text;
    string              hint[];


                                                                        // Main Entry
int main() {
    int                 rc;

    hint[rc++] = "Check if you have no known allergies.";
    hint[rc++] = "Cow's milk is the usual cause of milk allergy, but milk from sheep, goats, buffalo and other mammals also can cause a reaction.";
    hint[rc++] = "Eggs or foods that contain eggs.";
    hint[rc++] = "Peanuts are not the same as tree nuts. Peanuts grow underground and are part of a different plant family, the legumes. Other examples of legumes include beans, peas, lentils and soybeans.";
    hint[rc++] = "Tree nuts include almond, brazil, cashew, hazelnut, pistachio, and walnuts. Tree net allergies are not the same as peanuts, which are legumes, or seeds, such as sunflower or sesame.";
    hint[rc++] = "Wheat or foods that contain wheat.";
    hint[rc++] = "Soybeans are a member of the legume family. Beans, peas, lentils and peanuts are also legumes. Please list peanut separately.";
    hint[rc++] = "Tuna, salmon and halibut are the most common types in this group. Fish and shellfish are not related.";
    hint[rc++] = "Either group of shellfish: crustacea (such as shrimp, crab and lobster) and mollusks (such as clams, mussels, oysters and scallops). Crustacea cause most shellfish reactions.";
    hint[rc++] = "Sesame seeds or foods that contain sesame seeds.";
    hint[rc++] = "Check if an allergy is not listed and please describe in the associated text box.";

    cb_allergies = GetSetting("Dialog Examples", "Multiple Select Options", "Allergies");
    cb_other_text = GetSetting("Dialog Examples", "Multiple Select Options", "Allergies Other");

    rc = DialogBox("CheckboxExample01Dlg", "cb_");
    if (IsNotError(rc))  {
      PutSetting("Dialog Examples", "Multiple Select Options", "Allergies", cb_allergies);
      PutSetting("Dialog Examples", "Multiple Select Options", "Allergies Other", cb_other_text);
      MessageBox("Codes: %s\r\rOtherText: %s", cb_allergies, cb_other_text);
      }
    return rc;
    }

                                                                        // In Dialog Proc - Set State
void cb_set_state() {

    if (CheckboxGetState(CB_BOX_NONE)) {
      ControlDisable(CB_BOX_MILK);
      ControlDisable(CB_BOX_EGG);
      ControlDisable(CB_BOX_PEANUT);
      ControlDisable(CB_BOX_TREE_NUT);
      ControlDisable(CB_BOX_WHEAT);
      ControlDisable(CB_BOX_SOY);
      ControlDisable(CB_BOX_FISH);
      ControlDisable(CB_BOX_SHELLFISH);
      ControlDisable(CB_BOX_SESAME);
      ControlDisable(CB_BOX_OTHER);
      ControlDisable(CB_OTHER_TITLE);
      ControlDisable(CB_OTHER_TEXT);
      }
    else {
      ControlEnable(CB_BOX_MILK);
      ControlEnable(CB_BOX_EGG);
      ControlEnable(CB_BOX_PEANUT);
      ControlEnable(CB_BOX_TREE_NUT);
      ControlEnable(CB_BOX_WHEAT);
      ControlEnable(CB_BOX_SOY);
      ControlEnable(CB_BOX_FISH);
      ControlEnable(CB_BOX_SHELLFISH);
      ControlEnable(CB_BOX_SESAME);
      ControlEnable(CB_BOX_OTHER);
      }
    if (CheckboxGetState(CB_BOX_OTHER)) {
      ControlEnable(CB_OTHER_TITLE);
      ControlEnable(CB_OTHER_TEXT);
      }
    else {
      ControlDisable(CB_OTHER_TITLE);
      ControlDisable(CB_OTHER_TEXT);
      }
    }

                                                                        // In Dialog Proc - Load
int cb_load() {

    if (ScanString(cb_allergies, "None") >= 0) {
      CheckboxSetState(CB_BOX_NONE);
      }
    if (ScanString(cb_allergies, "Milk") >= 0) {
      CheckboxSetState(CB_BOX_MILK);
      }
    if (ScanString(cb_allergies, "Egg") >= 0) {
      CheckboxSetState(CB_BOX_EGG);
      }
    if (ScanString(cb_allergies, "Peanut") >= 0) {
      CheckboxSetState(CB_BOX_PEANUT);
      }
    if (ScanString(cb_allergies, "Tree Nut") >= 0) {
      CheckboxSetState(CB_BOX_TREE_NUT);
      }
    if (ScanString(cb_allergies, "Wheat") >= 0) {
      CheckboxSetState(CB_BOX_WHEAT);
      }
    if (ScanString(cb_allergies, "Soy") >= 0) {
      CheckboxSetState(CB_BOX_SOY);
      }
    if (ScanString(cb_allergies, "Fish") >= 0) {
      CheckboxSetState(CB_BOX_FISH);
      }
    if (ScanString(cb_allergies, "Shellfish") >= 0) {
      CheckboxSetState(CB_BOX_SHELLFISH);
      }
    if (ScanString(cb_allergies, "Sesame") >= 0) {
      CheckboxSetState(CB_BOX_SESAME);
      }
    if (ScanString(cb_allergies, "Other") >= 0) {
      CheckboxSetState(CB_BOX_OTHER);
      EditSetText(CB_OTHER_TEXT, cb_other_text);
      }

    cb_set_state();
    return ERROR_NONE;
    }

                                                                        // In Dialog Proc - Control Change
void cb_action(int c_id, int c_ac) {

    int                 state;

    if ((c_id >= CB_BOX_NONE) && (c_id <= CB_BOX_OTHER)) {
      if (c_ac == BN_SETFOCUS) {
        EditSetText(CB_OTHER_DESCRIPTION, hint[c_id - CB_BOX_NONE]);
        }
      if (c_ac == BN_KILLFOCUS) {
        EditSetText(CB_OTHER_DESCRIPTION, "");
        }
      }

    if ((c_id == CB_BOX_NONE) && (c_ac == BN_CLICKED)) {
      state = CheckboxGetState(c_id);
      if (state != FALSE) {
        CheckboxSetState(CB_BOX_MILK, 0);
        CheckboxSetState(CB_BOX_EGG, 0);
        CheckboxSetState(CB_BOX_PEANUT, 0);
        CheckboxSetState(CB_BOX_TREE_NUT, 0);
        CheckboxSetState(CB_BOX_WHEAT, 0);
        CheckboxSetState(CB_BOX_SOY, 0);
        CheckboxSetState(CB_BOX_FISH, 0);
        CheckboxSetState(CB_BOX_SHELLFISH, 0);
        CheckboxSetState(CB_BOX_SESAME, 0);
        CheckboxSetState(CB_BOX_OTHER, 0);
        EditSetText(CB_OTHER_TEXT, "");
        }
      }

    if ((c_id == CB_BOX_OTHER) && (c_ac == BN_CLICKED)) {
      if (CheckboxGetState(c_id) == FALSE) {
        EditSetText(CB_OTHER_TEXT, "");
        }
      }

    if ((c_id == CB_BOX_NONE) ||
        (c_id == CB_BOX_MILK) ||
        (c_id == CB_BOX_EGG) ||
        (c_id == CB_BOX_PEANUT) ||
        (c_id == CB_BOX_TREE_NUT) ||
        (c_id == CB_BOX_WHEAT) ||
        (c_id == CB_BOX_SOY) ||
        (c_id == CB_BOX_FISH) ||
        (c_id == CB_BOX_SHELLFISH) ||
        (c_id == CB_BOX_SESAME) ||
        (c_id == CB_BOX_OTHER)) {
      if (c_ac == BN_CLICKED) {
        cb_set_state();
        }
      }
    }

                                                                        // In Dialog Proc - Pressed OK
int cb_validate() {

    string              s1, s2;

    if (CheckboxGetState(CB_BOX_NONE)) {
      s1 = AppendWithDelimiter(s1, "None");
      }
    if (CheckboxGetState(CB_BOX_MILK)) {
      s1 = AppendWithDelimiter(s1, "Milk");
      }
    if (CheckboxGetState(CB_BOX_EGG)) {
      s1 = AppendWithDelimiter(s1, "Egg");
      }
    if (CheckboxGetState(CB_BOX_PEANUT)) {
      s1 = AppendWithDelimiter(s1, "Peanut");
      }
    if (CheckboxGetState(CB_BOX_TREE_NUT)) {
      s1 = AppendWithDelimiter(s1, "Tree Nut");
      }
    if (CheckboxGetState(CB_BOX_WHEAT)) {
      s1 = AppendWithDelimiter(s1, "Wheat");
      }
    if (CheckboxGetState(CB_BOX_SOY)) {
      s1 = AppendWithDelimiter(s1, "Soy");
      }
    if (CheckboxGetState(CB_BOX_FISH)) {
      s1 = AppendWithDelimiter(s1, "Fish");
      }
    if (CheckboxGetState(CB_BOX_SHELLFISH)) {
      s1 = AppendWithDelimiter(s1, "Shellfish");
      }
    if (CheckboxGetState(CB_BOX_SESAME)) {
      s1 = AppendWithDelimiter(s1, "Sesame");
      }
    if (CheckboxGetState(CB_BOX_OTHER)) {
      s1 = AppendWithDelimiter(s1, "Other");
      s2 = EditGetText(CB_OTHER_TEXT);
      if (s2 == "") {
        MessageBox('X', "Please specify the nature of the other allergy.");
        return ERROR_SOFT | CB_OTHER_TEXT;
        }
      }

    if (s1 == "") {
      MessageBox('X', "Select at least one allergy or pick 'None'.");
      return ERROR_SOFT | CB_BOX_NONE;
      }

    cb_allergies = s1;
    cb_other_text = s2;
    return ERROR_NONE;
    }


Each of the checkboxes has the BS_NOTIFY style added in order to capture the set and kill focus messages. Any window or control that can accept keyword input receives keyboard focus from the operating system and will send set and kill focus messages.


An array of hints is created (thanks to the internet for these descriptions!), which is referenced during the control focus change in the action procedure:



    if ((c_id >= CB_BOX_NONE) && (c_id <= CB_BOX_OTHER)) {
      if (c_ac == BN_SETFOCUS) {
        EditSetText(CB_OTHER_DESCRIPTION, hint[c_id - CB_BOX_NONE]);
        }
      if (c_ac == BN_KILLFOCUS) {
        EditSetText(CB_OTHER_DESCRIPTION, "");
        }
      }


As shown, the control id is used as the index to the hints list, which is set as the description. The kill focus message is processed to clear the description. Without this code, when the other message box or OK and Cancel buttons receive focus, the description from the last checkbox will still be on the dialog. Also, one might note that the added code is at the start of the action proceeding the other operations. This makes sure that the focus is processed for the controls in which we have interest.


Watching Messages


As shown in the first example, we had a little monitor dumping messages to a window to keep track of what messages are being sent and received. This can be very useful for debugging. I have used more a more sophisticated log that I append to the side of dialogs:


Dialog showing windows messages in a debug list.


This drives message data to a side log to show the message interaction. Developing a script to achieve something like this is a good exercise in both using dialog controls and receiving and outputting notifications.


Conclusion


Many controls send messages which can be used to improve the user experience, such as those we have explored here in reference to checkboxes. Understanding the dynamic of messaging is critical to smooth implementation. By interpreting the messages from checkbox controls, more sophisticated dialog behavior can be developed. Also, the debug process can be aided by dumping data to logs or creating a rolling window to watch what it happening in real-time.


 


 


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

Novaworks’ Legato Resources

Legato Script Developers LinkedIn Group

Primer: An Introduction to Legato 



Posted by
Scott Theis
in Development at 17:13
Trackbacks
Trackback specific URI for this entry

No Trackbacks

Comments
Display comments as (Linear | Threaded)
No comments
The author does not allow comments to this entry

Quicksearch

Categories

  • XML Accounting
  • XML AICPA News
  • XML FASB News
  • XML GASB News
  • XML IASB News
  • XML Development
  • XML Events
  • XML FERC
  • XML eForms News
  • XML FERC Filing Help
  • XML Filing Technology
  • XML Information Technology
  • XML Investor Education
  • XML MSRB
  • XML EMMA News
  • XML FDTA
  • XML MSRB Filing Help
  • XML Novaworks News
  • XML GoFiler Online Updates
  • XML GoFiler Updates
  • XML XBRLworks Updates
  • XML SEC
  • XML Corporation Finance
  • XML DERA
  • XML EDGAR News
  • XML Investment Management
  • XML SEC Filing Help
  • XML XBRL
  • XML Data Quality Committee
  • XML GRIP Taxonomy
  • XML IFRS Taxonomy
  • XML US GAAP Taxonomy

Calendar

Back May '25 Forward
Mo Tu We Th Fr Sa Su
Sunday, May 18. 2025
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  

Feeds

  • XML
Sign Up Now
Get SEC news articles and blog posts delivered monthly to your inbox!
Based on the s9y Bulletproof template framework

Compliance

  • FERC
  • EDGAR
  • EMMA

Software

  • GoFiler Suite
  • SEC Exhibit Explorer
  • SEC Extractor
  • XBRLworks
  • Legato Scripting

Company

  • About Novaworks
  • News
  • Site Map
  • Support

Follow Us:

  • LinkedIn
  • YouTube
  • RSS
  • Newsletter
  • © 2024 Novaworks, LLC
  • Privacy
  • Terms of Use
  • Trademarks and Patents
  • Contact Us