+static int tree_grab_focus(struct dlgparam *dp)
+{
+ int i, f;
+
+ /*
+ * See if any of the treeitems has the focus.
+ */
+ f = -1;
+ for (i = 0; i < dp->ntreeitems; i++)
+ if (GTK_WIDGET_HAS_FOCUS(dp->treeitems[i])) {
+ f = i;
+ break;
+ }
+
+ if (f >= 0)
+ return FALSE;
+ else {
+ gtk_widget_grab_focus(dp->currtreeitem);
+ return TRUE;
+ }
+}
+
+gint tree_focus(GtkContainer *container, GtkDirectionType direction,
+ gpointer data)
+{
+ struct dlgparam *dp = (struct dlgparam *)data;
+
+ gtk_signal_emit_stop_by_name(GTK_OBJECT(container), "focus");
+ /*
+ * If there's a focused treeitem, we return FALSE to cause the
+ * focus to move on to some totally other control. If not, we
+ * focus the selected one.
+ */
+ return tree_grab_focus(dp);
+}
+
+int win_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data)
+{
+ struct dlgparam *dp = (struct dlgparam *)data;
+
+ if (event->keyval == GDK_Escape && dp->cancelbutton) {
+ gtk_signal_emit_by_name(GTK_OBJECT(dp->cancelbutton), "clicked");
+ return TRUE;
+ }
+
+ if ((event->state & GDK_MOD1_MASK) &&
+ (unsigned char)event->string[0] > 0 &&
+ (unsigned char)event->string[0] <= 127) {
+ int schr = (unsigned char)event->string[0];
+ struct Shortcut *sc = &dp->shortcuts->sc[schr];
+
+ switch (sc->action) {
+ case SHORTCUT_TREE:
+ tree_grab_focus(dp);
+ break;
+ case SHORTCUT_FOCUS:
+ gtk_widget_grab_focus(sc->widget);
+ break;
+ case SHORTCUT_UCTRL:
+ /*
+ * We must do something sensible with a uctrl.
+ * Precisely what this is depends on the type of
+ * control.
+ */
+ switch (sc->uc->ctrl->generic.type) {
+ case CTRL_CHECKBOX:
+ case CTRL_BUTTON:
+ /* Check boxes and buttons get the focus _and_ get toggled. */
+ gtk_widget_grab_focus(sc->uc->toplevel);
+ gtk_signal_emit_by_name(GTK_OBJECT(sc->uc->toplevel),
+ "clicked");
+ break;
+ case CTRL_FILESELECT:
+ case CTRL_FONTSELECT:
+ /* File/font selectors have their buttons pressed (ooer),
+ * and focus transferred to the edit box. */
+ gtk_signal_emit_by_name(GTK_OBJECT(sc->uc->button),
+ "clicked");
+ gtk_widget_grab_focus(sc->uc->entry);
+ break;
+ case CTRL_RADIO:
+ /*
+ * Radio buttons are fun, because they have
+ * multiple shortcuts. We must find whether the
+ * activated shortcut is the shortcut for the whole
+ * group, or for a particular button. In the former
+ * case, we find the currently selected button and
+ * focus it; in the latter, we focus-and-click the
+ * button whose shortcut was pressed.
+ */
+ if (schr == sc->uc->ctrl->radio.shortcut) {
+ int i;
+ for (i = 0; i < sc->uc->ctrl->radio.nbuttons; i++)
+ if (gtk_toggle_button_get_active
+ (GTK_TOGGLE_BUTTON(sc->uc->buttons[i]))) {
+ gtk_widget_grab_focus(sc->uc->buttons[i]);
+ }
+ } else if (sc->uc->ctrl->radio.shortcuts) {
+ int i;
+ for (i = 0; i < sc->uc->ctrl->radio.nbuttons; i++)
+ if (schr == sc->uc->ctrl->radio.shortcuts[i]) {
+ gtk_widget_grab_focus(sc->uc->buttons[i]);
+ gtk_signal_emit_by_name
+ (GTK_OBJECT(sc->uc->buttons[i]), "clicked");
+ }
+ }
+ break;
+ case CTRL_LISTBOX:
+ /*
+ * If the list is really an option menu, we focus
+ * and click it. Otherwise we tell it to focus one
+ * of its children, which appears to do the Right
+ * Thing.
+ */
+ if (sc->uc->optmenu) {
+ GdkEventButton bev;
+ gint returnval;
+
+ gtk_widget_grab_focus(sc->uc->optmenu);
+ /* Option menus don't work using the "clicked" signal.
+ * We need to manufacture a button press event :-/ */
+ bev.type = GDK_BUTTON_PRESS;
+ bev.button = 1;
+ gtk_signal_emit_by_name(GTK_OBJECT(sc->uc->optmenu),
+ "button_press_event",
+ &bev, &returnval);
+ } else {
+ assert(sc->uc->list != NULL);
+
+ gtk_container_focus(GTK_CONTAINER(sc->uc->list),
+ GTK_DIR_TAB_FORWARD);
+ }
+ break;
+ }
+ break;
+ }
+ }
+
+ return FALSE;
+}
+
+int tree_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data)
+{
+ struct dlgparam *dp = (struct dlgparam *)data;
+
+ if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up ||
+ event->keyval == GDK_Down || event->keyval == GDK_KP_Down) {
+ int dir, i, j = -1;
+ for (i = 0; i < dp->ntreeitems; i++)
+ if (widget == dp->treeitems[i])
+ break;
+ if (i < dp->ntreeitems) {
+ if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up)
+ dir = -1;
+ else
+ dir = +1;
+
+ while (1) {
+ i += dir;
+ if (i < 0 || i >= dp->ntreeitems)
+ break; /* nothing in that dir to select */
+ /*
+ * Determine if this tree item is visible.
+ */
+ {
+ GtkWidget *w = dp->treeitems[i];
+ int vis = TRUE;
+ while (w && (GTK_IS_TREE_ITEM(w) || GTK_IS_TREE(w))) {
+ if (!GTK_WIDGET_VISIBLE(w)) {
+ vis = FALSE;
+ break;
+ }
+ w = w->parent;
+ }
+ if (vis) {
+ j = i; /* got one */
+ break;
+ }
+ }
+ }
+ }
+ gtk_signal_emit_stop_by_name(GTK_OBJECT(widget),
+ "key_press_event");
+ if (j >= 0) {
+ gtk_signal_emit_by_name(GTK_OBJECT(dp->treeitems[j]), "toggle");
+ gtk_widget_grab_focus(dp->treeitems[j]);
+ }
+ return TRUE;
+ }
+
+ /*
+ * It's nice for Left and Right to expand and collapse tree
+ * branches.
+ */
+ if (event->keyval == GDK_Left || event->keyval == GDK_KP_Left) {
+ gtk_signal_emit_stop_by_name(GTK_OBJECT(widget),
+ "key_press_event");
+ gtk_tree_item_collapse(GTK_TREE_ITEM(widget));
+ return TRUE;
+ }
+ if (event->keyval == GDK_Right || event->keyval == GDK_KP_Right) {
+ gtk_signal_emit_stop_by_name(GTK_OBJECT(widget),
+ "key_press_event");
+ gtk_tree_item_expand(GTK_TREE_ITEM(widget));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void shortcut_add(struct Shortcuts *scs, GtkWidget *labelw,
+ int chr, int action, void *ptr)
+{
+ GtkLabel *label = GTK_LABEL(labelw);
+ gchar *currstr, *pattern;
+ int i;
+
+ if (chr == NO_SHORTCUT)
+ return;
+
+ chr = tolower((unsigned char)chr);
+
+ assert(scs->sc[chr].action == SHORTCUT_EMPTY);
+
+ scs->sc[chr].action = action;
+
+ if (action == SHORTCUT_FOCUS) {
+ scs->sc[chr].uc = NULL;
+ scs->sc[chr].widget = (GtkWidget *)ptr;
+ } else {
+ scs->sc[chr].widget = NULL;
+ scs->sc[chr].uc = (struct uctrl *)ptr;
+ }
+
+ gtk_label_get(label, &currstr);
+ for (i = 0; currstr[i]; i++)
+ if (tolower((unsigned char)currstr[i]) == chr) {
+ GtkRequisition req;
+
+ pattern = dupprintf("%*s_", i, "");
+
+ gtk_widget_size_request(GTK_WIDGET(label), &req);
+ gtk_label_set_pattern(label, pattern);
+ gtk_widget_set_usize(GTK_WIDGET(label), -1, req.height);
+
+ sfree(pattern);
+ break;
+ }
+}
+
+int get_listitemheight(void)
+{
+ GtkWidget *listitem = gtk_list_item_new_with_label("foo");
+ GtkRequisition req;
+ gtk_widget_size_request(listitem, &req);
+ gtk_widget_unref(listitem);
+ return req.height;
+}
+
+int do_config_box(const char *title, Config *cfg, int midsession)