+#define HOST_BOX_TITLE "Host Name (or IP address)"
+#define PORT_BOX_TITLE "Port"
+
+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);
+ conf_set_filename(conf, key, filename);
+ filename_free(filename);
+ }
+}
+
+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,