Disable Editing of Individual Fields

Normally Panorama allows the data in any field to be editing at any time. However, it is possible to “lock” individual fields so that they cannot be edited (they can still be modified by a formula or by program code). Individual fields can be disabled in the data sheet, in forms, or both. Fields can be disabled/enabled manually, or this process can be automated with program code.

### Disable a Field in the Data Sheet

To disable editing of a field in the data sheet, uncheck the Editable option in the Field Properties panel.

When this option is unchecked, Panorama will just beep when you double click on this field in the data sheet.

### Disable Fields in a Form

The Editable option normally applies only to the data sheet, not to forms. But if you want, you can extend the effect of this option to forms on a case-by-case basis. If you want the Editable option to apply to a form, check the Disabled Fields mirror Data Sheet option in the Form Properties panel.

With this option enabled, any fields disabled in the data sheet will also be disabled for editing in this form as well. Not only will text editing be disabled, but also checkboxes, radio buttons, pop-up menus, steppers and sliders. When tabbing through the form, disabled fields will be skipped. (Note: If your form includes Text List or Matrix objects attached to a disabled field, these types of objects will remain enabled.)

### The .BlockedEdit Procedure

Usually Panorama simply beeps when you click on a field that is disabled (whether you click on a data sheet cell, or on a Text Editor, Data Button, or other item in a form.) However, if the database contains a `.BlockedEdit` procedure, that procedure will run when a disabled field is clicked on (it does not run, however, when a disabled field is skipped while tabbing). For example, this procedure could be used to notify the user why their editing is being blocked, or even to ask the user for a password to unlock the field.

If there is more than one disabled field, the `.BlockedEdit` procedure will be triggered no matter which field was clicked on. However, the procedure can find out which field was blocked – the field name is passed in the first parameter to the procedure. Here’s an example of how a `.BlockedEdit` procedure could be written to notify the user that the field is disabled, instead of simply beeping.

    let blockedField = parameter(1)
    nsnotify "Cannot edit.","TEXT","Editing of the "+blockedField+" field is disabled."

Note: The `.BlockedEdit` code can enable the field (see below), but if it does so the user will have to click again to edit the field. There is no way that Panorama can be told to allow the original editing request. By the time the `.BlockedEdit` code runs, the original request has already been blocked.

### Using a Procedure to Enable or Disable Fields

In addition to using the Field Properties panel to manually enable or disabled fields, this can also be done on-the-fly with code, either with the setfieldproperties statement (for one field at a time) or the disablefieldediting statement (for all fields).

The disablefieldediting statement has one parameter, a carriage return separated list of fields for which editing is to be disabled. This example disables editing for the Price and Discount fields, while enabling editing for all other fields.

    disablefieldediting commatocr("Price,Discount")

The Price and Discount fields will be disabled in the data sheet, and also in any forms where the Disabled Fields mirror Data Sheet option has been checked.

A quick way to disable all fields is to use the info(“fields”) function.

    disablefieldediting info("fields")

Conversly, to enable all fields, just use `""` (an empty list of fields).

    disablefieldediting ""

Note: Panorama 6 had a similar statement, called lockfields. In fact, Panorama X treats lockfields as a synonym for disablefieldediting, so if you have old code with this statement, it will still work. For example, you can lock all fields with this line of code.

    lockfields info("fields")

However, there are some important differences from Panorama 6 that you need to keep in mind. The old lockfields statement allowed a second parameter that specified whether the effects were permanent. This parameter is no longer allowed, instead, the disabled field settings are always permanent. Also, the old lockfield statement would have to be re-run if any fields were inserted into or removed from the database, otherwise the wrong fields might be disabled. This is no longer the case.

#### Which Fields are Disabled?

