📄 gtkdlg.c
字号:
/*
* gtkdlg.c - GTK implementation of the PuTTY configuration box.
*/
/*
* TODO when porting to GTK 2.0:
*
* - GtkTree is apparently deprecated and we should switch to
* GtkTreeView instead.
* - GtkLabel has a built-in mnemonic scheme, so we should at
* least consider switching to that from the current adhockery.
*/
#include <assert.h>
#include <stdarg.h>
#include <ctype.h>
#include <time.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <gdk/gdkx.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "gtkcols.h"
#include "gtkpanel.h"
#ifdef TESTMODE
#define PUTTY_DO_GLOBALS /* actually _define_ globals */
#endif
#include "putty.h"
#include "storage.h"
#include "dialog.h"
#include "tree234.h"
struct Shortcut {
GtkWidget *widget;
struct uctrl *uc;
int action;
};
struct Shortcuts {
struct Shortcut sc[128];
};
struct uctrl {
union control *ctrl;
GtkWidget *toplevel;
void *privdata;
int privdata_needs_free;
GtkWidget **buttons; int nbuttons; /* for radio buttons */
GtkWidget *entry; /* for editbox, combobox, filesel, fontsel */
GtkWidget *button; /* for filesel, fontsel */
GtkWidget *list; /* for combobox, listbox */
GtkWidget *menu; /* for optionmenu (==droplist) */
GtkWidget *optmenu; /* also for optionmenu */
GtkWidget *text; /* for text */
GtkAdjustment *adj; /* for the scrollbar in a list box */
guint textsig;
};
struct dlgparam {
tree234 *byctrl, *bywidget;
void *data;
struct { unsigned char r, g, b, ok; } coloursel_result; /* 0-255 */
/* `flags' are set to indicate when a GTK signal handler is being called
* due to automatic processing and should not flag a user event. */
int flags;
struct Shortcuts *shortcuts;
GtkWidget *window, *cancelbutton, *currtreeitem, **treeitems;
union control *currfocus, *lastfocus;
int ntreeitems;
int retval;
};
#define FLAG_UPDATING_COMBO_LIST 1
enum { /* values for Shortcut.action */
SHORTCUT_EMPTY, /* no shortcut on this key */
SHORTCUT_TREE, /* focus a tree item */
SHORTCUT_FOCUS, /* focus the supplied widget */
SHORTCUT_UCTRL, /* do something sane with uctrl */
SHORTCUT_UCTRL_UP, /* uctrl is a draglist, move Up */
SHORTCUT_UCTRL_DOWN, /* uctrl is a draglist, move Down */
};
/*
* Forward references.
*/
static gboolean widget_focus(GtkWidget *widget, GdkEventFocus *event,
gpointer data);
static void shortcut_add(struct Shortcuts *scs, GtkWidget *labelw,
int chr, int action, void *ptr);
static int listitem_single_key(GtkWidget *item, GdkEventKey *event,
gpointer data);
static int listitem_multi_key(GtkWidget *item, GdkEventKey *event,
gpointer data);
static int listitem_button(GtkWidget *item, GdkEventButton *event,
gpointer data);
static void menuitem_activate(GtkMenuItem *item, gpointer data);
static void coloursel_ok(GtkButton *button, gpointer data);
static void coloursel_cancel(GtkButton *button, gpointer data);
static void window_destroy(GtkWidget *widget, gpointer data);
static int uctrl_cmp_byctrl(void *av, void *bv)
{
struct uctrl *a = (struct uctrl *)av;
struct uctrl *b = (struct uctrl *)bv;
if (a->ctrl < b->ctrl)
return -1;
else if (a->ctrl > b->ctrl)
return +1;
return 0;
}
static int uctrl_cmp_byctrl_find(void *av, void *bv)
{
union control *a = (union control *)av;
struct uctrl *b = (struct uctrl *)bv;
if (a < b->ctrl)
return -1;
else if (a > b->ctrl)
return +1;
return 0;
}
static int uctrl_cmp_bywidget(void *av, void *bv)
{
struct uctrl *a = (struct uctrl *)av;
struct uctrl *b = (struct uctrl *)bv;
if (a->toplevel < b->toplevel)
return -1;
else if (a->toplevel > b->toplevel)
return +1;
return 0;
}
static int uctrl_cmp_bywidget_find(void *av, void *bv)
{
GtkWidget *a = (GtkWidget *)av;
struct uctrl *b = (struct uctrl *)bv;
if (a < b->toplevel)
return -1;
else if (a > b->toplevel)
return +1;
return 0;
}
static void dlg_init(struct dlgparam *dp)
{
dp->byctrl = newtree234(uctrl_cmp_byctrl);
dp->bywidget = newtree234(uctrl_cmp_bywidget);
dp->coloursel_result.ok = FALSE;
dp->treeitems = NULL;
dp->window = dp->cancelbutton = dp->currtreeitem = NULL;
dp->flags = 0;
dp->currfocus = NULL;
}
static void dlg_cleanup(struct dlgparam *dp)
{
struct uctrl *uc;
freetree234(dp->byctrl); /* doesn't free the uctrls inside */
dp->byctrl = NULL;
while ( (uc = index234(dp->bywidget, 0)) != NULL) {
del234(dp->bywidget, uc);
if (uc->privdata_needs_free)
sfree(uc->privdata);
sfree(uc->buttons);
sfree(uc);
}
freetree234(dp->bywidget);
dp->bywidget = NULL;
sfree(dp->treeitems);
}
static void dlg_add_uctrl(struct dlgparam *dp, struct uctrl *uc)
{
add234(dp->byctrl, uc);
add234(dp->bywidget, uc);
}
static struct uctrl *dlg_find_byctrl(struct dlgparam *dp, union control *ctrl)
{
if (!dp->byctrl)
return NULL;
return find234(dp->byctrl, ctrl, uctrl_cmp_byctrl_find);
}
static struct uctrl *dlg_find_bywidget(struct dlgparam *dp, GtkWidget *w)
{
struct uctrl *ret = NULL;
if (!dp->bywidget)
return NULL;
do {
ret = find234(dp->bywidget, w, uctrl_cmp_bywidget_find);
if (ret)
return ret;
w = w->parent;
} while (w);
return ret;
}
void *dlg_get_privdata(union control *ctrl, void *dlg)
{
struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
return uc->privdata;
}
void dlg_set_privdata(union control *ctrl, void *dlg, void *ptr)
{
struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
uc->privdata = ptr;
uc->privdata_needs_free = FALSE;
}
void *dlg_alloc_privdata(union control *ctrl, void *dlg, size_t size)
{
struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
/*
* This is an internal allocation routine, so it's allowed to
* use smalloc directly.
*/
uc->privdata = smalloc(size);
uc->privdata_needs_free = FALSE;
return uc->privdata;
}
union control *dlg_last_focused(union control *ctrl, void *dlg)
{
struct dlgparam *dp = (struct dlgparam *)dlg;
if (dp->currfocus != ctrl)
return dp->currfocus;
else
return dp->lastfocus;
}
void dlg_radiobutton_set(union control *ctrl, void *dlg, int which)
{
struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
assert(uc->ctrl->generic.type == CTRL_RADIO);
assert(uc->buttons != NULL);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(uc->buttons[which]), TRUE);
}
int dlg_radiobutton_get(union control *ctrl, void *dlg)
{
struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
int i;
assert(uc->ctrl->generic.type == CTRL_RADIO);
assert(uc->buttons != NULL);
for (i = 0; i < uc->nbuttons; i++)
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(uc->buttons[i])))
return i;
return 0; /* got to return something */
}
void dlg_checkbox_set(union control *ctrl, void *dlg, int checked)
{
struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
assert(uc->ctrl->generic.type == CTRL_CHECKBOX);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(uc->toplevel), checked);
}
int dlg_checkbox_get(union control *ctrl, void *dlg)
{
struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
assert(uc->ctrl->generic.type == CTRL_CHECKBOX);
return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(uc->toplevel));
}
void dlg_editbox_set(union control *ctrl, void *dlg, char const *text)
{
struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
assert(uc->ctrl->generic.type == CTRL_EDITBOX);
assert(uc->entry != NULL);
gtk_entry_set_text(GTK_ENTRY(uc->entry), text);
}
void dlg_editbox_get(union control *ctrl, void *dlg, char *buffer, int length)
{
struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
assert(uc->ctrl->generic.type == CTRL_EDITBOX);
assert(uc->entry != NULL);
strncpy(buffer, gtk_entry_get_text(GTK_ENTRY(uc->entry)),
length);
buffer[length-1] = '\0';
}
static void container_remove_and_destroy(GtkWidget *w, gpointer data)
{
GtkContainer *cont = GTK_CONTAINER(data);
/* gtk_container_remove will unref the widget for us; we need not. */
gtk_container_remove(cont, w);
}
/* The `listbox' functions can also apply to combo boxes. */
void dlg_listbox_clear(union control *ctrl, void *dlg)
{
struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
uc->ctrl->generic.type == CTRL_LISTBOX);
assert(uc->menu != NULL || uc->list != NULL);
if (uc->menu) {
gtk_container_foreach(GTK_CONTAINER(uc->menu),
container_remove_and_destroy,
GTK_CONTAINER(uc->menu));
} else {
gtk_list_clear_items(GTK_LIST(uc->list), 0, -1);
}
}
void dlg_listbox_del(union control *ctrl, void *dlg, int index)
{
struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
uc->ctrl->generic.type == CTRL_LISTBOX);
assert(uc->menu != NULL || uc->list != NULL);
if (uc->menu) {
gtk_container_remove
(GTK_CONTAINER(uc->menu),
g_list_nth_data(GTK_MENU_SHELL(uc->menu)->children, index));
} else {
gtk_list_clear_items(GTK_LIST(uc->list), index, index+1);
}
}
void dlg_listbox_add(union control *ctrl, void *dlg, char const *text)
{
dlg_listbox_addwithid(ctrl, dlg, text, 0);
}
/*
* 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)
{
struct dlgparam *dp = (struct dlgparam *)dlg;
struct uctrl *uc = dlg_find_byctrl(dp, ctrl);
assert(uc->ctrl->generic.type == CTRL_EDITBOX ||
uc->ctrl->generic.type == CTRL_LISTBOX);
assert(uc->menu != NULL || uc->list != NULL);
dp->flags |= FLAG_UPDATING_COMBO_LIST;
if (uc->menu) {
/*
* List item in a drop-down (but non-combo) list. Tabs are
* ignored; we just provide a standard menu item with the
* text.
*/
GtkWidget *menuitem = gtk_menu_item_new_with_label(text);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -