# jprefs.tcl

## Introduction

The jprefs.tcl library is distributed as part of the jstools package. It consists of procedures to support application configuration files (written in Tcl) and user preference files (as Tcl code).

This document describes jprefs.tcl version 4.1/4.4.

## Usage

### Accessing the Library

In order to use the jprefs.tcl library, it (and any other libraries it depends on) must be in your Tcl `auto_path`, described in `tclvars`(n). Information about how to arrange that, and other conventions common to the jstools libraries, is in the Usage section of The jstools Libraries.

### Preferences and Preference Files

These procedures manage user preferences by saving and reading automatically generated files of Tcl code. (Earlier versions of this library used Xstyle defaults files.) These files are managed internally; the user need not edit them directly. Users' preferences are stored in global arrays that an application can consult to see the current state of user preferences. By default, these procedures use the global array `J_PREFS` and the file ~/.tk/defaults.tcl, both of which are intended to store crossapplication defaults.

### The Preference Hierarchy

Preferences are organised into a threelevel hierarchy. At the lowest level are individual preferences. These preferences are grouped into (hopefully) related modules. For instance, all font preferences might be associated into a module, or all preferences to do with printing. Preference modules are grouped into panels. All the preferences within (modules within) a panel share the same global array, and they're written into and read from the same file.

Panels are the highestlevel grouping of preferences; simple applications will probably only need one preference panel. However, it's not easy to save a single preference (or module) without saving all the other preferences in the same panel. The jstools applications use a single panel (`.global_prefs`) for all their shared (i.e. crossapplication) preferences, and some of them use an applicationspecific preference panel too.

If you use the jprefpanel.tcl library for your userinterface, this threelevel division is reflected in the interface: each panel is displayed in its own toplevel dialogue box, and within each panel, preferences in a single module are displayed together. In this case the term panel is intuitive. If you use some different userinterface, though, you can think of a panel as just a level of grouping - a group of (modules of) preferences associated with a single global array and a single file.

### Global Preferences

The `j:read_global_prefs` procedure reads the standard jstools crossapplication preferences, most of which are used by the jstools libraries. You should make sure you read the user's global preferences, either by calling `j:read_global_prefs` or, better yet, by calling `j:jstools_init` (in jinit.tcl), early in your code; otherwise your application won't work consistently with other applications using the libraries, and some library procedures might not work at all. You should also give your users access to the jstools Global Preferences panel, which you can bring up with the `j:global_pref_panel` procedure in jprefpanel.tcl.