Procedure code can use either the getfieldproperties( function or the info(“disabledfields”) function to find out which fields are currently disabled. The info(“disabledfields”) function returns a carriage return delimited list of currently disabled fields (use the info(“enabledfields”) function if you want a list of enabled fields).

This example uses this function to toggle whether editing is allowed. If no fields are disabled, then this code will disable all fields. If any fields are disabled, it will enable all fields. In other words, each time this code runs it will toggle back and forth between enabling and disabling field editing.

    if info("disabledfields")=""
        disablefieldediting info("fields")
    else
        disablefieldediting ""
    endif

Note, the code above can also be written on a singe line by using the ?( function.

    disablefieldediting ?(info("disabledfields")="",info("fields"),"")

Note: If you want to find out what fields are disabled in some other database, you can use the dbinfo( function with the “disabledfields” option (or the “enabledfields” option if you want to know enabled fields).

### Requiring a Password to Edit Data

Finally, let’s look at an example putting all of the features described above to practical use. In this example, five procedures will be added to a database to lock down all data entry into the database. When someone clicks on a field to edit it, they will be asked for a password. If the password is entered correctly, they will be able to edit any field in that record – but as soon as they switch to a different record, everything will be locked down again. The one exception is a new record – if someone creates a new record, they will be able to fill it in. But if they click to a new record and come back to the new record, they’ll have to enter the password to edit it further.

The first component of this system is the `.CurrentRecord` procedure. This procedure is triggered whenever Panorama switches to a different record (see Implicitly Triggered Procedures). In this case, there is just one line of code that will disable editing of all fields the database. Since the `.CurrentRecord` procedure is also triggered when the database first opens, this code will immediately disable all fields when the database is opened.

The next component is the Unlock Record procedure. This will appear in the Action menu. This code asks the user to type in a password. If they type in the correct password (in this case presto), all the fields will be unlocked, and they can edit the current record. You’ll want to change the password to one of your choice.

Next, we have the `.BlockedEdit` procedure. This isn’t absolutely required, but it makes the system more convenient. When the user clicks on a field to edit it, this code automatically calls the Unlock Record procedure to ask for a password. If you leave this procedure out, Panorama will just beep, and the user would have to go to the Action menu to unlock the record.

The `.NewRecord` procedure makes sure that when new records are added to the database, they are automatically enabled and ready to fill in the fields. There’s a bit of a trick to this code. When Panorama adds a new record, it first triggers the `.NewRecord` procedure, but then triggers the `.CurrentRecord` procedure. That’s a problem, because the `.CurrentRecord` procedure is written to disable editing. The `wait 0` code tells Panorama to wait a beat before running the next line. In this case Panorama will run the `.CurrentRecord` procedure, then the `.NewRecord` procedure can get in the last word by enabling the editing.

The `.DeleteRecord` procedure completes the system. If a record is completely empty, this code will allow it to be deleted. But if the record is not empty, then it will ask for a password. To find out if the password was entered correctly, the code checks to see if the fields are enabled. If they are, it goes ahead and deletes the record, but first it disables the fields again so that editing is locked going forward.

Here is the source code for these procedures in case you want to cut and paste it into your own databases.

    // .CurrentRecord
    disablefieldediting info("fields")

    // Unlock Record
    let userPassword = ""
    supergetpassword userPassword,{remember=no}
    if info("dialogtrigger") contains "cancel" stop endif
    if userPassword = "presto" /* <-- YOUR PASSWORD HERE */
        disablefieldediting ""
        nsnotify "Record unlocked."
    endif

    // .BlockedEdit
    nsnotify parameter(1)+" is locked."
    call "Unlock Record"

    // .NewRecord
    if info("trigger") = "New.Add"
        addrecord
    elseif info("trigger") = "New.Insert"
        insertrecord
    elseif info("trigger") = "New.Return"
        insertbelow
    endif
    wait 0 /* let .CurrentRecord run before finishing */
    disablefieldediting ""

    // .DeleteRecord
    if strip(exportline())<>""
        call "Unlock Record"
        if dbinfo("disabledfields","")<>""
            beep
            return
        endif
        disablefieldediting info("fields")
    endif
    deleterecord

That’s it! Less than 30 lines of code to add password protected editing to your database. Of course you can customize this code to your exact needs.

See Also

- addfield -- adds a new field to the current database (on the end).
- Adding New Fields -- adding one or more fields to a database.
- autoallfieldwidths -- automatically sets the width of all fields based on the data in each field.
- autofieldwidth -- automatically sets the width of the current field based on the data in it.
- Automatic Field Calculations -- performing formulas automatically when data is entered into a field.
- Automatic Field Code -- running a short program when data is entered into a field.
- automaticfieldchoices -- updates the current field's Choice list with actual data in the database.
- automaticfieldname( -- returns an available field name.
- cell -- enters a value into the currently active field (i.e. cell).
- checkdesignlock -- checks if field structure can be changed, if not, returns an error.
- commonfieldspopup -- pops up a list of common fields, and changes the current field specifications when a field is chosen from this menu.
- Construct Multiple Fields -- using a template to quickly add multiple fields to a database.
- constructfields -- creates one or more new fields based on a template.
- databaseconsoledump -- dumps the raw contents of the specified database to the console in comma delimited format.
- Date Patterns -- control how dates are displayed or converted to text.
- Dates -- working with dates.
- dbinfo( -- gets information about a database: what forms it contains, what fields, what flash art pictures, etc.
- deletefield -- deletes the current field from the database.
- Deleting Fields -- deleting fields from the database.
- disablefieldediting -- disables editing for a specified list of database fields (all others are enabled).
- editfield -- begins editing of the specified field.
- editfieldname -- opens the data sheet window's field properties inspector and selects the field name.
- Field Blueprint Dialog -- examining and modifying the raw specification of a field.
- Field Properties -- available field attributes.
- Field Properties Panel -- examining and modifying field attributes.
- Field Width -- adjusting the width of a field in the data sheet.
- fieldalignment( -- returns the alignment of a database field.
- fieldformula( -- returns the formula associated with a database field.
- fieldname -- changes the name of the current field.
- fieldnumber( -- returns the number of a database field (starting with 1).
- fieldpattern( -- returns the output pattern associated with a database field.
- Fields -- introduction to database fields.
- fieldtype -- changes the data type of the current field.
- fieldtypes -- returns a carriage return delimited array with list of fields and field data types.
- fieldtypes( -- returns a carriage return delimited array with a list of the fields and field data types.
- fieldwidth( -- returns the width (in the data sheet) of a database field.
- findreplacedialog -- opens the Find & Replace dialog.
- firstcolumn -- moves to the first column in the data sheet (leftmost column).
- getfieldproperties( -- returns a dictionary containing all of the properties of the specified field. (See the setfieldproperties statement if you want to change one or more field properties.)
- hiddenfields( -- returns a list of hidden fields in the curent data sheet window
- hidecurrentfield -- hides the current field in the data sheet.
- hidefieldsbetween -- shows all fields except those in between specified numbers.
- hidelineitemfields -- hides all line item fields.
- hidethesefields -- hides specific fields in the data sheet, making all others visible.
- Hiding and Showing Fields -- temporarily hiding fields in the data sheet.
- info("datatype") -- returns the data type of the current field.
- info("disabledfields") -- returns a list of disabled fields in the current database (fields that cannot be edited).
- info("enabledfields") -- returns a list of enabled fields in the current database (fields that can be edited).
- insertfield -- inserts a new field into the database in front of the current field.
- lastcolumn -- move to the last column in the data sheet (rightmost column).
- Limiting the Maximum Number of Data Sheet Columns -- adjusting the maximum number of columns displayed in data sheet windows.
- Line Item Fields -- are used for repeating items within a record
- mergefieldsdialog -- opens the standard *Merge Fields* dialog.
- Merging Adjacent Fields -- merging two fields into one.
- movefieldbefore -- moves the current field to a new position.
- newdatabasewithfields -- creates a new database with one or more fields.
- newdatabasewithtemplate -- creates a new database with a template.
- Numeric Data -- numeric data (fixed and floating point).
- Numeric Patterns -- control how a number is displayed or converted to text.
- Rearranging Field Order -- rearranging the order of fields in the data sheet.
- reorderfieldsdialog -- opens the Reorder Fields dialog.
- serverdatabaseconsoledump -- dumps the raw contents of the specified database on the server to the console in comma delimited format.
- setfieldnames -- changes the names of all database fields at once.
- setfieldproperties -- modifies one or more properties (name, data type, formula, etc.) of the current field.
- showallfields -- makes every field in the data sheet visible.
- showcolumns -- forces Panorama to display specified fields.
- showfields -- forces Panorama to display specified fields.
- showfieldsbetween -- hides all fields except those in between specified numbers.
- showhidefieldsdialog -- opens the standard Show/Hide Fields dialog (in the Fields menu).
- showthesefields -- makes specific fields in the data sheet visible, hiding all others.
- Smart Dates -- keyboard entry of dates.
- splitfielddialog -- opens the standard *Split Field* dialog.
- Splitting a Field -- splitting a field into two fields.
- tokenname( -- returns the name of a field or variable (instead of the value contained in the field or variables).
- visiblefieldnumbers( -- returns a data array of visible fields (by number) in the data sheet.
- visiblefields( -- returns a list of visible fields in the data sheet.

History

Version | Status | Notes
10.2 | New | New in this version.