Permalink
Fetching contributors…
Cannot retrieve contributors at this time
666 lines (634 sloc) 24.3 KB
/*
* Exports and types from dialog.c.
*/
/*
* This will come in handy for generic control handlers. Anyone
* knows how to make this more portable, let me know :-)
*/
#define ATOFFSET(data, offset) ( (void *) ( (char *)(data) + (offset) ) )
/*
* This is the big union which defines a single control, of any
* type.
*
* General principles:
* - _All_ pointers in this structure are expected to point to
* dynamically allocated things, unless otherwise indicated.
* - `char' fields giving keyboard shortcuts are expected to be
* NO_SHORTCUT if no shortcut is desired for a particular control.
* - The `label' field can often be NULL, which will cause the
* control to not have a label at all. This doesn't apply to
* checkboxes and push buttons, in which the label is not
* separate from the control.
*/
#define NO_SHORTCUT '\0'
enum {
CTRL_TEXT, /* just a static line of text */
CTRL_EDITBOX, /* label plus edit box */
CTRL_RADIO, /* label plus radio buttons */
CTRL_CHECKBOX, /* checkbox (contains own label) */
CTRL_BUTTON, /* simple push button (no label) */
CTRL_LISTBOX, /* label plus list box */
CTRL_COLUMNS, /* divide window into columns */
CTRL_FILESELECT, /* label plus filename selector */
CTRL_FONTSELECT, /* label plus font selector */
CTRL_TABDELAY, /* see `tabdelay' below */
CTRL_ICON /* static icon without label */
};
/*
* Many controls have `intorptr' unions for storing user data,
* since the user might reasonably want to store either an integer
* or a void * pointer. Here I define a union, and two convenience
* functions to create that union from actual integers or pointers.
*
* The convenience functions are declared as inline if possible.
* Otherwise, they're declared here and defined when this header is
* included with DEFINE_INTORPTR_FNS defined. This is a total pain,
* but such is life.
*/
typedef union { void *p; int i; } intorptr;
#ifndef INLINE
intorptr I(int i);
intorptr P(void *p);
#endif
#if defined DEFINE_INTORPTR_FNS || defined INLINE
#ifdef INLINE
#define PREFIX INLINE
#else
#define PREFIX
#endif
PREFIX intorptr I(int i) { intorptr ret; ret.i = i; return ret; }
PREFIX intorptr P(void *p) { intorptr ret; ret.p = p; return ret; }
#undef PREFIX
#endif
/*
* Each control has an `int' field specifying which columns it
* occupies in a multi-column part of the dialog box. These macros
* pack and unpack that field.
*
* If a control belongs in exactly one column, just specifying the
* column number is perfectly adequate.
*/
#define COLUMN_FIELD(start, span) ( (((span)-1) << 16) + (start) )
#define COLUMN_START(field) ( (field) & 0xFFFF )
#define COLUMN_SPAN(field) ( (((field) >> 16) & 0xFFFF) + 1 )
union control;
/*
* The number of event types is being deliberately kept small, on
* the grounds that not all platforms might be able to report a
* large number of subtle events. We have:
* - the special REFRESH event, called when a control's value
* needs setting
* - the ACTION event, called when the user does something that
* positively requests action (double-clicking a list box item,
* or pushing a push-button)
* - the VALCHANGE event, called when the user alters the setting
* of the control in a way that is usually considered to alter
* the underlying data (toggling a checkbox or radio button,
* moving the items around in a drag-list, editing an edit
* control)
* - the SELCHANGE event, called when the user alters the setting
* of the control in a more minor way (changing the selected
* item in a list box).
* - the CALLBACK event, which happens after the handler routine
* has requested a subdialog (file selector, font selector,
* colour selector) and it has come back with information.
*/
enum {
EVENT_REFRESH,
EVENT_ACTION,
EVENT_VALCHANGE,
EVENT_SELCHANGE,
EVENT_CALLBACK
};
typedef void (*handler_fn)(union control *ctrl, void *dlg,
void *data, int event);
#define STANDARD_PREFIX \
int type; \
char *label; \
int tabdelay; \
int column; \
handler_fn handler; \
intorptr context; \
intorptr helpctx
union control {
/*
* The first possibility in this union is the generic header
* shared by all the structures, which we are therefore allowed
* to access through any one of them.
*/
struct {
int type;
/*
* Every control except CTRL_COLUMNS has _some_ sort of
* label. By putting it in the `generic' union as well as
* everywhere else, we avoid having to have an irritating
* switch statement when we go through and deallocate all
* the memory in a config-box structure.
*
* Yes, this does mean that any non-NULL value in this
* field is expected to be dynamically allocated and
* freeable.
*
* For CTRL_COLUMNS, this field MUST be NULL.
*/
char *label;
/*
* If `tabdelay' is non-zero, it indicates that this
* particular control should not yet appear in the tab
* order. A subsequent CTRL_TABDELAY entry will place it.
*/
int tabdelay;
/*
* Indicate which column(s) this control occupies. This can
* be unpacked into starting column and column span by the
* COLUMN macros above.
*/
int column;
/*
* Most controls need to provide a function which gets
* called when that control's setting is changed, or when
* the control's setting needs initialising.
*
* The `data' parameter points to the writable data being
* modified as a result of the configuration activity; for
* example, the PuTTY `Conf' structure, although not
* necessarily.
*
* The `dlg' parameter is passed back to the platform-
* specific routines to read and write the actual control
* state.
*/
handler_fn handler;
/*
* Almost all of the above functions will find it useful to
* be able to store a piece of `void *' or `int' data.
*/
intorptr context;
/*
* For any control, we also allow the storage of a piece of
* data for use by context-sensitive help. For example, on
* Windows you can click the magic question mark and then
* click a control, and help for that control should spring
* up. Hence, here is a slot in which to store per-control
* data that a particular platform-specific driver can use
* to ensure it brings up the right piece of help text.
*/
intorptr helpctx;
} generic;
struct {
STANDARD_PREFIX;
union control *ctrl;
} tabdelay;
struct {
STANDARD_PREFIX;
} text;
struct {
STANDARD_PREFIX;
char shortcut; /* keyboard shortcut */
/*
* Percentage of the dialog-box width used by the edit box.
* If this is set to 100, the label is on its own line;
* otherwise the label is on the same line as the box
* itself.
*/
int percentwidth;
int password; /* details of input are hidden */
/*
* A special case of the edit box is the combo box, which
* has a drop-down list built in. (Note that a _non_-
* editable drop-down list is done as a special case of a
* list box.)
*
* Don't try setting has_list and password on the same
* control; front ends are not required to support that
* combination.
*/
int has_list;
/*
* Edit boxes tend to need two items of context, so here's
* a spare.
*/
intorptr context2;
} editbox;
struct {
STANDARD_PREFIX;
/*
* `shortcut' here is a single keyboard shortcut which is
* expected to select the whole group of radio buttons. It
* can be NO_SHORTCUT if required, and there is also a way
* to place individual shortcuts on each button; see below.
*/
char shortcut;
/*
* There are separate fields for `ncolumns' and `nbuttons'
* for several reasons.
*
* Firstly, we sometimes want the last of a set of buttons
* to have a longer label than the rest; we achieve this by
* setting `ncolumns' higher than `nbuttons', and the
* layout code is expected to understand that the final
* button should be given all the remaining space on the
* line. This sounds like a ludicrously specific special
* case (if we're doing this sort of thing, why not have
* the general ability to have a particular button span
* more than one column whether it's the last one or not?)
* but actually it's reasonably common for the sort of
* three-way control you get a lot of in PuTTY: `yes'
* versus `no' versus `some more complex way to decide'.
*
* Secondly, setting `nbuttons' higher than `ncolumns' lets
* us have more than one line of radio buttons for a single
* setting. A very important special case of this is
* setting `ncolumns' to 1, so that each button is on its
* own line.
*/
int ncolumns;
int nbuttons;
/*
* This points to a dynamically allocated array of `char *'
* pointers, each of which points to a dynamically
* allocated string.
*/
char **buttons; /* `nbuttons' button labels */
/*
* This points to a dynamically allocated array of `char'
* giving the individual keyboard shortcuts for each radio
* button. The array may be NULL if none are required.
*/
char *shortcuts; /* `nbuttons' shortcuts; may be NULL */
/*
* This points to a dynamically allocated array of
* intorptr, giving helpful data for each button.
*/
intorptr *buttondata; /* `nbuttons' entries; may be NULL */
} radio;
struct {
STANDARD_PREFIX;
char shortcut;
} checkbox;
struct {
STANDARD_PREFIX;
char shortcut;
/*
* At least Windows has the concept of a `default push
* button', which gets implicitly pressed when you hit
* Return even if it doesn't have the input focus.
*/
int isdefault;
/*
* Also, the reverse of this: a default cancel-type button,
* which is implicitly pressed when you hit Escape.
*/
int iscancel;
} button;
struct {
STANDARD_PREFIX;
char shortcut; /* keyboard shortcut */
/*
* Height of the list box, in approximate number of lines.
* If this is zero, the list is a drop-down list.
*/
int height; /* height in lines */
/*
* If this is set, the list elements can be reordered by
* the user (by drag-and-drop or by Up and Down buttons,
* whatever the per-platform implementation feels
* comfortable with). This is not guaranteed to work on a
* drop-down list, so don't try it!
*/
int draglist;
/*
* If this is non-zero, the list can have more than one
* element selected at a time. This is not guaranteed to
* work on a drop-down list, so don't try it!
*
* Different non-zero values request slightly different
* types of multi-selection (this may well be meaningful
* only in GTK, so everyone else can ignore it if they
* want). 1 means the list box expects to have individual
* items selected, whereas 2 means it expects the user to
* want to select a large contiguous range at a time.
*/
int multisel;
/*
* Percentage of the dialog-box width used by the list box.
* If this is set to 100, the label is on its own line;
* otherwise the label is on the same line as the box
* itself. Setting this to anything other than 100 is not
* guaranteed to work on a _non_-drop-down list, so don't
* try it!
*/
int percentwidth;
/*
* Some list boxes contain strings that contain tab
* characters. If `ncols' is greater than 0, then
* `percentages' is expected to be non-zero and to contain
* the respective widths of `ncols' columns, which together
* will exactly fit the width of the list box. Otherwise
* `percentages' must be NULL.
*
* There should never be more than one column in a
* drop-down list (one with height==0), because front ends
* may have to implement it as a special case of an
* editable combo box.
*/
int ncols; /* number of columns */
int *percentages; /* % width of each column */
/*
* Flag which can be set to FALSE to suppress the horizontal
* scroll bar if a list box entry goes off the right-hand
* side.
*/
int hscroll;
} listbox;
struct {
STANDARD_PREFIX;
char shortcut;
/*
* `filter' dictates what type of files will be selected by
* default; for example, when selecting private key files
* the file selector would do well to only show .PPK files
* (on those systems where this is the chosen extension).
*
* The precise contents of `filter' are platform-defined,
* unfortunately. The special value NULL means `all files'
* and is always a valid fallback.
*
* Unlike almost all strings in this structure, this value
* is NOT expected to require freeing (although of course
* you can always use ctrl_alloc if you do need to create
* one on the fly). This is because the likely mode of use
* is to define string constants in a platform-specific
* header file, and directly reference those. Or worse, a
* particular platform might choose to cast integers into
* this pointer type...
*/
char const *filter;
/*
* Some systems like to know whether a file selector is
* choosing a file to read or one to write (and possibly
* create).
*/
int for_writing;
/*
* On at least some platforms, the file selector is a
* separate dialog box, and contains a user-settable title.
*
* This value _is_ expected to require freeing.
*/
char *title;
} fileselect;
struct {
/* In this variant, `label' MUST be NULL. */
STANDARD_PREFIX;
int ncols; /* number of columns */
int *percentages; /* % width of each column */
/*
* Every time this control type appears, exactly one of
* `ncols' and the previous number of columns MUST be one.
* Attempting to allow a seamless transition from a four-
* to a five-column layout, for example, would be way more
* trouble than it was worth. If you must lay things out
* like that, define eight unevenly sized columns and use
* column-spanning a lot. But better still, just don't.
*
* `percentages' may be NULL if ncols==1, to save space.
*/
} columns;
struct {
STANDARD_PREFIX;
char shortcut;
} fontselect;
struct {
STANDARD_PREFIX;
intorptr handle;
} icon;
};
#undef STANDARD_PREFIX
/*
* `controlset' is a container holding an array of `union control'
* structures, together with a panel name and a title for the whole
* set. In Windows and any similar-looking GUI, each `controlset'
* in the config will be a container box within a panel.
*
* Special case: if `boxname' is NULL, the control set gives an
* overall title for an entire panel of controls.
*/
struct controlset {
char *pathname; /* panel path, e.g. "SSH/Tunnels" */
char *boxname; /* internal short name of controlset */
char *boxtitle; /* title of container box */
int ncolumns; /* current no. of columns at bottom */
int ncontrols; /* number of `union control' in array */
int ctrlsize; /* allocated size of array */
union control **ctrls; /* actual array */
};
typedef void (*ctrl_freefn_t)(void *); /* used by ctrl_alloc_with_free */
/*
* This is the container structure which holds a complete set of
* controls.
*/
struct controlbox {
int nctrlsets; /* number of ctrlsets */
int ctrlsetsize; /* ctrlset size */
struct controlset **ctrlsets; /* actual array of ctrlsets */
int nfrees;
int freesize;
void **frees; /* array of aux data areas to free */
ctrl_freefn_t *freefuncs; /* parallel array of free functions */
};
struct controlbox *ctrl_new_box(void);
void ctrl_free_box(struct controlbox *);
/*
* Standard functions used for populating a controlbox structure.
*/
/* Set up a panel title. */
struct controlset *ctrl_settitle(struct controlbox *,
const char *path, const char *title);
/* Retrieve a pointer to a controlset, creating it if absent. */
struct controlset *ctrl_getset(struct controlbox *, const char *path,
const char *name, const char *boxtitle);
void ctrl_free_set(struct controlset *);
void ctrl_free(union control *);
/*
* This function works like `malloc', but the memory it returns
* will be automatically freed when the controlbox is freed. Note
* that a controlbox is a dialog-box _template_, not an instance,
* and so data allocated through this function is better not used
* to hold modifiable per-instance things. It's mostly here for
* allocating structures to be passed as control handler params.
*
* ctrl_alloc_with_free also allows you to provide a function to free
* the structure, in case there are other dynamically allocated bits
* and pieces dangling off it.
*/
void *ctrl_alloc(struct controlbox *b, size_t size);
void *ctrl_alloc_with_free(struct controlbox *b, size_t size,
ctrl_freefn_t freefunc);
/*
* Individual routines to create `union control' structures in a controlset.
*
* Most of these routines allow the most common fields to be set
* directly, and put default values in the rest. Each one returns a
* pointer to the `union control' it created, so that final tweaks
* can be made.
*/
/* `ncolumns' is followed by that many percentages, as integers. */
union control *ctrl_columns(struct controlset *, int ncolumns, ...);
union control *ctrl_editbox(struct controlset *, const char *label,
char shortcut, int percentage, intorptr helpctx,
handler_fn handler,
intorptr context, intorptr context2);
union control *ctrl_combobox(struct controlset *, const char *label,
char shortcut, int percentage, intorptr helpctx,
handler_fn handler,
intorptr context, intorptr context2);
/*
* `ncolumns' is followed by (alternately) radio button titles and
* intorptrs, until a NULL in place of a title string is seen. Each
* title is expected to be followed by a shortcut _iff_ `shortcut'
* is NO_SHORTCUT.
*/
union control *ctrl_radiobuttons(struct controlset *, const char *label,
char shortcut, int ncolumns, intorptr helpctx,
handler_fn handler, intorptr context, ...);
union control *ctrl_pushbutton(struct controlset *, const char *label,
char shortcut, intorptr helpctx,
handler_fn handler, intorptr context);
union control *ctrl_listbox(struct controlset *, const char *label,
char shortcut, intorptr helpctx,
handler_fn handler, intorptr context);
union control *ctrl_droplist(struct controlset *, const char *label,
char shortcut, int percentage, intorptr helpctx,
handler_fn handler, intorptr context);
union control *ctrl_draglist(struct controlset *, const char *label,
char shortcut, intorptr helpctx,
handler_fn handler, intorptr context);
union control *ctrl_filesel(struct controlset *, const char *label,
char shortcut, const char *filter, int write,
const char *title, intorptr helpctx,
handler_fn handler, intorptr context);
union control *ctrl_fontsel(struct controlset *, const char *label,
char shortcut, intorptr helpctx,
handler_fn handler, intorptr context);
union control *ctrl_text(struct controlset *, const char *text,
intorptr helpctx);
union control *ctrl_checkbox(struct controlset *, const char *label,
char shortcut, intorptr helpctx,
handler_fn handler, intorptr context);
union control *ctrl_tabdelay(struct controlset *, union control *);
union control *ctrl_icon(struct controlset *, intorptr helpctx, intorptr context);
// Should be somewhere below, but this is easier
void dlg_icon_set(union control *ctrl, void *dlg, char const *icon);
/*
* Routines the platform-independent dialog code can call to read
* and write the values of controls.
*/
void dlg_radiobutton_set(union control *ctrl, void *dlg, int whichbutton);
int dlg_radiobutton_get(union control *ctrl, void *dlg);
void dlg_checkbox_set(union control *ctrl, void *dlg, int checked);
int dlg_checkbox_get(union control *ctrl, void *dlg);
void dlg_editbox_set(union control *ctrl, void *dlg, char const *text);
char *dlg_editbox_get(union control *ctrl, void *dlg); /* result must be freed by caller */
/* The `listbox' functions can also apply to combo boxes. */
void dlg_listbox_clear(union control *ctrl, void *dlg);
void dlg_listbox_del(union control *ctrl, void *dlg, int index);
void dlg_listbox_add(union control *ctrl, void *dlg, char const *text);
/*
* Each listbox entry may have a numeric id associated with it.
* Note that some front ends only permit a string to be stored at
* each position, which means that _if_ you put two identical
* strings in any listbox then you MUST not assign them different
* IDs and expect to get meaningful results back.
*/
void dlg_listbox_addwithid(union control *ctrl, void *dlg,
char const *text, int id);
int dlg_listbox_getid(union control *ctrl, void *dlg, int index);
/* dlg_listbox_index returns <0 if no single element is selected. */
int dlg_listbox_index(union control *ctrl, void *dlg);
int dlg_listbox_issel(union control *ctrl, void *dlg, int index);
void dlg_listbox_select(union control *ctrl, void *dlg, int index);
void dlg_text_set(union control *ctrl, void *dlg, char const *text);
void dlg_filesel_set(union control *ctrl, void *dlg, Filename *fn);
Filename *dlg_filesel_get(union control *ctrl, void *dlg);
void dlg_fontsel_set(union control *ctrl, void *dlg, FontSpec *fn);
FontSpec *dlg_fontsel_get(union control *ctrl, void *dlg);
/*
* Bracketing a large set of updates in these two functions will
* cause the front end (if possible) to delay updating the screen
* until it's all complete, thus avoiding flicker.
*/
void dlg_update_start(union control *ctrl, void *dlg);
void dlg_update_done(union control *ctrl, void *dlg);
/*
* Set input focus into a particular control.
*/
void dlg_set_focus(union control *ctrl, void *dlg);
/*
* Change the label text on a control.
*/
void dlg_label_change(union control *ctrl, void *dlg, char const *text);
/*
* Return the `ctrl' structure for the most recent control that had
* the input focus apart from the one mentioned. This is NOT
* GUARANTEED to work on all platforms, so don't base any critical
* functionality on it!
*/
union control *dlg_last_focused(union control *ctrl, void *dlg);
/*
* During event processing, you might well want to give an error
* indication to the user. dlg_beep() is a quick and easy generic
* error; dlg_error() puts up a message-box or equivalent.
*/
void dlg_beep(void *dlg);
void dlg_error_msg(void *dlg, const char *msg);
/*
* This function signals to the front end that the dialog's
* processing is completed, and passes an integer value (typically
* a success status).
*/
void dlg_end(void *dlg, int value);
/*
* Routines to manage a (per-platform) colour selector.
* dlg_coloursel_start() is called in an event handler, and
* schedules the running of a colour selector after the event
* handler returns. The colour selector will send EVENT_CALLBACK to
* the control that spawned it, when it's finished;
* dlg_coloursel_results() fetches the results, as integers from 0
* to 255; it returns nonzero on success, or zero if the colour
* selector was dismissed by hitting Cancel or similar.
*
* dlg_coloursel_start() accepts an RGB triple which is used to
* initialise the colour selector to its starting value.
*/
void dlg_coloursel_start(union control *ctrl, void *dlg,
int r, int g, int b);
int dlg_coloursel_results(union control *ctrl, void *dlg,
int *r, int *g, int *b);
/*
* This routine is used by the platform-independent code to
* indicate that the value of a particular control is likely to
* have changed. It triggers a call of the handler for that control
* with `event' set to EVENT_REFRESH.
*
* If `ctrl' is NULL, _all_ controls in the dialog get refreshed
* (for loading or saving entire sets of settings).
*/
void dlg_refresh(union control *ctrl, void *dlg);
/*
* Standard helper functions for reading a controlbox structure.
*/
/*
* Find the index of next controlset in a controlbox for a given
* path, or -1 if no such controlset exists. If -1 is passed as
* input, finds the first. Intended usage is something like
*
* for (index=-1; (index=ctrl_find_path(ctrlbox, index, path)) >= 0 ;) {
* ... process this controlset ...
* }
*/
int ctrl_find_path(struct controlbox *b, const char *path, int index);
int ctrl_path_elements(const char *path);
/* Return the number of matching path elements at the starts of p1 and p2,
* or INT_MAX if the paths are identical. */
int ctrl_path_compare(const char *p1, const char *p2);