Newer
Older
/*
* config.c - the platform-independent parts of the PuTTY
* configuration box.
*/
#include <assert.h>
#include <stdlib.h>
#include "putty.h"
#include "dialog.h"
#include "storage.h"
#define PRINTER_DISABLED_STRING "None (printing disabled)"
#define HOST_BOX_TITLE "Host Name (or IP address)"
#define PORT_BOX_TITLE "Port"
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
void conf_radiobutton_handler(union control *ctrl, void *dlg,
void *data, int event)
{
int button;
Conf *conf = (Conf *)data;
/*
* For a standard radio button set, the context parameter gives
* the primary key (CONF_foo), and the extra data per button
* gives the value the target field should take if that button
* is the one selected.
*/
if (event == EVENT_REFRESH) {
int val = conf_get_int(conf, ctrl->radio.context.i);
for (button = 0; button < ctrl->radio.nbuttons; button++)
if (val == ctrl->radio.buttondata[button].i)
break;
/* We expected that `break' to happen, in all circumstances. */
assert(button < ctrl->radio.nbuttons);
dlg_radiobutton_set(ctrl, dlg, button);
} else if (event == EVENT_VALCHANGE) {
button = dlg_radiobutton_get(ctrl, dlg);
assert(button >= 0 && button < ctrl->radio.nbuttons);
conf_set_int(conf, ctrl->radio.context.i,
ctrl->radio.buttondata[button].i);
}
}
#define CHECKBOX_INVERT (1<<30)
void conf_checkbox_handler(union control *ctrl, void *dlg,
void *data, int event)
{
int key, invert;
Conf *conf = (Conf *)data;
/*
* For a standard checkbox, the context parameter gives the
* primary key (CONF_foo), optionally ORed with CHECKBOX_INVERT.
*/
key = ctrl->checkbox.context.i;
if (key & CHECKBOX_INVERT) {
key &= ~CHECKBOX_INVERT;
invert = 1;
} else
invert = 0;
/*
* C lacks a logical XOR, so the following code uses the idiom
* (!a ^ !b) to obtain the logical XOR of a and b. (That is, 1
* iff exactly one of a and b is nonzero, otherwise 0.)
*/
if (event == EVENT_REFRESH) {
int val = conf_get_int(conf, key);
dlg_checkbox_set(ctrl, dlg, (!val ^ !invert));
} else if (event == EVENT_VALCHANGE) {
conf_set_int(conf, key, !dlg_checkbox_get(ctrl,dlg) ^ !invert);
}
}
void conf_editbox_handler(union control *ctrl, void *dlg,
void *data, int event)
{
/*
* The standard edit-box handler expects the main `context'
* field to contain the primary key. The secondary `context2'
* field indicates the type of this field:
*
* - if context2 > 0, the field is a string.
* - if context2 == -1, the field is an int and the edit box
* is numeric.
* - if context2 < -1, the field is an int and the edit box is
* _floating_, and (-context2) gives the scale. (E.g. if
* context2 == -1000, then typing 1.2 into the box will set
* the field to 1200.)
*/
int key = ctrl->editbox.context.i;
int length = ctrl->editbox.context2.i;
Conf *conf = (Conf *)data;
if (length > 0) {
if (event == EVENT_REFRESH) {
char *field = conf_get_str(conf, key);
dlg_editbox_set(ctrl, dlg, field);
} else if (event == EVENT_VALCHANGE) {
char *field = dlg_editbox_get(ctrl, dlg);
conf_set_str(conf, key, field);
sfree(field);
}
} else if (length < 0) {
if (event == EVENT_REFRESH) {
char str[80];
int value = conf_get_int(conf, key);
if (length == -1)
sprintf(str, "%d", value);
else
sprintf(str, "%g", (double)value / (double)(-length));
dlg_editbox_set(ctrl, dlg, str);
} else if (event == EVENT_VALCHANGE) {
char *str = dlg_editbox_get(ctrl, dlg);
if (length == -1)
conf_set_int(conf, key, atoi(str));
else
conf_set_int(conf, key, (int)((-length) * atof(str)));
sfree(str);
}
}
}
void conf_filesel_handler(union control *ctrl, void *dlg,
void *data, int event)
{
int key = ctrl->fileselect.context.i;
Conf *conf = (Conf *)data;
if (event == EVENT_REFRESH) {
dlg_filesel_set(ctrl, dlg, *conf_get_filename(conf, key));
} else if (event == EVENT_VALCHANGE) {
Filename filename;
dlg_filesel_get(ctrl, dlg, &filename);
conf_set_filename(conf, key, &filename);
/* If Filenames ever become dynamic, free this one. */
}
}
void conf_fontsel_handler(union control *ctrl, void *dlg,
void *data, int event)
{
int key = ctrl->fontselect.context.i;
Conf *conf = (Conf *)data;
if (event == EVENT_REFRESH) {
dlg_fontsel_set(ctrl, dlg, conf_get_fontspec(conf, key));
} else if (event == EVENT_VALCHANGE) {
FontSpec *fontspec = dlg_fontsel_get(ctrl, dlg);
conf_set_fontspec(conf, key, fontspec);
fontspec_free(fontspec);
}
}
static void config_host_handler(union control *ctrl, void *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
/*
* This function works just like the standard edit box handler,
* only it has to choose the control's label and text from two
* different places depending on the protocol.
*/
if (event == EVENT_REFRESH) {
if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) {
/*
* This label text is carefully chosen to contain an n,
* since that's the shortcut for the host name control.
*/
dlg_label_change(ctrl, dlg, "Serial line");
dlg_editbox_set(ctrl, dlg, conf_get_str(conf, CONF_serline));
} else {
dlg_label_change(ctrl, dlg, HOST_BOX_TITLE);
dlg_editbox_set(ctrl, dlg, conf_get_str(conf, CONF_host));
}
} else if (event == EVENT_VALCHANGE) {
char *s = dlg_editbox_get(ctrl, dlg);
if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
conf_set_str(conf, CONF_serline, s);
else
conf_set_str(conf, CONF_host, s);
sfree(s);
}
}
static void config_port_handler(union control *ctrl, void *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
char buf[80];
/*
* This function works similarly to the standard edit box handler,
* only it has to choose the control's label and text from two
* different places depending on the protocol.
*/
if (event == EVENT_REFRESH) {
if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL) {
/*
* This label text is carefully chosen to contain a p,
* since that's the shortcut for the port control.
*/
dlg_label_change(ctrl, dlg, "Speed");
sprintf(buf, "%d", conf_get_int(conf, CONF_serspeed));
} else {
dlg_label_change(ctrl, dlg, PORT_BOX_TITLE);
if (conf_get_int(conf, CONF_port) != 0)
sprintf(buf, "%d", conf_get_int(conf, CONF_port));
else
/* Display an (invalid) port of 0 as blank */
buf[0] = '\0';
}
dlg_editbox_set(ctrl, dlg, buf);
} else if (event == EVENT_VALCHANGE) {
char *s = dlg_editbox_get(ctrl, dlg);
int i = atoi(s);
sfree(s);
if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)
conf_set_int(conf, CONF_serspeed, i);
else
conf_set_int(conf, CONF_port, i);
}
}
struct hostport {
union control *host, *port;
};
/*
* We export this function so that platform-specific config
* routines can use it to conveniently identify the protocol radio
* buttons in order to add to them.
*/
void config_protocolbuttons_handler(union control *ctrl, void *dlg,
void *data, int event)
{
int button;
Conf *conf = (Conf *)data;
struct hostport *hp = (struct hostport *)ctrl->radio.context.p;
/*
* This function works just like the standard radio-button
* handler, except that it also has to change the setting of
* the port box, and refresh both host and port boxes when. We
* expect the context parameter to point at a hostport
* structure giving the `union control's for both.
*/
if (event == EVENT_REFRESH) {
int protocol = conf_get_int(conf, CONF_protocol);
for (button = 0; button < ctrl->radio.nbuttons; button++)
if (protocol == ctrl->radio.buttondata[button].i)
break;
/* We expected that `break' to happen, in all circumstances. */
assert(button < ctrl->radio.nbuttons);
dlg_radiobutton_set(ctrl, dlg, button);
} else if (event == EVENT_VALCHANGE) {
int oldproto = conf_get_int(conf, CONF_protocol);
int newproto, port;
button = dlg_radiobutton_get(ctrl, dlg);
assert(button >= 0 && button < ctrl->radio.nbuttons);
newproto = ctrl->radio.buttondata[button].i;
conf_set_int(conf, CONF_protocol, newproto);
if (oldproto != newproto) {
Backend *ob = backend_from_proto(oldproto);
Backend *nb = backend_from_proto(newproto);
assert(ob);
assert(nb);
/* Iff the user hasn't changed the port from the old protocol's
* default, update it with the new protocol's default.
* (This includes a "default" of 0, implying that there is no
* sensible default for that protocol; in this case it's
* displayed as a blank.)
* This helps with the common case of tabbing through the
* controls in order and setting a non-default port before
* getting to the protocol; we want that non-default port
* to be preserved. */
port = conf_get_int(conf, CONF_port);
if (port == ob->default_port)
conf_set_int(conf, CONF_port, nb->default_port);
dlg_refresh(hp->host, dlg);
dlg_refresh(hp->port, dlg);
}
}
static void loggingbuttons_handler(union control *ctrl, void *dlg,
void *data, int event)
{
int button;
Conf *conf = (Conf *)data;
/* This function works just like the standard radio-button handler,
* but it has to fall back to "no logging" in situations where the
* configured logging type isn't applicable.
*/
if (event == EVENT_REFRESH) {
int logtype = conf_get_int(conf, CONF_logtype);
for (button = 0; button < ctrl->radio.nbuttons; button++)
if (logtype == ctrl->radio.buttondata[button].i)
/* We fell off the end, so we lack the configured logging type */
if (button == ctrl->radio.nbuttons) {
button = 0;
conf_set_int(conf, CONF_logtype, LGTYP_NONE);
}
dlg_radiobutton_set(ctrl, dlg, button);
} else if (event == EVENT_VALCHANGE) {
button = dlg_radiobutton_get(ctrl, dlg);
assert(button >= 0 && button < ctrl->radio.nbuttons);
conf_set_int(conf, CONF_logtype, ctrl->radio.buttondata[button].i);
static void numeric_keypad_handler(union control *ctrl, void *dlg,
void *data, int event)
{
int button;
Conf *conf = (Conf *)data;
/*
* This function works much like the standard radio button
* handler, but it has to handle two fields in Conf.
*/
if (event == EVENT_REFRESH) {
if (conf_get_int(conf, CONF_nethack_keypad))
button = 2;
else if (conf_get_int(conf, CONF_app_keypad))
button = 1;
else
button = 0;
assert(button < ctrl->radio.nbuttons);
dlg_radiobutton_set(ctrl, dlg, button);
} else if (event == EVENT_VALCHANGE) {
button = dlg_radiobutton_get(ctrl, dlg);
assert(button >= 0 && button < ctrl->radio.nbuttons);
if (button == 2) {
conf_set_int(conf, CONF_app_keypad, FALSE);
conf_set_int(conf, CONF_nethack_keypad, TRUE);
} else {
conf_set_int(conf, CONF_app_keypad, (button != 0));
conf_set_int(conf, CONF_nethack_keypad, FALSE);
}
}
}
static void cipherlist_handler(union control *ctrl, void *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
if (event == EVENT_REFRESH) {
int i;
static const struct { char *s; int c; } ciphers[] = {
{ "3DES", CIPHER_3DES },
{ "Blowfish", CIPHER_BLOWFISH },
{ "DES", CIPHER_DES },
{ "AES (SSH-2 only)", CIPHER_AES },
{ "Arcfour (SSH-2 only)", CIPHER_ARCFOUR },
{ "-- warn below here --", CIPHER_WARN }
};
/* Set up the "selected ciphers" box. */
/* (cipherlist assumed to contain all ciphers) */
dlg_update_start(ctrl, dlg);
dlg_listbox_clear(ctrl, dlg);
for (i = 0; i < CIPHER_MAX; i++) {
int c = conf_get_int_int(conf, CONF_ssh_cipherlist, i);
int j;
char *cstr = NULL;
for (j = 0; j < (sizeof ciphers) / (sizeof ciphers[0]); j++) {
if (ciphers[j].c == c) {
cstr = ciphers[j].s;
break;
}
}
dlg_listbox_addwithid(ctrl, dlg, cstr, c);
}
dlg_update_done(ctrl, dlg);
} else if (event == EVENT_VALCHANGE) {
int i;
/* Update array to match the list box. */
for (i=0; i < CIPHER_MAX; i++)
conf_set_int_int(conf, CONF_ssh_cipherlist, i,
dlg_listbox_getid(ctrl, dlg, i));
}
}
#ifndef NO_GSSAPI
static void gsslist_handler(union control *ctrl, void *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
if (event == EVENT_REFRESH) {
int i;
dlg_update_start(ctrl, dlg);
dlg_listbox_clear(ctrl, dlg);
for (i = 0; i < ngsslibs; i++) {
int id = conf_get_int_int(conf, CONF_ssh_gsslist, i);
assert(id >= 0 && id < ngsslibs);
dlg_listbox_addwithid(ctrl, dlg, gsslibnames[id], id);
}
dlg_update_done(ctrl, dlg);
} else if (event == EVENT_VALCHANGE) {
int i;
/* Update array to match the list box. */
for (i=0; i < ngsslibs; i++)
conf_set_int_int(conf, CONF_ssh_gsslist, i,
dlg_listbox_getid(ctrl, dlg, i));
}
}
#endif

Jacob Nevins
committed
static void kexlist_handler(union control *ctrl, void *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;

Jacob Nevins
committed
if (event == EVENT_REFRESH) {
int i;
static const struct { char *s; int k; } kexes[] = {
{ "Diffie-Hellman group 1", KEX_DHGROUP1 },
{ "Diffie-Hellman group 14", KEX_DHGROUP14 },
{ "Diffie-Hellman group exchange", KEX_DHGEX },
{ "RSA-based key exchange", KEX_RSA },

Jacob Nevins
committed
{ "-- warn below here --", KEX_WARN }
};
/* Set up the "kex preference" box. */
/* (kexlist assumed to contain all algorithms) */
dlg_update_start(ctrl, dlg);
dlg_listbox_clear(ctrl, dlg);
for (i = 0; i < KEX_MAX; i++) {
int k = conf_get_int_int(conf, CONF_ssh_kexlist, i);

Jacob Nevins
committed
int j;
char *kstr = NULL;
for (j = 0; j < (sizeof kexes) / (sizeof kexes[0]); j++) {
if (kexes[j].k == k) {
kstr = kexes[j].s;
break;
}
}
dlg_listbox_addwithid(ctrl, dlg, kstr, k);
}
dlg_update_done(ctrl, dlg);
} else if (event == EVENT_VALCHANGE) {
int i;
/* Update array to match the list box. */
for (i=0; i < KEX_MAX; i++)
conf_set_int_int(conf, CONF_ssh_kexlist, i,
dlg_listbox_getid(ctrl, dlg, i));

Jacob Nevins
committed
}
}
static void printerbox_handler(union control *ctrl, void *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
if (event == EVENT_REFRESH) {
int nprinters, i;
printer_enum *pe;
char *printer;
dlg_update_start(ctrl, dlg);
/*
* Some backends may wish to disable the drop-down list on
* this edit box. Be prepared for this.
*/
if (ctrl->editbox.has_list) {
dlg_listbox_clear(ctrl, dlg);
dlg_listbox_add(ctrl, dlg, PRINTER_DISABLED_STRING);
pe = printer_start_enum(&nprinters);
for (i = 0; i < nprinters; i++)
dlg_listbox_add(ctrl, dlg, printer_get_name(pe, i));
printer_finish_enum(pe);
}
printer = conf_get_str(conf, CONF_printer);
if (!printer)
printer = PRINTER_DISABLED_STRING;
dlg_editbox_set(ctrl, dlg, printer);
dlg_update_done(ctrl, dlg);
} else if (event == EVENT_VALCHANGE) {
char *printer = dlg_editbox_get(ctrl, dlg);
if (!strcmp(printer, PRINTER_DISABLED_STRING))
printer[0] = '\0';
conf_set_str(conf, CONF_printer, printer);
sfree(printer);
}
}
static void codepage_handler(union control *ctrl, void *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
if (event == EVENT_REFRESH) {
int i;
const char *cp, *thiscp;
dlg_update_start(ctrl, dlg);
thiscp = cp_name(decode_codepage(conf_get_str(conf,
CONF_line_codepage)));
dlg_listbox_clear(ctrl, dlg);
for (i = 0; (cp = cp_enumerate(i)) != NULL; i++)
dlg_listbox_add(ctrl, dlg, cp);
dlg_editbox_set(ctrl, dlg, thiscp);
conf_set_str(conf, CONF_line_codepage, thiscp);
dlg_update_done(ctrl, dlg);
} else if (event == EVENT_VALCHANGE) {
char *codepage = dlg_editbox_get(ctrl, dlg);
conf_set_str(conf, CONF_line_codepage,
cp_name(decode_codepage(codepage)));
sfree(codepage);
}
}
static void sshbug_handler(union control *ctrl, void *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
if (event == EVENT_REFRESH) {
dlg_update_start(ctrl, dlg);
dlg_listbox_clear(ctrl, dlg);
dlg_listbox_addwithid(ctrl, dlg, "Auto", AUTO);
dlg_listbox_addwithid(ctrl, dlg, "Off", FORCE_OFF);
dlg_listbox_addwithid(ctrl, dlg, "On", FORCE_ON);
switch (conf_get_int(conf, ctrl->listbox.context.i)) {
case AUTO: dlg_listbox_select(ctrl, dlg, 0); break;
case FORCE_OFF: dlg_listbox_select(ctrl, dlg, 1); break;
case FORCE_ON: dlg_listbox_select(ctrl, dlg, 2); break;
}
dlg_update_done(ctrl, dlg);
} else if (event == EVENT_SELCHANGE) {
int i = dlg_listbox_index(ctrl, dlg);
if (i < 0)
i = AUTO;
else
i = dlg_listbox_getid(ctrl, dlg, i);
conf_set_int(conf, ctrl->listbox.context.i, i);
}
}
#define SAVEDSESSION_LEN 2048
struct sessionsaver_data {
union control *editbox, *listbox, *loadbutton, *savebutton, *delbutton;
union control *okbutton, *cancelbutton;

Jacob Nevins
committed
struct sesslist sesslist;

Jacob Nevins
committed
int midsession;
};
/*
* Helper function to load the session selected in the list box, if
* any, as this is done in more than one place below. Returns 0 for
* failure.
*/
static int load_selected_session(struct sessionsaver_data *ssd,
char *savedsession,
void *dlg, Conf *conf, int *maybe_launch)
{
int i = dlg_listbox_index(ssd->listbox, dlg);
int isdef;
if (i < 0) {
dlg_beep(dlg);
return 0;
}

Jacob Nevins
committed
isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");
load_settings(ssd->sesslist.sessions[i], conf);
if (!isdef) {

Jacob Nevins
committed
strncpy(savedsession, ssd->sesslist.sessions[i],
SAVEDSESSION_LEN);
savedsession[SAVEDSESSION_LEN-1] = '\0';
if (maybe_launch)
*maybe_launch = TRUE;
} else {
savedsession[0] = '\0';
if (maybe_launch)
*maybe_launch = FALSE;
}
dlg_refresh(NULL, dlg);
/* Restore the selection, which might have been clobbered by
* changing the value of the edit box. */
dlg_listbox_select(ssd->listbox, dlg, i);
return 1;
}
static void sessionsaver_handler(union control *ctrl, void *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
struct sessionsaver_data *ssd =
(struct sessionsaver_data *)ctrl->generic.context.p;
char *savedsession;
/*
* The first time we're called in a new dialog, we must
* allocate space to store the current contents of the saved
* session edit box (since it must persist even when we switch
* panels, but is not part of the Conf).
*
* FIXME: this is disgusting, and we'd do much better to have
* the persistent storage be dynamically allocated and get rid
* of the arbitrary limit SAVEDSESSION_LEN. To do that would
* require a means of making sure the memory gets freed at the
* appropriate moment.
if (!ssd->editbox) {
savedsession = NULL;
} else if (!dlg_get_privdata(ssd->editbox, dlg)) {
savedsession = (char *)
dlg_alloc_privdata(ssd->editbox, dlg, SAVEDSESSION_LEN);
savedsession[0] = '\0';
} else {
savedsession = dlg_get_privdata(ssd->editbox, dlg);
}
if (event == EVENT_REFRESH) {
if (ctrl == ssd->editbox) {
dlg_editbox_set(ctrl, dlg, savedsession);
} else if (ctrl == ssd->listbox) {
int i;
dlg_update_start(ctrl, dlg);
dlg_listbox_clear(ctrl, dlg);

Jacob Nevins
committed
for (i = 0; i < ssd->sesslist.nsessions; i++)
dlg_listbox_add(ctrl, dlg, ssd->sesslist.sessions[i]);
dlg_update_done(ctrl, dlg);
}
} else if (event == EVENT_VALCHANGE) {
int top, bottom, halfway, i;
if (ctrl == ssd->editbox) {
char *tmp = dlg_editbox_get(ctrl, dlg);
strncpy(savedsession, tmp, SAVEDSESSION_LEN);
sfree(tmp);
top = ssd->sesslist.nsessions;
bottom = -1;
while (top-bottom > 1) {
halfway = (top+bottom)/2;
i = strcmp(savedsession, ssd->sesslist.sessions[halfway]);
if (i <= 0 ) {
top = halfway;
} else {
bottom = halfway;
}
}
if (top == ssd->sesslist.nsessions) {
top -= 1;
}
dlg_listbox_select(ssd->listbox, dlg, top);
}
} else if (event == EVENT_ACTION) {
int mbl = FALSE;

Jacob Nevins
committed
if (!ssd->midsession &&
(ctrl == ssd->listbox ||
(ssd->loadbutton && ctrl == ssd->loadbutton))) {
/*
* The user has double-clicked a session, or hit Load.
* We must load the selected session, and then
* terminate the configuration dialog _if_ there was a
* double-click on the list box _and_ that session
* contains a hostname.
*/
if (load_selected_session(ssd, savedsession, dlg, conf, &mbl) &&
(mbl && ctrl == ssd->listbox && conf_launchable(conf))) {
dlg_end(dlg, 1); /* it's all over, and succeeded */
}
} else if (ctrl == ssd->savebutton) {
int isdef = !strcmp(savedsession, "Default Settings");
if (!savedsession[0]) {
int i = dlg_listbox_index(ssd->listbox, dlg);
if (i < 0) {
dlg_beep(dlg);
return;
}

Jacob Nevins
committed
isdef = !strcmp(ssd->sesslist.sessions[i], "Default Settings");
if (!isdef) {

Jacob Nevins
committed
strncpy(savedsession, ssd->sesslist.sessions[i],
SAVEDSESSION_LEN);
savedsession[SAVEDSESSION_LEN-1] = '\0';
} else {
savedsession[0] = '\0';
}
}
char *errmsg = save_settings(savedsession, conf);
if (errmsg) {
dlg_error_msg(dlg, errmsg);
sfree(errmsg);
}
}

Jacob Nevins
committed
get_sesslist(&ssd->sesslist, FALSE);
get_sesslist(&ssd->sesslist, TRUE);
dlg_refresh(ssd->editbox, dlg);
dlg_refresh(ssd->listbox, dlg);

Jacob Nevins
committed
} else if (!ssd->midsession &&
ssd->delbutton && ctrl == ssd->delbutton) {
int i = dlg_listbox_index(ssd->listbox, dlg);
if (i <= 0) {
dlg_beep(dlg);
} else {

Jacob Nevins
committed
del_settings(ssd->sesslist.sessions[i]);
get_sesslist(&ssd->sesslist, FALSE);
get_sesslist(&ssd->sesslist, TRUE);
dlg_refresh(ssd->listbox, dlg);
}
} else if (ctrl == ssd->okbutton) {

Jacob Nevins
committed
if (ssd->midsession) {
/* In a mid-session Change Settings, Apply is always OK. */
dlg_end(dlg, 1);
return;
}
/*
* Annoying special case. If the `Open' button is
* pressed while no host name is currently set, _and_
* the session list previously had the focus, _and_
* there was a session selected in that which had a
* valid host name in it, then load it and go.
*/
if (dlg_last_focused(ctrl, dlg) == ssd->listbox &&
!conf_launchable(conf)) {
Conf *conf2 = conf_new();
int mbl = FALSE;
if (!load_selected_session(ssd, savedsession, dlg,
conf2, &mbl)) {
dlg_beep(dlg);
conf_free(conf2);
return;
}
/* If at this point we have a valid session, go! */
if (mbl && conf_launchable(conf2)) {
conf_copy_into(conf, conf2);
dlg_end(dlg, 1);
} else
dlg_beep(dlg);
conf_free(conf2);
}
/*
* Otherwise, do the normal thing: if we have a valid
* session, get going.
*/
if (conf_launchable(conf)) {
dlg_end(dlg, 1);
} else
dlg_beep(dlg);
} else if (ctrl == ssd->cancelbutton) {
dlg_end(dlg, 0);
}
}
}
struct charclass_data {
union control *listbox, *editbox, *button;
};
static void charclass_handler(union control *ctrl, void *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
struct charclass_data *ccd =
(struct charclass_data *)ctrl->generic.context.p;
if (event == EVENT_REFRESH) {
if (ctrl == ccd->listbox) {
int i;
dlg_update_start(ctrl, dlg);
dlg_listbox_clear(ctrl, dlg);
for (i = 0; i < 128; i++) {
char str[100];
sprintf(str, "%d\t(0x%02X)\t%c\t%d", i, i,
(i >= 0x21 && i != 0x7F) ? i : ' ',
conf_get_int_int(conf, CONF_wordness, i));
dlg_listbox_add(ctrl, dlg, str);
}
dlg_update_done(ctrl, dlg);
}
} else if (event == EVENT_ACTION) {
if (ctrl == ccd->button) {
char *str;
int i, n;
str = dlg_editbox_get(ccd->editbox, dlg);
n = atoi(str);
sfree(str);
for (i = 0; i < 128; i++) {
if (dlg_listbox_issel(ccd->listbox, dlg, i))
conf_set_int_int(conf, CONF_wordness, i, n);
}
dlg_refresh(ccd->listbox, dlg);
}
}
}
struct colour_data {
union control *listbox, *redit, *gedit, *bedit, *button;
};
static const char *const colours[] = {
"Default Foreground", "Default Bold Foreground",
"Default Background", "Default Bold Background",
"Cursor Text", "Cursor Colour",
"ANSI Black", "ANSI Black Bold",
"ANSI Red", "ANSI Red Bold",
"ANSI Green", "ANSI Green Bold",
"ANSI Yellow", "ANSI Yellow Bold",
"ANSI Blue", "ANSI Blue Bold",
"ANSI Magenta", "ANSI Magenta Bold",
"ANSI Cyan", "ANSI Cyan Bold",
"ANSI White", "ANSI White Bold"
};
static void colour_handler(union control *ctrl, void *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
struct colour_data *cd =
(struct colour_data *)ctrl->generic.context.p;
int update = FALSE, clear = FALSE, r, g, b;
if (event == EVENT_REFRESH) {
if (ctrl == cd->listbox) {
int i;
dlg_update_start(ctrl, dlg);
dlg_listbox_clear(ctrl, dlg);
for (i = 0; i < lenof(colours); i++)
dlg_listbox_add(ctrl, dlg, colours[i]);
dlg_update_done(ctrl, dlg);
clear = TRUE;
update = TRUE;
}
} else if (event == EVENT_SELCHANGE) {
if (ctrl == cd->listbox) {
/* The user has selected a colour. Update the RGB text. */
int i = dlg_listbox_index(ctrl, dlg);
if (i < 0) {
clear = TRUE;
} else {
clear = FALSE;
r = conf_get_int_int(conf, CONF_colours, i*3+0);
g = conf_get_int_int(conf, CONF_colours, i*3+0);
b = conf_get_int_int(conf, CONF_colours, i*3+0);
}
update = TRUE;
}
} else if (event == EVENT_VALCHANGE) {
if (ctrl == cd->redit || ctrl == cd->gedit || ctrl == cd->bedit) {
/* The user has changed the colour using the edit boxes. */
char *str;
int i, cval;
str = dlg_editbox_get(ctrl, dlg);
cval = atoi(str);
sfree(str);
if (cval > 255) cval = 255;
if (cval < 0) cval = 0;
i = dlg_listbox_index(cd->listbox, dlg);
if (i >= 0) {
if (ctrl == cd->redit)
conf_set_int_int(conf, CONF_colours, i*3+0, cval);
else if (ctrl == cd->gedit)
conf_set_int_int(conf, CONF_colours, i*3+1, cval);
else if (ctrl == cd->bedit)
conf_set_int_int(conf, CONF_colours, i*3+2, cval);
}
}
} else if (event == EVENT_ACTION) {
if (ctrl == cd->button) {
int i = dlg_listbox_index(cd->listbox, dlg);
if (i < 0) {
dlg_beep(dlg);
return;
}
/*
* Start a colour selector, which will send us an
* EVENT_CALLBACK when it's finished and allow us to
* pick up the results.
*/
dlg_coloursel_start(ctrl, dlg,
conf_get_int_int(conf, CONF_colours, i*3+0),
conf_get_int_int(conf, CONF_colours, i*3+1),
conf_get_int_int(conf, CONF_colours, i*3+2));
}
} else if (event == EVENT_CALLBACK) {
if (ctrl == cd->button) {
int i = dlg_listbox_index(cd->listbox, dlg);
/*
* Collect the results of the colour selector. Will
* return nonzero on success, or zero if the colour
* selector did nothing (user hit Cancel, for example).
*/
if (dlg_coloursel_results(ctrl, dlg, &r, &g, &b)) {
conf_set_int_int(conf, CONF_colours, i*3+0, r);
conf_set_int_int(conf, CONF_colours, i*3+0, g);
conf_set_int_int(conf, CONF_colours, i*3+0, b);
update = TRUE;
}
}
}
if (update) {
if (clear) {
dlg_editbox_set(cd->redit, dlg, "");
dlg_editbox_set(cd->gedit, dlg, "");
dlg_editbox_set(cd->bedit, dlg, "");
} else {
char buf[40];
sprintf(buf, "%d", r); dlg_editbox_set(cd->redit, dlg, buf);
sprintf(buf, "%d", g); dlg_editbox_set(cd->gedit, dlg, buf);
sprintf(buf, "%d", b); dlg_editbox_set(cd->bedit, dlg, buf);
}
}
}
struct ttymodes_data {
union control *modelist, *valradio, *valbox;
union control *addbutton, *rembutton, *listbox;
};
static void ttymodes_handler(union control *ctrl, void *dlg,
void *data, int event)
{
Conf *conf = (Conf *)data;
struct ttymodes_data *td =
(struct ttymodes_data *)ctrl->generic.context.p;
if (event == EVENT_REFRESH) {
if (ctrl == td->listbox) {
char *key, *val;
dlg_update_start(ctrl, dlg);
dlg_listbox_clear(ctrl, dlg);
for (val = conf_get_str_strs(conf, CONF_ttymodes, NULL, &key);
val != NULL;
val = conf_get_str_strs(conf, CONF_ttymodes, key, &key)) {
char *disp = dupprintf("%s\t%s", key,
(val[0] == 'A') ? "(auto)" : val+1);
dlg_listbox_add(ctrl, dlg, disp);
sfree(disp);
}
dlg_update_done(ctrl, dlg);
} else if (ctrl == td->modelist) {
int i;
dlg_update_start(ctrl, dlg);
dlg_listbox_clear(ctrl, dlg);
for (i = 0; ttymodes[i]; i++)
dlg_listbox_add(ctrl, dlg, ttymodes[i]);
dlg_listbox_select(ctrl, dlg, 0); /* *shrug* */
dlg_update_done(ctrl, dlg);
} else if (ctrl == td->valradio) {
dlg_radiobutton_set(ctrl, dlg, 0);
}
} else if (event == EVENT_ACTION) {
if (ctrl == td->addbutton) {
int ind = dlg_listbox_index(td->modelist, dlg);
if (ind >= 0) {
char type = dlg_radiobutton_get(td->valradio, dlg) ? 'V' : 'A';
const char *key;
char *str, *val;
/* Construct new entry */
key = ttymodes[ind];
str = dlg_editbox_get(td->valbox, dlg);
val = dupprintf("%c%s", type, str);
sfree(str);
conf_set_str_str(conf, CONF_ttymodes, key, val);
sfree(val);
dlg_refresh(td->listbox, dlg);
} else
dlg_beep(dlg);
} else if (ctrl == td->rembutton) {
int i = 0;
char *key, *val;
int multisel = dlg_listbox_index(td->listbox, dlg) < 0;
for (val = conf_get_str_strs(conf, CONF_ttymodes, NULL, &key);
val != NULL;
val = conf_get_str_strs(conf, CONF_ttymodes, key, &key)) {
if (dlg_listbox_issel(td->listbox, dlg, i)) {
if (!multisel) {
/* Populate controls with entry we're about to
* delete, for ease of editing.
* (If multiple entries were selected, don't
* touch the controls.) */
int ind = 0;
val++;
while (ttymodes[ind]) {
if (!strcmp(ttymodes[ind], key))
break;
ind++;