Support custom clipboard names under X.

This required me to turn the drop-lists into combo boxes and add an
extra string-typed Conf setting alongside each enumerated value.
This commit is contained in:
Simon Tatham 2017-12-17 18:44:27 +00:00
Родитель 018aa57645
Коммит 2a76f8d4a2
6 изменённых файлов: 242 добавлений и 58 удалений

104
config.c
Просмотреть файл

@ -1342,29 +1342,100 @@ static void clipboard_selector_handler(union control *ctrl, void *dlg,
{
Conf *conf = (Conf *)data;
int setting = ctrl->generic.context.i;
#ifdef NAMED_CLIPBOARDS
int strsetting = ctrl->editbox.context2.i;
#endif
static const struct {
const char *name;
int id;
} options[] = {
{"No action", CLIPUI_NONE},
{CLIPNAME_IMPLICIT, CLIPUI_IMPLICIT},
{CLIPNAME_EXPLICIT, CLIPUI_EXPLICIT},
};
if (event == EVENT_REFRESH) {
int i, val = conf_get_int(conf, setting);
dlg_update_start(ctrl, dlg);
dlg_listbox_clear(ctrl, dlg);
dlg_listbox_addwithid(ctrl, dlg, "No action", CLIPUI_NONE);
dlg_listbox_addwithid(ctrl, dlg, CLIPNAME_IMPLICIT, CLIPUI_IMPLICIT);
dlg_listbox_addwithid(ctrl, dlg, CLIPNAME_EXPLICIT, CLIPUI_EXPLICIT);
#ifdef NAMED_CLIPBOARDS
for (i = 0; i < lenof(options); i++)
dlg_listbox_add(ctrl, dlg, options[i].name);
if (val == CLIPUI_CUSTOM) {
const char *sval = conf_get_str(conf, strsetting);
for (i = 0; i < lenof(options); i++)
if (!strcmp(sval, options[i].name))
break; /* needs escaping */
if (i < lenof(options) || sval[0] == '=') {
char *escaped = dupcat("=", sval, (const char *)NULL);
dlg_editbox_set(ctrl, dlg, escaped);
sfree(escaped);
} else {
dlg_editbox_set(ctrl, dlg, sval);
}
} else {
dlg_editbox_set(ctrl, dlg, options[0].name); /* fallback */
for (i = 0; i < lenof(options); i++)
if (val == options[i].id)
dlg_editbox_set(ctrl, dlg, options[i].name);
}
#else
for (i = 0; i < lenof(options); i++)
dlg_listbox_addwithid(ctrl, dlg, options[i].name, options[i].id);
dlg_listbox_select(ctrl, dlg, 0); /* fallback */
for (i = 0; i < 3; i++)
if (val == dlg_listbox_getid(ctrl, dlg, i))
for (i = 0; i < lenof(options); i++)
if (val == options[i].id)
dlg_listbox_select(ctrl, dlg, i);
#endif
dlg_update_done(ctrl, dlg);
} else if (event == EVENT_SELCHANGE) {
} else if (event == EVENT_SELCHANGE
#ifdef NAMED_CLIPBOARDS
|| event == EVENT_VALCHANGE
#endif
) {
#ifdef NAMED_CLIPBOARDS
const char *sval = dlg_editbox_get(ctrl, dlg);
int i;
for (i = 0; i < lenof(options); i++)
if (!strcmp(sval, options[i].name)) {
conf_set_int(conf, setting, options[i].id);
conf_set_str(conf, strsetting, "");
break;
}
if (i == lenof(options)) {
conf_set_int(conf, setting, CLIPUI_CUSTOM);
if (sval[0] == '=')
sval++;
conf_set_str(conf, strsetting, sval);
}
#else
int index = dlg_listbox_index(ctrl, dlg);
if (index >= 0) {
int val = dlg_listbox_getid(ctrl, dlg, index);
conf_set_int(conf, setting, val);
}
#endif
}
}
static void clipboard_control(struct controlset *s, const char *label,
char shortcut, int percentage, intorptr helpctx,
int setting, int strsetting)
{
#ifdef NAMED_CLIPBOARDS
ctrl_combobox(s, label, shortcut, percentage, helpctx,
clipboard_selector_handler, I(setting), I(strsetting));
#else
/* strsetting isn't needed in this case */
ctrl_droplist(s, label, shortcut, percentage, helpctx,
clipboard_selector_handler, I(setting));
#endif
}
void setup_config_box(struct controlbox *b, int midsession,
int protocol, int protcfginfo)
{
@ -1894,18 +1965,15 @@ void setup_config_box(struct controlbox *b, int midsession,
CLIPNAME_EXPLICIT_OBJECT,
NO_SHORTCUT, HELPCTX(selection_autocopy),
conf_checkbox_handler, I(CONF_mouseautocopy));
ctrl_droplist(s, "Mouse paste action:", NO_SHORTCUT, 60,
HELPCTX(selection_clipactions),
clipboard_selector_handler,
I(CONF_mousepaste));
ctrl_droplist(s, "{Ctrl,Shift} + Ins:", NO_SHORTCUT, 60,
HELPCTX(selection_clipactions),
clipboard_selector_handler,
I(CONF_ctrlshiftins));
ctrl_droplist(s, "Ctrl + Shift + {C,V}:", NO_SHORTCUT, 60,
HELPCTX(selection_clipactions),
clipboard_selector_handler,
I(CONF_ctrlshiftcv));
clipboard_control(s, "Mouse paste action:", NO_SHORTCUT, 60,
HELPCTX(selection_clipactions),
CONF_mousepaste, CONF_mousepaste_custom);
clipboard_control(s, "{Ctrl,Shift} + Ins:", NO_SHORTCUT, 60,
HELPCTX(selection_clipactions),
CONF_ctrlshiftins, CONF_ctrlshiftins_custom);
clipboard_control(s, "Ctrl + Shift + {C,V}:", NO_SHORTCUT, 60,
HELPCTX(selection_clipactions),
CONF_ctrlshiftcv, CONF_ctrlshiftcv_custom);
/*
* The Window/Selection/Words panel.

Просмотреть файл

@ -1520,7 +1520,7 @@ actions pastes from (including turning the paste action off
completely). On platforms with a single system clipboard, the
available options are to paste from that clipboard or to paste from
PuTTY's internal memory of the last selected text within that window.
On X, the options are \cw{CLIPBOARD} or \cw{PRIMARY}.
On X, the standard options are \cw{CLIPBOARD} or \cw{PRIMARY}.
(\cw{PRIMARY} is conceptually similar in that it \e{also} refers to
the last selected text \dash just across all applications instead of
@ -1531,6 +1531,16 @@ The two keyboard options each come with a corresponding key to copy
from, Ctrl-Ins will copy to the same location; similarly, Ctrl-Shift-C
will copy to whatever Ctrl-Shift-V pastes from.
On X, you can also enter a selection name of your choice. For example,
there is a rarely-used standard selection called \cq{SECONDARY}, which
Emacs (for example) can work with if you hold down the Meta key while
dragging to select or clicking to paste; if you configure a PuTTY
keyboard action to access this clipboard, then you can interoperate
with other applications' use of it. Another thing you could do would
be to invent a clipboard name yourself, to create a special clipboard
shared \e{only} between instances of PuTTY, or between just instances
configured in that particular way.
\H{config-selection-words} The Words panel
PuTTY will \I{word-by-word selection}select a word at a time in the

Просмотреть файл

@ -903,6 +903,9 @@ void cleanup_exit(int);
X(INT, NONE, mousepaste) \
X(INT, NONE, ctrlshiftins) \
X(INT, NONE, ctrlshiftcv) \
X(STR, NONE, mousepaste_custom) \
X(STR, NONE, ctrlshiftins_custom) \
X(STR, NONE, ctrlshiftcv_custom) \
/* translations */ \
X(INT, NONE, vtmode) \
X(STR, NONE, line_codepage) \

Просмотреть файл

@ -448,7 +448,7 @@ static void wprefs(void *sesskey, const char *name,
}
static void write_clip_setting(void *handle, const char *savekey,
Conf *conf, int confkey)
Conf *conf, int confkey, int strconfkey)
{
int val = conf_get_int(conf, confkey);
switch (val) {
@ -462,21 +462,33 @@ static void write_clip_setting(void *handle, const char *savekey,
case CLIPUI_EXPLICIT:
write_setting_s(handle, savekey, "explicit");
break;
case CLIPUI_CUSTOM:
{
char *sval = dupcat("custom:", conf_get_str(conf, strconfkey),
(const char *)NULL);
write_setting_s(handle, savekey, sval);
sfree(sval);
}
break;
}
}
static void read_clip_setting(void *handle, const char *savekey,
int def, Conf *conf, int confkey)
int def, Conf *conf, int confkey, int strconfkey)
{
char *setting = read_setting_s(handle, savekey);
int val;
conf_set_str(conf, strconfkey, "");
if (!setting) {
val = def;
} else if (!strcmp(setting, "implicit")) {
val = CLIPUI_IMPLICIT;
} else if (!strcmp(setting, "explicit")) {
val = CLIPUI_EXPLICIT;
} else if (!strncmp(setting, "custom:", 7)) {
val = CLIPUI_CUSTOM;
conf_set_str(conf, strconfkey, setting + 7);
} else {
val = CLIPUI_NONE;
}
@ -676,9 +688,12 @@ void save_open_settings(void *sesskey, Conf *conf)
}
write_setting_i(sesskey, "MouseAutocopy",
conf_get_int(conf, CONF_mouseautocopy));
write_clip_setting(sesskey, "MousePaste", conf, CONF_mousepaste);
write_clip_setting(sesskey, "CtrlShiftIns", conf, CONF_ctrlshiftins);
write_clip_setting(sesskey, "CtrlShiftCV", conf, CONF_ctrlshiftcv);
write_clip_setting(sesskey, "MousePaste", conf,
CONF_mousepaste, CONF_mousepaste_custom);
write_clip_setting(sesskey, "CtrlShiftIns", conf,
CONF_ctrlshiftins, CONF_ctrlshiftins_custom);
write_clip_setting(sesskey, "CtrlShiftCV", conf,
CONF_ctrlshiftcv, CONF_ctrlshiftcv_custom);
write_setting_s(sesskey, "LineCodePage", conf_get_str(conf, CONF_line_codepage));
write_setting_i(sesskey, "CJKAmbigWide", conf_get_int(conf, CONF_cjk_ambig_wide));
write_setting_i(sesskey, "UTF8Override", conf_get_int(conf, CONF_utf8_override));
@ -1103,11 +1118,11 @@ void load_open_settings(void *sesskey, Conf *conf)
gppi(sesskey, "MouseAutocopy", CLIPUI_DEFAULT_AUTOCOPY,
conf, CONF_mouseautocopy);
read_clip_setting(sesskey, "MousePaste", CLIPUI_DEFAULT_MOUSE,
conf, CONF_mousepaste);
conf, CONF_mousepaste, CONF_mousepaste_custom);
read_clip_setting(sesskey, "CtrlShiftIns", CLIPUI_DEFAULT_INS,
conf, CONF_ctrlshiftins);
conf, CONF_ctrlshiftins, CONF_ctrlshiftins_custom);
read_clip_setting(sesskey, "CtrlShiftCV", CLIPUI_NONE,
conf, CONF_ctrlshiftcv);
conf, CONF_ctrlshiftcv, CONF_ctrlshiftcv_custom);
/*
* The empty default for LineCodePage will be converted later
* into a plausible default for the locale.

Просмотреть файл

@ -115,6 +115,7 @@ struct gui_data {
#endif
int direct_to_font;
struct clipboard_state clipstates[N_CLIPBOARDS];
int clipboard_ctrlshiftins, clipboard_ctrlshiftcv;
int font_width, font_height;
int width, height;
int ignore_sbar;
@ -1041,6 +1042,12 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
#endif
term_request_paste(inst->term, CLIP_CLIPBOARD);
return TRUE;
case CLIPUI_CUSTOM:
#ifdef KEY_EVENT_DIAGNOSTICS
debug((" - Shift-Insert: paste from custom clipboard\n"));
#endif
term_request_paste(inst->term, inst->clipboard_ctrlshiftins);
return TRUE;
default:
#ifdef KEY_EVENT_DIAGNOSTICS
debug((" - Shift-Insert: no paste action\n"));
@ -1067,6 +1074,13 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
term_request_copy(inst->term,
clips_clipboard, lenof(clips_clipboard));
return TRUE;
case CLIPUI_CUSTOM:
#ifdef KEY_EVENT_DIAGNOSTICS
debug((" - Ctrl-Insert: copy to custom clipboard\n"));
#endif
term_request_copy(inst->term,
&inst->clipboard_ctrlshiftins, 1);
return TRUE;
default:
#ifdef KEY_EVENT_DIAGNOSTICS
debug((" - Ctrl-Insert: no copy action\n"));
@ -1113,6 +1127,21 @@ gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data)
term_request_copy(inst->term, clips, lenof(clips));
}
return TRUE;
case CLIPUI_CUSTOM:
if (paste) {
#ifdef KEY_EVENT_DIAGNOSTICS
debug((" - Ctrl-Shift-V: paste from custom clipboard\n"));
#endif
term_request_paste(inst->term,
inst->clipboard_ctrlshiftcv);
} else {
#ifdef KEY_EVENT_DIAGNOSTICS
debug((" - Ctrl-Shift-C: copy to custom clipboard\n"));
#endif
term_request_copy(inst->term,
&inst->clipboard_ctrlshiftcv, 1);
}
return TRUE;
}
}
@ -2571,6 +2600,20 @@ void palette_reset(void *frontend)
}
}
static struct clipboard_state *clipboard_from_atom(
struct gui_data *inst, GdkAtom atom)
{
int i;
for (i = 0; i < N_CLIPBOARDS; i++) {
struct clipboard_state *state = &inst->clipstates[i];
if (state->inst == inst && state->atom == atom)
return state;
}
return NULL;
}
#ifdef JUST_USE_GTK_CLIPBOARD_UTF8
/* ----------------------------------------------------------------------
@ -2580,25 +2623,27 @@ void palette_reset(void *frontend)
* formats it feels like.
*/
static void init_one_clipboard(struct gui_data *inst, int clipboard,
GdkAtom atom)
void set_clipboard_atom(struct gui_data *inst, int clipboard, GdkAtom atom)
{
struct clipboard_state *state = &inst->clipstates[clipboard];
state->inst = inst;
state->atom = atom;
state->clipboard = clipboard;
state->atom = atom;
state->gtkclipboard = gtk_clipboard_get_for_display(
gdk_display_get_default(), atom);
g_object_set_data(G_OBJECT(state->gtkclipboard), "user-data", state);
if (state->atom != GDK_NONE) {
state->gtkclipboard = gtk_clipboard_get_for_display(
gdk_display_get_default(), state->atom);
g_object_set_data(G_OBJECT(state->gtkclipboard), "user-data", state);
} else {
state->gtkclipboard = NULL;
}
}
int init_clipboard(struct gui_data *inst)
{
init_one_clipboard(inst, CLIP_PRIMARY, GDK_SELECTION_PRIMARY);
init_one_clipboard(inst, CLIP_CLIPBOARD, GDK_SELECTION_CLIPBOARD);
set_clipboard_atom(inst, CLIP_PRIMARY, GDK_SELECTION_PRIMARY);
set_clipboard_atom(inst, CLIP_CLIPBOARD, GDK_SELECTION_CLIPBOARD);
return TRUE;
}
@ -2663,6 +2708,9 @@ void write_clip(void *frontend, int clipboard,
return;
}
if (!state->gtkclipboard)
return;
cdi = snew(struct clipboard_data_instance);
state->current_cdi = cdi;
cdi->pasteout_data_utf8 = snewn(len*6, char);
@ -2721,6 +2769,10 @@ void frontend_request_paste(void *frontend, int clipboard)
{
struct gui_data *inst = (struct gui_data *)frontend;
struct clipboard_state *state = &inst->clipstates[clipboard];
if (!state->gtkclipboard)
return;
gtk_clipboard_request_text(state->gtkclipboard,
clipboard_text_received, inst);
}
@ -2881,20 +2933,6 @@ void write_clip(void *frontend, int clipboard,
term_lost_clipboard_ownership(inst->term, clipboard);
}
static struct clipboard_state *clipboard_from_atom(
struct gui_data *inst, GdkAtom atom)
{
int i;
for (i = 0; i < N_CLIPBOARDS; i++) {
struct clipboard_state *state = &inst->clipstates[i];
if (state->inst == inst && state->atom == atom)
return state;
}
return NULL;
}
static void selection_get(GtkWidget *widget, GtkSelectionData *seldata,
guint info, guint time_stamp, gpointer data)
{
@ -3103,16 +3141,24 @@ static void selection_received(GtkWidget *widget, GtkSelectionData *seldata,
#endif
}
static void init_one_clipboard(struct gui_data *inst, int clipboard,
GdkAtom atom)
static void init_one_clipboard(struct gui_data *inst, int clipboard)
{
struct clipboard_state *state = &inst->clipstates[clipboard];
state->inst = inst;
state->atom = atom;
state->clipboard = clipboard;
}
void set_clipboard_atom(struct gui_data *inst, int clipboard, GdkAtom atom)
{
struct clipboard_state *state = &inst->clipstates[clipboard];
state->inst = inst;
state->clipboard = clipboard;
state->atom = atom;
}
void init_clipboard(struct gui_data *inst)
{
#ifndef NOT_X_WINDOWS
@ -3148,8 +3194,10 @@ void init_clipboard(struct gui_data *inst)
XA_CUT_BUFFER7, XA_STRING, 8, PropModeAppend, empty, 0);
#endif
init_one_clipboard(inst, CLIP_PRIMARY, GDK_SELECTION_PRIMARY);
init_one_clipboard(inst, CLIP_CLIPBOARD, GDK_SELECTION_CLIPBOARD);
inst->clipstates[CLIP_PRIMARY].atom = GDK_SELECTION_PRIMARY;
inst->clipstates[CLIP_CLIPBOARD].atom = GDK_SELECTION_CLIPBOARD;
init_one_clipboard(inst, CLIP_PRIMARY);
init_one_clipboard(inst, CLIP_CLIPBOARD);
g_signal_connect(G_OBJECT(inst->area), "selection_received",
G_CALLBACK(selection_received), inst);
@ -4252,7 +4300,7 @@ void event_log_menuitem(GtkMenuItem *item, gpointer data)
showeventlog(inst->eventlogstuff, inst->window);
}
void setup_clipboards(Terminal *term, Conf *conf)
void setup_clipboards(struct gui_data *inst, Terminal *term, Conf *conf)
{
assert(term->mouse_select_clipboards[0] == CLIP_LOCAL);
@ -4265,6 +4313,10 @@ void setup_clipboards(Terminal *term, Conf *conf)
term->n_mouse_select_clipboards++] = CLIP_CLIPBOARD;
}
set_clipboard_atom(inst, CLIP_CUSTOM_1, GDK_NONE);
set_clipboard_atom(inst, CLIP_CUSTOM_2, GDK_NONE);
set_clipboard_atom(inst, CLIP_CUSTOM_3, GDK_NONE);
switch (conf_get_int(conf, CONF_mousepaste)) {
case CLIPUI_IMPLICIT:
term->mouse_paste_clipboard = MOUSE_PASTE_CLIPBOARD;
@ -4272,10 +4324,41 @@ void setup_clipboards(Terminal *term, Conf *conf)
case CLIPUI_EXPLICIT:
term->mouse_paste_clipboard = CLIP_CLIPBOARD;
break;
case CLIPUI_CUSTOM:
term->mouse_paste_clipboard = CLIP_CUSTOM_1;
set_clipboard_atom(inst, CLIP_CUSTOM_1,
gdk_atom_intern(
conf_get_str(conf, CONF_mousepaste_custom),
FALSE));
break;
default:
term->mouse_paste_clipboard = CLIP_NULL;
break;
}
if (conf_get_int(conf, CONF_ctrlshiftins) == CLIPUI_CUSTOM) {
GdkAtom atom = gdk_atom_intern(
conf_get_str(conf, CONF_ctrlshiftins_custom), FALSE);
struct clipboard_state *state = clipboard_from_atom(inst, atom);
if (state) {
inst->clipboard_ctrlshiftins = state->clipboard;
} else {
inst->clipboard_ctrlshiftins = CLIP_CUSTOM_2;
set_clipboard_atom(inst, CLIP_CUSTOM_2, atom);
}
}
if (conf_get_int(conf, CONF_ctrlshiftcv) == CLIPUI_CUSTOM) {
GdkAtom atom = gdk_atom_intern(
conf_get_str(conf, CONF_ctrlshiftcv_custom), FALSE);
struct clipboard_state *state = clipboard_from_atom(inst, atom);
if (state) {
inst->clipboard_ctrlshiftins = state->clipboard;
} else {
inst->clipboard_ctrlshiftcv = CLIP_CUSTOM_3;
set_clipboard_atom(inst, CLIP_CUSTOM_3, atom);
}
}
}
struct after_change_settings_dialog_ctx {
@ -4352,7 +4435,7 @@ static void after_change_settings_dialog(void *vctx, int retval)
}
/* Pass new config data to the terminal */
term_reconfig(inst->term, inst->conf);
setup_clipboards(inst->term, inst->conf);
setup_clipboards(inst, inst->term, inst->conf);
/* Pass new config data to the back end */
if (inst->back)
inst->back->reconfig(inst->backhandle, inst->conf);
@ -5131,7 +5214,7 @@ void new_session_window(Conf *conf, const char *geometry_string)
inst->eventlogstuff = eventlogstuff_new();
inst->term = term_init(inst->conf, &inst->ucsdata, inst);
setup_clipboards(inst->term, inst->conf);
setup_clipboards(inst, inst->term, inst->conf);
inst->logctx = log_init(inst, inst->conf);
term_provide_logctx(inst->term, inst->logctx);

Просмотреть файл

@ -121,6 +121,9 @@ unsigned long getticks(void);
#define PLATFORM_CLIPBOARDS(X) \
X(CLIP_PRIMARY, "X11 primary selection") \
X(CLIP_CLIPBOARD, "XDG clipboard") \
X(CLIP_CUSTOM_1, "<custom#1>") \
X(CLIP_CUSTOM_2, "<custom#2>") \
X(CLIP_CUSTOM_3, "<custom#3>") \
/* end of list */
#ifdef OSX_GTK
@ -149,6 +152,8 @@ unsigned long getticks(void);
#define CLIPUI_DEFAULT_AUTOCOPY FALSE
#define CLIPUI_DEFAULT_MOUSE CLIPUI_IMPLICIT
#define CLIPUI_DEFAULT_INS CLIPUI_IMPLICIT
/* X11 supports arbitrary named clipboards */
#define NAMED_CLIPBOARDS
#endif
/* The per-session frontend structure managed by gtkwin.c */