(Many of the other jstools libraries make use of the preferences read by `j:read_global_prefs`, so users may see their effects even if you don't specifically check for them.)

## Credits and Copyright

### Author

Jay Sekora
`js@aq.org`
`http://www.aq.org/~js/`

### Copyright

The library is copyright © 1992-1996 by Jay Sekora, but may be freely redistributed under the conditions at the top of the file.

## Overview

### Procedures

`j:source_config` - read user configuration from a file
`j:read_prefs` - read X defaults for preferences from file, set array
`j:read_global_prefs` - read common jstools preferences from ~/.tk/defaults.tcl
`j:read_standard_prefs` - alias for `j:read_global_prefs` (deprecated)
`j:write_prefs` - write Tcl code for preferences to file from array
`j:pref:read_panel` - read all preferences in a particular panel
`j:pref:write_panel` - write all preferences in a particular panel
`j:pref:panel` - create a panel and associate it with a global variable and a file
`j:pref:module` - create a module and associate it with a panel
`j:pref:preference` - create a preference and associate it with a module
`j:pref:create_pref` - alias for `j:pref:preference` (deprecated)
`j:pref:create_global_prefs` - create standard prefs for global preference panel
`j:pref:create_standard_prefs` - alias for `j:pref:create_global_prefs` (deprecated)

### See Also

jprefpanel.tcl
jbindentry.tcl
jbindtext.tcl
jinit.tcl
jldb.tcl

## j:source_config

### Usage

`j:source_config` [`-directory` directory]`` filename

### Argument

filename is the name of the file to source

### Option

`-directory` directory`` (default ~/.tk)

### Description

This procedure is used for reading configuration files. It sources the file filename, normally in the user's .tk directory but in directory if specified. The file is sourced in the context of the calling procedure, so any variables set by the sourced file will be visible to the procedure calling `j:source_config` (and if they're global in the calling procedure, they'll be global to `j:source_config` as well).

## j:read_prefs

### Usage

`j:read_prefs` [options]`` defaults

### Options

`-array` array`` (default `J_PREFS`) - array to read defaults into
`-directory` dir`` (default ~/.tk) - directory file is in
`-file` file`` (default defaults) - defaults file to read
`-prefix` prefix`` (default `{}`) - prefix to keys in array

### Argument

defaults - list of `{`pref`` default`}` sublists

### Example

`j:read_prefs -array BROWSERPREFS -file jbrowser-defaults {
{fancy 0}
{lbwidth 20}
{lbheight 20}
{lbfont default}
}
` [...]`
if $BROWSERPREFS(fancy) {` [...]`}
`
`set mode READONLY
j:read_prefs -array TESTPREFS -file test-${mode}-defaults \
-prefix $mode {
{always_ask 1}
{keep_backups 1}
}
` [...]`
if $TESTPREFS($mode,always_ask) {` [...]`}`

### Description

This procedure reads a preferences file, and extracts each specified pref from it into the array array. If a particular pref doesn't occur in the preferences file (or the defaults file doesn't exist at all), the corresponding default is used instead. The file name will be defaults.tcl unless file is specified, and it will be searched for in ~/.tk unless directory is specified.

(The format of file is a set of Tcl variable assignments, but you should not count on being able to put arbitrary Tcl code in the file, since it's rewritten whenever a user saves hir preferences.)

If file is specified, it will have a .tcl suffix tacked onto it to produce the actual file name.

As a special case, if a pref is `tk_strictMotif`, then the `tk_strictMotif` global variable will be set as well as the `tk_strictMotif` element in array.

If prefix is specified and nonnull, it (plus a comma) is prepended to each preference name, so that for instance you can specify ``-prefix friend`' and access preferences (and array indices) like ``friend,name`', ``friend,age`', etc. (This mechanism is used by `jedit` for modespecific preferences, with prefix being the name of the mode.)

## j:read_global_prefs

### Usage

`j:read_global_prefs`

### Description

This procedure reads a standard set of defaults from the file ~/.tk/defaults.tcl, and sets the corresponding elements of the `J_PREFS` global array (and the `tk_strictMotif` global variable). It's a shorthand for using `j:read_prefs` with a particular fixed set of defaults.

Currently, the standard defaults handled (and the hardwired values used if they don't occur in ~/.tk/defaults.tcl) are:

`language` (default `en`) - ISO code for language to display text in
`autoposition` (default `0`) - whether to centre popup panels
`bindings` (default `basic`) - keyboard bindings (`emacs`, `edt`, `vi`, or `basic`)
`typeover` (default `1`) - whether typing replaces the selection
`confirm` (default `1`) - whether to confirm before certain actions
`printer` (default `lp`) - name of preferred printer
`scrollbarside` (default `right`) - which side of windows scrollbars are on
`visiblebell` (default `1`) - whether to flash the window to simulate a bell
`audiblebell` (default `1`) - whether to ring the display's bell
`j_fs_fast` (default `0`) - if `1`, don't `stat`(2) files in file selector list
`tk_strictMotif` (default `0`) - if `1`, don't hilight active widgets

Many of these are used by other procedures in the jstools libraries (or by Tk itself), but some of them have to be handled by your application. In particular, `bindings` has to be processed by your application. There are procedures in the jbindtext.tcl and jbindentry.tcl libraries to make it easy to set up bindings. The simplest way to read users' global preferences and set up keyboard bindings appropriately, though, is to call `j:jstools_init` early in your code; that procedure calls `j:read_global_prefs`, and then sets up bindings appropriately.

In version 3.6/2.0 of jstools, this procedure was called `j:read_standard_prefs`, and that name is still supported for backwardscompatibility. (Don't rely on it staying, though.)

## j:write_prefs

### Usage

`j:write_prefs` [options]

### Options

`-array array` (default `J_PREFS`) - array to read current values from
`-directory dir` (default ~/.tk) - directory defaults file is in
`-file file` (default `defaults`) - defaults file to write
`-prefix` prefix`` (default `{}`) - prefix to keys in array

### Examples

`j:write_prefs -array BROWSERPREFS -file jbrowser-defaults
`
`j:write_prefs -array TESTPREFS \
-file test-${mode}-defaults -prefix $mode
`

### Description

This procedure is used to save user preferences. By default, it writes all elements of array array which do not have a comma in their names into a preference file. The file name will be defaults.tcl unless file is specified, and it will be put in ~/.tk unless directory is specified. The directory is created if necessary (and possible).

If file is specified, it will have a .tcl suffix tacked onto it to produce the actual file name.

If prefix is specified and nonnull, then it writes all elements of array whose names start with prefix followed by a comma. For instance you can specify ``-prefix friend`' and write preferences (and array indices) like ``friend,name`', ``friend,age`', etc. (This mechanism is used by `jedit` for modespecific preferences, with prefix being the name of the mode.)

## j:pref:read_panel

### Usage

`j:pref:read_panel` panel`` defaults

### Arguments

panel - the name of the panel whose preferences will be read
defaults (optional) - a list of twoelement preference-default pairs

### Example

`j:pref:panel .visual \
-title "Visual Appearance" \
-file visual \
-directory ~/.app_prefs \
-array VISUAL
` [. . .]`
j:pref:read_panel .visual {
{color_scheme LightSkyBlue}
{width 640}
{height 480}
}`

###
Description

This procedure reads preferences from the file specified in a call to `j:pref:panel` to hold panel's preferences. (I.e., assuming such a file exists and was created properly, it reads in the preferences for the modules in panel.)

If defaults is provided, it is a list of twoelement sublists which provide default values for panel's preferences in case the preference file doesn't exist yet. The first element of each sublist is the name of a preference (see `j:pref:preference`), and the second is the default value for that preference.

## j:pref:write_panel

### Usage

`j:pref:write_panel` panel

### Argument

panel - the name of the panel whose preferences are to be saved

### Description

This procedure saves all the preferences associated with panel; i.e., the preferences in the various modules in panel. It does this by finding the Tcl global array associated with panel (which is used by all the preferences in panel's modules) and saving the current value of all its elements as Tcl code in the file and directory associated with panel. Those values are set by `j:pref:panel`.

If you use jprefpanel.tcl to create your user interface, this procedure will be invoked by the Save button on a preference panel. However, you can also invoke it yourself whenever you want to checkpoint the values of preferences associated with a panel (for instance, if you provide an additional interface to change them).

## j:pref:panel

### Usage

`j:pref:panel` panel`` options

### Argument

panel - the name of the panel, a legal Tk toplevel widget pathname

### Options

`-title` title`` (default `Global Preferences`) - localisable title for window
`-array` array`` (default `J_PREFS`) - global array for all preferences in panel
`-file` file`` (default defaults) - file preferences are saved in/loaded from
`-directory` dir`` (default ~/.tk) - directory containing file

### Example

See `j:prefe:preference` for an example

### Description

This procedure registers the title and window name for a new preference panel, the Tcl global array that preferences in that panel will be stored in, and the name of the file (and its directory) where those preferences will be saved and loaded by `j:write_prefs` and `j:read_prefs`. It must be called before `j:pref:module` (which in turn must be called before `j:pref:preference`).

If you use`` the jprefpanel.tcl library to create your interface, the Tk toplevel window created to hold the preference panel will be named panel, which must be a valid widget name, and the window's title will be title (or its localised equivalent). If you're not using jprefpanel.tcl, panel can be an arbitrary string, and title is ignored.

## j:pref:module

### Usage

`j:pref:module` module`` options``

### Argument

module - the name of the module to create

### Options

`-label` label`` (default `Preferences`) - localisable name for module
`-panel` panel`` (default `main`) - name of panel to display this module in

### Example

See `j:prefe:preference` for an example

### Description

This procedure creates and registers a new preferences module module. It must be called before any actual preferences are registered with `j:pref:preference`. The global array preferences in module are stored in, and the file (and directory) they are read from and written to, are based on panel and set by `j:pref:panel`.

If you use`` the jprefpanel.tcl library to create your interface, the userinterface components that allow the user to set preferences in module will appear on the panel named panel, which should be a legal Tk toplevel name. You should provide a (localisable) humanreadable name for the module as label, which will be used as a title for the preferences in module.

## j:pref:preference

### Usage

`j:pref:preference` [pref`` module`` options]

### Arguments

pref - the name of the global variable that stores this preference
module - the module to which this preference belongs

### Options

`-prompt` prompt`` (default `{}`) - localisable label for this preference
`-type` type`` (default `string`) - kind of preference
`-link` link`` (default `{}`) - unimplemented
`-text` text`` (default `{}`) - unimplemented
`-values` values`` - permissible values for oneofmany preferences
`-default` default`` (default `{}`) - initial factory setting for this preference

### Examples

`j:pref:panel .printing_prefs \
-title "Printing Preferences" \
-array PRINT \
-file printing_prefs
j:pref:module document \
-label Output \
-panel .printing_prefs
j:pref:preference size document \
-prompt {Font size:} -default 12
j:pref:preference font document \
-type radio -prompt {Font:} -default` /`Courier \
-values {
{/Courier Courier}
{/Times-Roman Times}
{/Helvetica Helvetica}
}
j:pref:preference landscape document \
-type boolean -prompt {Landscape:} -default 0
j:pref:module media \
-label Media \
-panel .printing_prefs
j:pref:preference paper media \
-type radio -prompt {Paper:} -default plain \
-values {
{plain {Plain white paper}}
{transparency {Transparency}}
{letterhead {Corporate printed letterhead}}
}
j:pref:preference colour media \
-type radio -prompt {Colour printing?} -default 0 \
-values {{0 {Black and white}} {1 Colour}}
j:pref:preference resolution media \
-type boolean -prompt {High resolution:} -default 0
j:pref:show_panel .printing_prefs
`

### Description

This procedure registers a new preference pref within module. The given module must already have been registered by a call to `j:pref:module` (and the modules panel must already have been registered by a call to `j:pref:panel`. The preference will be stored in the global array specified for module's panel with the name of the preference as the key; for instance, in the extended example above, the value of the `font` preference is stored in the global variable `PRINT(font)`.

The prompt option is a localisable humanreadable label to be associated with the preference. The kind of preference is specified by type, which can currently be one of `string`, `boolean`, or `radio`.

If type is `string` (or unspecified), the value of the preference can be any arbitrary string. If it is `boolean`, the value can be true or false (`1` or `0`). If it is `radio`, the value can be one of a set of values provided by values.

The values option is an arbitrarilylong list of twoelement sublists. The first element in each sublist is the value of the variable (i.e., a machinereadable setting for the preference); the second element is a label corresponding to it (i.e., a humanreadable setting for the preference).

The default option provides a default value for the preference; if the variable corresponding to the preference does not already exist, it will be created and set to default.

In previous versions of jstools, this procedure was called `j:pref:create_pref`, and that name is still supported for backwardscompatibility. (Don't rely on it staying, though.)

## Bugs and Misfeatures

- The `-prefix` option to `j:read_prefs` and `j:write_prefs` seems like a kludge to me. I put it in because I didn't want to have to have separate procedures in `jedit` just to read and write modespecific preferences.

## Future Directions

- There should be a mechanism for multiple levels of inheritable preferences - e.g., sitewide > peruser > perapplication > permode > perdocument

`
`