GnmFunc: make this a GObject.
[gnumeric.git] / src / dialogs / dialog-function-select.c
blobd03e141dbbc0ec80e96788fba5422bccf23f473b
1 /*
2 * dialog-function-select.c: Implements the function selector
4 * Authors:
5 * Michael Meeks <michael@ximian.com>
6 * Andreas J. Guelzow <aguelzow@pyrshep.ca>
8 * Copyright (C) 2003-2010 Andreas J. Guelzow <aguelzow@pyrshep.ca>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, see <https://www.gnu.org/licenses/>.
24 #include <gnumeric-config.h>
25 #include <glib/gi18n-lib.h>
26 #include <gnumeric.h>
27 #include <dialogs/dialogs.h>
28 #include <dialogs/help.h>
30 #include <gui-util.h>
31 #include <gutils.h>
32 #include <func.h>
33 #include <workbook.h>
34 #include <wbc-gtk.h>
35 #include <application.h>
36 #include <position.h>
37 #include <expr.h>
38 #include <value.h>
39 #include <sheet.h>
40 #include <gnumeric-conf.h>
41 #include <gnm-format.h>
42 #include <auto-format.h>
44 #include <gsf/gsf-impl-utils.h>
45 #include <string.h>
47 #define F2(func,s) dgettext ((func)->tdomain->str, (s))
49 #define FUNCTION_SELECT_KEY "function-selector-dialog"
50 #define FUNCTION_SELECT_HELP_KEY "function-selector-dialog-help-mode"
51 #define FUNCTION_SELECT_PASTE_KEY "function-selector-dialog-paste-mode"
52 #define FUNCTION_SELECT_DIALOG_KEY "function-selector-dialog"
54 #define UNICODE_ELLIPSIS "\xe2\x80\xa6"
56 typedef enum {
57 GURU_MODE = 0,
58 HELP_MODE,
59 PASTE_MODE
60 } DialogMode;
62 typedef struct {
63 WBCGtk *wbcg;
64 Workbook *wb;
65 Sheet *sheet;
67 gboolean localized_function_names;
69 GtkBuilder *gui;
70 GtkWidget *dialog;
71 GtkWidget *ok_button;
72 GtkWidget *paste_button;
73 GtkListStore *model;
74 GtkComboBox *cb;
75 GtkListStore *model_functions;
76 GtkTreeModel *model_filter;
77 GtkTreeView *treeview;
78 GtkTextView *description_view;
79 GtkWidget *search_entry;
81 GSList *recent_funcs;
83 struct {
84 gint from;
85 gint to;
86 char *prefix;
87 } paste;
89 DialogMode mode;
90 char const *formula_guru_key;
91 } FunctionSelectState;
93 enum {
94 CAT_NAME,
95 CATEGORY,
96 CAT_SEPARATOR,
97 NUM_CAT_COLUMNS
99 enum {
100 FUN_NAME,
101 FUNCTION,
102 FUNCTION_DESC,
103 FUNCTION_PAL,
104 FUNCTION_CAT,
105 FUNCTION_VISIBLE,
106 FUNCTION_RECENT,
107 FUNCTION_USED,
108 NUM_COLUMNS
111 /*************************************************************************/
112 /* Search Functions */
113 /*************************************************************************/
115 typedef struct {
116 char const *text;
117 gboolean recent_only;
118 gboolean used_only;
119 GnmFuncGroup const * cat;
120 } search_t;
122 static gboolean
123 cb_dialog_function_select_search_all (GtkTreeModel *model,
124 G_GNUC_UNUSED GtkTreePath *path,
125 GtkTreeIter *iter, gpointer data)
127 search_t *specs = data;
128 gchar *name;
129 gchar *desc;
130 gboolean visible, was_visible, recent, used;
131 GnmFuncGroup const * cat;
133 gtk_tree_model_get (model, iter,
134 FUN_NAME, &name,
135 FUNCTION_DESC, &desc,
136 FUNCTION_VISIBLE, &was_visible,
137 FUNCTION_RECENT, &recent,
138 FUNCTION_USED, &used,
139 FUNCTION_CAT, &cat,
140 -1);
142 if (specs->recent_only && !recent)
143 visible = FALSE;
144 else if (specs->used_only && !used)
145 visible = FALSE;
146 else if (specs->cat != NULL && specs->cat != cat)
147 visible = FALSE;
148 else if (specs->text == NULL)
149 visible = TRUE;
150 else {
151 gchar *name_n, *name_cf, *text_n, *text_cf;
153 text_n = g_utf8_normalize (specs->text, -1, G_NORMALIZE_ALL);
154 text_cf = g_utf8_casefold(text_n, -1);
156 name_n = g_utf8_normalize (name, -1, G_NORMALIZE_ALL);
157 name_cf = g_utf8_casefold(name_n, -1);
158 visible = (NULL != g_strstr_len (name_cf, -1, text_cf));
159 g_free (name_n);
160 g_free (name_cf);
162 if (!visible) {
163 name_n = g_utf8_normalize (desc, -1, G_NORMALIZE_ALL);
164 name_cf = g_utf8_casefold(name_n, -1);
165 visible = (NULL != g_strstr_len (name_cf, -1, text_cf));
166 g_free (name_n);
167 g_free (name_cf);
170 g_free (text_n);
171 g_free (text_cf);
174 g_free (name);
175 g_free (desc);
177 if (visible != was_visible)
178 gtk_list_store_set (GTK_LIST_STORE (model), iter,
179 FUNCTION_VISIBLE, visible,
180 -1);
181 return FALSE;
184 static void
185 dialog_function_select_search (GtkEntry *entry, gpointer data)
187 search_t specs = {NULL, FALSE, FALSE, NULL};
188 FunctionSelectState *state = data;
189 GtkTreeIter iter;
191 if (0 != gtk_entry_get_text_length (entry))
192 specs.text = gtk_entry_get_text (entry);
194 if (gtk_combo_box_get_active_iter (state->cb, &iter)) {
195 gtk_tree_model_get (GTK_TREE_MODEL (state->model), &iter,
196 CATEGORY, &specs.cat,
197 -1);
198 specs.recent_only
199 = (specs.cat != NULL &&
200 specs.cat == GINT_TO_POINTER(-1));
201 specs.used_only
202 = (specs.cat != NULL &&
203 specs.cat == GINT_TO_POINTER(-2));
204 if (specs.recent_only || specs.used_only)
205 specs.cat = NULL;
208 gtk_tree_model_foreach (GTK_TREE_MODEL (state->model_functions),
209 cb_dialog_function_select_search_all,
210 (gpointer) &specs);
213 static void
214 dialog_function_select_erase_search_entry (GtkEntry *entry,
215 G_GNUC_UNUSED GtkEntryIconPosition icon_pos,
216 G_GNUC_UNUSED GdkEvent *event,
217 gpointer data)
219 gtk_entry_set_text (entry, "");
220 dialog_function_select_search (entry, data);
223 static void
224 dialog_function_select_cat_changed (G_GNUC_UNUSED GtkComboBox *widget,
225 gpointer data)
227 FunctionSelectState *state = data;
229 dialog_function_select_search (GTK_ENTRY (state->search_entry),
230 data);
233 /*************************************************************************/
235 static gboolean
236 cb_dialog_function_load_recent_funcs(GtkTreeModel *model,
237 G_GNUC_UNUSED GtkTreePath *path,
238 GtkTreeIter *iter,
239 gpointer data)
241 gpointer this;
243 gtk_tree_model_get (model, iter,
244 FUNCTION, &this,
245 -1);
246 if (this == data) {
247 gtk_list_store_set (GTK_LIST_STORE (model), iter,
248 FUNCTION_RECENT, TRUE,
249 -1);
250 return TRUE;
252 return FALSE;
255 static void
256 dialog_function_load_recent_funcs (FunctionSelectState *state)
258 GSList const *recent_funcs;
260 for (recent_funcs = gnm_conf_get_functionselector_recentfunctions ();
261 recent_funcs;
262 recent_funcs = recent_funcs->next) {
263 char const *name = recent_funcs->data;
264 GnmFunc *fd;
266 if (name == NULL)
267 continue;
269 fd = gnm_func_lookup (name, NULL);
270 if (fd) {
271 state->recent_funcs = g_slist_prepend (state->recent_funcs, fd);
272 gtk_tree_model_foreach (GTK_TREE_MODEL (state->model_functions),
273 cb_dialog_function_load_recent_funcs,
274 fd);
279 static void
280 dialog_function_write_recent_func (FunctionSelectState *state, GnmFunc const *fd)
282 GSList *rec_funcs;
283 GSList *gconf_value_list = NULL;
284 guint ulimit = gnm_conf_get_functionselector_num_of_recent ();
286 state->recent_funcs = g_slist_remove (state->recent_funcs, (gpointer) fd);
287 state->recent_funcs = g_slist_prepend (state->recent_funcs, (gpointer) fd);
289 while (g_slist_length (state->recent_funcs) > ulimit)
290 state->recent_funcs = g_slist_remove (state->recent_funcs,
291 g_slist_last (state->recent_funcs)->data);
293 for (rec_funcs = state->recent_funcs; rec_funcs; rec_funcs = rec_funcs->next) {
294 gconf_value_list = g_slist_prepend
295 (gconf_value_list,
296 g_strdup (gnm_func_get_name (rec_funcs->data,
297 state->localized_function_names)));
299 gnm_conf_set_functionselector_recentfunctions (gconf_value_list);
300 g_slist_free_full (gconf_value_list, g_free);
303 static gboolean
304 cb_unref (GtkTreeModel *model, G_GNUC_UNUSED GtkTreePath *path,
305 GtkTreeIter *iter, G_GNUC_UNUSED gpointer data)
307 GnmFunc *f;
309 gtk_tree_model_get (model, iter,
310 FUNCTION, &f,
311 -1);
312 gnm_func_dec_usage (f);
313 return FALSE;
316 static void
317 cb_dialog_function_select_destroy (FunctionSelectState *state)
319 if (state->formula_guru_key &&
320 gnm_dialog_raise_if_exists (state->wbcg, state->formula_guru_key)) {
321 /* The formula guru is waiting for us.*/
322 state->formula_guru_key = NULL;
323 dialog_formula_guru (state->wbcg, NULL);
326 if (state->gui != NULL)
327 g_object_unref (state->gui);
328 g_slist_free (state->recent_funcs);
329 gtk_tree_model_foreach (GTK_TREE_MODEL (state->model_functions),
330 cb_unref,
331 NULL);
332 g_free (state->paste.prefix);
333 g_free (state);
337 * cb_dialog_function_select_cancel_clicked:
338 * @button:
339 * @state:
341 * Close (destroy) the dialog
343 static void
344 cb_dialog_function_select_cancel_clicked (G_GNUC_UNUSED GtkWidget *button,
345 FunctionSelectState *state)
347 gtk_widget_destroy (state->dialog);
348 return;
352 * cb_dialog_function_select_ok_clicked:
353 * @button:
354 * @state:
356 * Close (destroy) the dialog
358 static void
359 cb_dialog_function_select_ok_clicked (G_GNUC_UNUSED GtkWidget *button,
360 FunctionSelectState *state)
362 GtkTreeIter iter;
363 GtkTreeModel *model;
364 GnmFunc *func;
365 GtkTreeSelection *the_selection = gtk_tree_view_get_selection (state->treeview);
367 if (state->formula_guru_key != NULL &&
368 gtk_tree_selection_get_selected (the_selection, &model, &iter)) {
369 WBCGtk *wbcg = state->wbcg;
370 gtk_tree_model_get (model, &iter,
371 FUNCTION, &func,
372 -1);
373 dialog_function_write_recent_func (state, func);
374 state->formula_guru_key = NULL;
375 gtk_widget_destroy (state->dialog);
376 dialog_formula_guru (wbcg, func);
377 return;
380 gtk_widget_destroy (state->dialog);
381 return;
385 * cb_dialog_function_select_paste_clicked:
386 * @button:
387 * @state:
389 * Close (destroy) the dialog
391 static void
392 cb_dialog_function_select_paste_clicked (G_GNUC_UNUSED GtkWidget *button,
393 FunctionSelectState *state)
395 GtkTreeIter iter;
396 GtkTreeModel *model;
397 GnmFunc *func;
398 GtkTreeSelection *the_selection = gtk_tree_view_get_selection (state->treeview);
400 if (gtk_tree_selection_get_selected (the_selection, &model, &iter) &&
401 wbcg_edit_start (state->wbcg, FALSE, FALSE)) {
402 GtkEditable *entry
403 = GTK_EDITABLE (wbcg_get_entry (state->wbcg));
404 gint position;
405 gtk_tree_model_get (model, &iter,
406 FUNCTION, &func,
407 -1);
408 if (func != NULL) {
409 dialog_function_write_recent_func (state, func);
410 if (state->paste.from >= 0)
411 gtk_editable_select_region
412 (entry, state->paste.from,
413 state->paste.to);
414 gtk_editable_delete_selection (entry);
415 position = gtk_editable_get_position (entry);
416 gtk_editable_insert_text
417 (entry, func->name, -1, &position);
418 gtk_editable_set_position (entry, position);
422 gtk_widget_destroy (state->dialog);
423 return;
426 static void
427 cb_dialog_function_row_activated (G_GNUC_UNUSED GtkTreeView *tree_view,
428 G_GNUC_UNUSED GtkTreePath *path,
429 G_GNUC_UNUSED GtkTreeViewColumn *column,
430 FunctionSelectState *state)
432 switch (state->mode) {
433 case GURU_MODE:
434 cb_dialog_function_select_ok_clicked (NULL, state);
435 return;
436 case PASTE_MODE:
437 cb_dialog_function_select_paste_clicked (NULL, state);
438 return;
439 default:
440 return;
444 static gint
445 dialog_function_select_by_name (gconstpointer a_, gconstpointer b_,
446 gpointer user)
448 GnmFunc const * const a = (GnmFunc const * const)a_;
449 GnmFunc const * const b = (GnmFunc const * const)b_;
450 FunctionSelectState const *state = user;
451 gboolean localized = state->localized_function_names;
453 return g_utf8_collate (gnm_func_get_name (a, localized),
454 gnm_func_get_name (b, localized));
457 /*************************************************************************/
458 /* Functions related to the category selector */
459 /*************************************************************************/
461 typedef struct {
462 char const *name;
463 GtkTreeIter *iter;
464 } dialog_function_select_load_cb_t;
466 static gboolean
467 cb_dialog_function_select_load_cb (GtkTreeModel *model,
468 G_GNUC_UNUSED GtkTreePath *path,
469 GtkTreeIter *iter,
470 gpointer data)
472 dialog_function_select_load_cb_t *specs = data;
473 gchar *name;
474 gpointer ptr;
475 gboolean res;
477 gtk_tree_model_get (model, iter,
478 CAT_NAME, &name,
479 CATEGORY, &ptr,
480 -1);
482 if (ptr == NULL || ptr == GINT_TO_POINTER(-1)
483 || ptr == GINT_TO_POINTER(-2))
484 res = FALSE;
485 else if (go_utf8_collate_casefold (specs->name, name) < 0) {
486 specs->iter = gtk_tree_iter_copy (iter);
487 res = TRUE;
488 } else
489 res = FALSE;
491 g_free (name);
493 return res;
496 static void
497 dialog_function_select_load_cb (FunctionSelectState *state)
499 int i = 0;
500 GtkTreeIter p_iter;
501 GnmFuncGroup const * cat;
503 gtk_list_store_clear (state->model);
505 gtk_list_store_insert_before (state->model, &p_iter, NULL);
506 gtk_list_store_set (state->model, &p_iter,
507 CAT_NAME, _("All Functions"),
508 CATEGORY, NULL,
509 CAT_SEPARATOR, FALSE,
510 -1);
511 gtk_list_store_insert_before (state->model, &p_iter, NULL);
512 gtk_list_store_set (state->model, &p_iter,
513 CAT_NAME, _("Recently Used"),
514 CATEGORY, GINT_TO_POINTER(-1),
515 CAT_SEPARATOR, FALSE,
516 -1);
517 gtk_list_store_insert_before (state->model, &p_iter, NULL);
518 gtk_list_store_set (state->model, &p_iter,
519 CAT_NAME, _("In Use"),
520 CATEGORY, GINT_TO_POINTER(-2),
521 CAT_SEPARATOR, FALSE,
522 -1);
524 gtk_list_store_insert_before (state->model, &p_iter, NULL);
525 gtk_list_store_set (state->model, &p_iter,
526 CAT_NAME, "-",
527 CATEGORY, NULL,
528 CAT_SEPARATOR, TRUE,
529 -1);
531 while ((cat = gnm_func_group_get_nth (i++)) != NULL) {
532 dialog_function_select_load_cb_t specs;
533 specs.name = _(cat->display_name->str);
534 specs.iter = NULL;
536 gtk_tree_model_foreach (GTK_TREE_MODEL (state->model),
537 cb_dialog_function_select_load_cb,
538 &specs);
540 gtk_list_store_insert_before (state->model, &p_iter, specs.iter);
541 gtk_list_store_set (state->model, &p_iter,
542 CAT_NAME, specs.name,
543 CATEGORY, cat,
544 CAT_SEPARATOR, FALSE,
545 -1);
546 if (specs.iter != NULL)
547 gtk_tree_iter_free (specs.iter);
551 static gboolean
552 dialog_function_select_cat_row_separator (GtkTreeModel *model,
553 GtkTreeIter *iter,
554 G_GNUC_UNUSED gpointer data)
556 gboolean sep;
558 gtk_tree_model_get (model, iter,
559 CAT_SEPARATOR, &sep,
560 -1);
562 return sep;
565 /*************************************************************************/
566 /* Functions related to the description field */
567 /*************************************************************************/
569 static GtkTextTag *
570 make_link (GtkTextBuffer *description, GtkWidget *target, const char *name,
571 GCallback cb, gpointer user)
573 GtkTextTag *link =
574 gtk_text_tag_table_lookup
575 (gtk_text_buffer_get_tag_table (description), name);
577 if (!link) {
578 GdkRGBA link_color;
579 char *link_color_text;
581 gnm_get_link_color (target, &link_color);
582 link_color_text = gdk_rgba_to_string (&link_color);
584 link = gtk_text_buffer_create_tag
585 (description, name,
586 "underline", PANGO_UNDERLINE_SINGLE,
587 "foreground", link_color_text,
588 NULL);
590 g_free (link_color_text);
592 if (cb)
593 g_signal_connect (link, "event", cb, user);
596 return link;
599 static gboolean
600 cb_link_event (GtkTextTag *link, G_GNUC_UNUSED GObject *trigger,
601 GdkEvent *event, G_GNUC_UNUSED GtkTextIter *iter,
602 G_GNUC_UNUSED gpointer user)
604 switch (event->type) {
605 case GDK_BUTTON_PRESS:
606 case GDK_2BUTTON_PRESS:
607 case GDK_3BUTTON_PRESS: {
608 GdkEventButton *eb = (GdkEventButton *)event;
609 const char *uri = g_object_get_data (G_OBJECT (link), "uri");
610 GError *error = NULL;
612 if (eb->button != 1)
613 break;
614 if (event->type != GDK_BUTTON_PRESS)
615 return TRUE;
617 error = go_gtk_url_show (uri, gdk_event_get_screen (event));
618 if (error) {
619 g_printerr ("Failed to show %s\n(%s)\n",
620 uri,
621 error->message);
622 g_error_free (error);
625 return TRUE;
628 #if 0
629 case GDK_ENTER_NOTIFY:
630 case GDK_LEAVE_NOTIFY:
631 /* We aren't getting these. */
632 #endif
633 default:
634 break;
637 return FALSE;
640 static char *
641 make_expr_example (Sheet *sheet, const char *text,
642 gboolean localized, gboolean consider_format)
644 GnmLocale *oldlocale = NULL;
645 GnmExprTop const *texpr;
646 char *res;
647 GnmParsePos pp;
648 GnmEvalPos ep;
649 GnmConventions const *convs = gnm_conventions_default;
650 char *tmp_text = NULL;
651 GOFormat const *fmt = NULL;
653 if (consider_format &&
654 g_ascii_strncasecmp (text, "TEXT(", 5) == 0 &&
655 text[strlen(text) - 1] == ')') {
656 char *p;
657 tmp_text = g_strdup (text + 5);
658 p = tmp_text + strlen (tmp_text) - 1;
659 while (p >= tmp_text && p[0] != '"') p--;
660 p[0] = 0;
661 while (p >= tmp_text && p[0] != '"') p--;
662 fmt = go_format_new_from_XL (p + 1);
663 while (p >= tmp_text && p[0] != ',') p--;
664 *p = 0;
667 eval_pos_init_sheet (&ep, sheet);
668 parse_pos_init_evalpos (&pp, &ep);
670 if (!localized)
671 oldlocale = gnm_push_C_locale ();
672 texpr = gnm_expr_parse_str (text, &pp,
673 GNM_EXPR_PARSE_DEFAULT,
674 convs,
675 NULL);
676 if (!localized)
677 gnm_pop_C_locale (oldlocale);
679 if (texpr) {
680 char *etxt = gnm_expr_top_as_string (texpr, &pp, convs);
681 GnmValue *val = gnm_expr_top_eval
682 (texpr, &ep, GNM_EXPR_EVAL_PERMIT_NON_SCALAR);
683 char *vtxt;
685 if (!fmt)
686 fmt = gnm_auto_style_format_suggest (texpr, &ep);
687 vtxt = format_value (fmt, val, -1, sheet_date_conv (sheet));
689 gnm_expr_top_unref (texpr);
690 value_release (val);
692 res = g_strdup_printf (_("%s evaluates to %s."), etxt, vtxt);
694 g_free (etxt);
695 g_free (vtxt);
696 } else {
697 g_warning ("Failed to parse [%s]", text);
698 res = g_strdup ("");
701 g_free (tmp_text);
702 go_format_unref (fmt);
704 return res;
709 #define ADD_LTEXT(text,len) gtk_text_buffer_insert (description, &ti, (text), (len))
710 #define ADD_TEXT(text) ADD_LTEXT((text),-1)
711 #define ADD_BOLD_TEXT(text,len) gtk_text_buffer_insert_with_tags (description, &ti, (text), (len), bold, NULL)
712 #define ADD_LINK_TEXT(text,len) gtk_text_buffer_insert_with_tags (description, &ti, (text), (len), link, NULL)
713 #define ADD_TEXT_WITH_ARGS(text) { const char *t = text; while (*t) { const char *at = strstr (t, "@{"); \
714 if (at == NULL) { ADD_TEXT(t); break;} ADD_LTEXT(t, at - t); t = at + 2; at = strchr (t,'}'); \
715 if (at != NULL) { ADD_BOLD_TEXT(t, at - t); t = at + 1; } else {ADD_TEXT (t); break;}}}
716 #define FINISH_ARGS if (seen_args && !args_finished) {\
717 gint min, max; \
718 gnm_func_count_args (func, &min, &max);\
719 if (max == G_MAXINT) { \
720 ADD_BOLD_TEXT(UNICODE_ELLIPSIS, strlen(UNICODE_ELLIPSIS)); \
721 ADD_LTEXT("\n",1); \
722 args_finished = TRUE; \
726 static void
727 describe_new_style (GtkTextBuffer *description,
728 GtkWidget *target,
729 GnmFunc const *func, Sheet *sheet)
731 GnmFuncHelp const *help;
732 GtkTextIter ti;
733 GtkTextTag *bold =
734 gtk_text_buffer_create_tag
735 (description, NULL,
736 "weight", PANGO_WEIGHT_BOLD,
737 NULL);
738 gboolean seen_args = FALSE;
739 gboolean args_finished = FALSE;
740 gboolean seen_examples = FALSE;
741 gboolean seen_extref = FALSE;
742 gboolean is_TEXT =
743 g_ascii_strcasecmp (gnm_func_get_name (func, FALSE), "TEXT") == 0;
745 gtk_text_buffer_get_end_iter (description, &ti);
747 for (help = func->help; 1; help++) {
748 switch (help->type) {
749 case GNM_FUNC_HELP_NAME: {
750 const char *text = F2 (func, help->text);
751 const char *colon = strchr (text, ':');
752 if (!colon)
753 break;
754 ADD_BOLD_TEXT (text, colon - text);
755 ADD_TEXT (": ");
756 ADD_TEXT_WITH_ARGS (colon + 1);
757 ADD_TEXT ("\n\n");
758 break;
760 case GNM_FUNC_HELP_ARG: {
761 const char *text = F2 (func, help->text);
762 const char *colon = strchr (text, ':');
763 if (!colon)
764 break;
766 if (!seen_args) {
767 seen_args = TRUE;
768 ADD_TEXT (_("Arguments:"));
769 ADD_TEXT ("\n");
772 ADD_BOLD_TEXT (text, colon - text);
773 ADD_TEXT (": ");
774 ADD_TEXT_WITH_ARGS (colon + 1);
775 ADD_TEXT ("\n");
776 break;
778 case GNM_FUNC_HELP_DESCRIPTION: {
779 const char *text = F2 (func, help->text);
780 FINISH_ARGS;
781 ADD_TEXT ("\n");
782 ADD_TEXT_WITH_ARGS (text);
783 ADD_TEXT ("\n");
784 break;
786 case GNM_FUNC_HELP_NOTE: {
787 const char *text = F2 (func, help->text);
788 FINISH_ARGS;
789 ADD_TEXT ("\n");
790 ADD_TEXT (_("Note: "));
791 ADD_TEXT_WITH_ARGS (text);
792 ADD_TEXT ("\n");
793 break;
795 case GNM_FUNC_HELP_EXAMPLES: {
796 const char *text = F2 (func, help->text);
797 gboolean was_translated = (text != help->text);
799 FINISH_ARGS;
800 if (!seen_examples) {
801 seen_examples = TRUE;
802 ADD_TEXT ("\n");
803 ADD_TEXT (_("Examples:"));
804 ADD_TEXT ("\n");
807 if (text[0] == '=') {
808 char *example =
809 make_expr_example (sheet, text + 1,
810 was_translated,
811 !is_TEXT);
812 ADD_TEXT (example);
813 g_free (example);
814 } else {
815 ADD_TEXT_WITH_ARGS (text);
817 ADD_TEXT ("\n");
818 break;
820 case GNM_FUNC_HELP_SEEALSO: {
821 const char *text = help->text; /* Not translated */
822 const char *pre = _("See also: ");
823 GtkTextTag *link =
824 make_link (description, target, "LINK",
825 NULL, NULL);
827 FINISH_ARGS;
828 ADD_TEXT ("\n");
830 while (*text) {
831 const char *end = strchr (text, ',');
832 if (!end) end = text + strlen (text);
834 ADD_TEXT (pre);
835 ADD_LINK_TEXT (text, end - text);
837 text = *end ? end + 1 : end;
839 pre = _(", ");
841 ADD_TEXT ("\n");
842 break;
844 case GNM_FUNC_HELP_END:
845 FINISH_ARGS;
846 return;
847 case GNM_FUNC_HELP_EXTREF: {
848 GtkTextTag *link;
849 char *uri, *tagname;
850 const char *text;
852 FINISH_ARGS;
854 * We put in just one link and let the web page handle
855 * the rest. In particular, we do not even look at
856 * what the help->text is here.
858 if (seen_extref)
859 break;
861 uri = g_strdup_printf ("http://www.gnumeric.org/func-doc.shtml?%s", func->name);
863 tagname = g_strdup_printf ("EXTLINK-%s", func->name);
864 link = make_link
865 (description, target, tagname,
866 G_CALLBACK (cb_link_event), NULL);
868 g_object_set_data_full (G_OBJECT (link),
869 "uri", uri,
870 g_free);
872 ADD_TEXT (_("Further information: "));
874 text = _("online descriptions");
875 ADD_LINK_TEXT (text, strlen (text));
877 ADD_TEXT (".\n");
879 seen_extref = TRUE;
880 break;
882 case GNM_FUNC_HELP_EXCEL: {
883 const char *text = F2 (func, help->text);
884 FINISH_ARGS;
885 ADD_TEXT ("\n");
886 ADD_TEXT (_("Microsoft Excel: "));
887 ADD_TEXT_WITH_ARGS (text);
888 ADD_TEXT ("\n");
889 break;
891 case GNM_FUNC_HELP_ODF: {
892 const char *text = F2 (func, help->text);
893 FINISH_ARGS;
894 ADD_TEXT ("\n");
895 ADD_TEXT (_("ODF (OpenFormula): "));
896 ADD_TEXT_WITH_ARGS (text);
897 ADD_TEXT ("\n");
898 break;
900 default:
901 break;
906 #undef ADD_TEXT_WITH_ARGS
907 #undef ADD_TEXT
908 #undef ADD_LTEXT
909 #undef ADD_BOLD_TEXT
910 #undef ADD_LINK_TEXT
911 #undef FINISH_ARGS
913 typedef struct {
914 GnmFunc *fd;
915 FunctionSelectState *state;
916 GtkTreePath *path;
917 } dialog_function_select_find_func_t;
920 static gboolean
921 dialog_function_select_search_func (GtkTreeModel *model,
922 GtkTreePath *path,
923 GtkTreeIter *iter,
924 gpointer dt)
926 GnmFunc* fd;
927 dialog_function_select_find_func_t *data = dt;
929 gtk_tree_model_get (model, iter,
930 FUNCTION, &fd,
931 -1);
932 if (fd == data->fd) {
933 data->path = gtk_tree_path_copy (path);
934 return TRUE;
936 return FALSE;
939 static void
940 dialog_function_select_find_func (FunctionSelectState *state, char* name)
942 GnmFunc *fd;
944 if (name == NULL)
945 return;
947 fd = gnm_func_lookup (name, state->wb);
948 if (fd != NULL) {
949 dialog_function_select_find_func_t data = {fd, state, NULL};
950 GtkTreeSelection *selection = gtk_tree_view_get_selection
951 (state->treeview);
952 GtkTreePath *path;
954 gtk_tree_model_foreach (GTK_TREE_MODEL (state->model_functions),
955 dialog_function_select_search_func,
956 &data);
957 if (data.path != NULL) {
958 GtkTreeIter iter;
959 if (gtk_tree_model_get_iter
960 (GTK_TREE_MODEL (state->model_functions), &iter,
961 data.path))
962 gtk_list_store_set (state->model_functions,
963 &iter,
964 FUNCTION_VISIBLE, TRUE,
965 -1);
967 path = gtk_tree_model_filter_convert_child_path_to_path
968 (GTK_TREE_MODEL_FILTER (state->model_filter),
969 data.path);
971 gtk_tree_selection_select_path (selection,
972 path);
973 gtk_tree_view_scroll_to_cell (state->treeview, path,
974 NULL, FALSE, 0., 0.);
975 gtk_tree_path_free (path);
976 gtk_tree_path_free (data.path);
977 } else
978 g_warning ("Function %s was not found in its category", name);
980 } else
981 g_warning ("Function %s was not found", name);
984 typedef struct {
985 FunctionSelectState *state;
986 gchar * name;
987 } cb_dialog_function_select_idle_handler_t;
989 static gboolean
990 cb_dialog_function_select_idle_handler (gpointer dt)
992 cb_dialog_function_select_idle_handler_t *data = dt;
994 dialog_function_select_find_func (data->state, data->name);
996 g_free (data->name);
997 g_free (data);
999 return FALSE;
1002 static void
1003 cb_description_clicked (GtkTextBuffer *textbuffer,
1004 GtkTextIter *location,
1005 GtkTextMark *mark,
1006 FunctionSelectState *state)
1008 const char * mark_name;
1009 GtkTextTag *link;
1010 GtkTextIter *start;
1011 GtkTextIter *end;
1012 cb_dialog_function_select_idle_handler_t *data;
1014 if ((mark == NULL) || ((mark_name = gtk_text_mark_get_name (mark)) == NULL)
1015 || (strcmp(mark_name, "selection_bound") != 0))
1016 return;
1018 link = gtk_text_tag_table_lookup
1019 (gtk_text_buffer_get_tag_table (textbuffer), "LINK");
1021 if ((link == NULL) || !gtk_text_iter_has_tag (location, link))
1022 return;
1024 start = gtk_text_iter_copy (location);
1025 end = gtk_text_iter_copy (location);
1027 if (!gtk_text_iter_begins_tag (start, link))
1028 gtk_text_iter_backward_to_tag_toggle (start, link);
1029 if (!gtk_text_iter_ends_tag (end, link))
1030 gtk_text_iter_forward_to_tag_toggle (end, link);
1032 data = g_new(cb_dialog_function_select_idle_handler_t, 1);
1034 data->name = gtk_text_buffer_get_text (textbuffer, start, end, FALSE);
1035 gtk_text_iter_free (start);
1036 gtk_text_iter_free (end);
1037 data->state = state;
1039 g_idle_add_full (G_PRIORITY_HIGH_IDLE, cb_dialog_function_select_idle_handler,
1040 data, NULL);
1043 static void
1044 cb_dialog_function_select_fun_selection_changed (GtkTreeSelection *selection,
1045 FunctionSelectState *state)
1047 GtkTreeIter iter;
1048 GtkTreeModel *model;
1049 GnmFunc const *func;
1050 GtkTextBuffer *description;
1051 GtkTextMark *mark;
1052 gboolean active = FALSE;
1054 description = gtk_text_view_get_buffer (state->description_view);
1056 mark = gtk_text_buffer_get_mark (description, "start-mark");
1057 gtk_text_view_scroll_to_mark (state->description_view, mark,
1058 0.1, TRUE, 0.0, 0.0);
1059 gtk_text_buffer_set_text (description, "", 0);
1061 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
1062 gtk_tree_model_get (model, &iter,
1063 FUNCTION, &func,
1064 -1);
1066 gnm_func_load_if_stub ((GnmFunc *)func);
1068 if (func->help == NULL)
1069 gtk_text_buffer_set_text (description, "?", -1);
1070 else
1071 describe_new_style (description,
1072 GTK_WIDGET (state->description_view),
1073 func, state->sheet);
1074 active = TRUE;
1076 gtk_widget_set_sensitive (state->ok_button, active);
1077 gtk_widget_set_sensitive (state->paste_button, active);
1081 /**********************************************************************/
1082 /* Setup Functions */
1083 /**********************************************************************/
1085 static const gchar *
1086 dialog_function_select_peek_description (GnmFunc *func)
1088 GnmFuncHelp const *help;
1090 gnm_func_load_if_stub (func);
1091 help = func->help;
1093 if (help == NULL)
1094 return "";
1096 for (; TRUE; help++) {
1097 switch (help->type) {
1098 case GNM_FUNC_HELP_ARG:
1099 case GNM_FUNC_HELP_NOTE:
1100 case GNM_FUNC_HELP_EXAMPLES:
1101 case GNM_FUNC_HELP_SEEALSO:
1102 case GNM_FUNC_HELP_EXTREF:
1103 case GNM_FUNC_HELP_EXCEL:
1104 case GNM_FUNC_HELP_ODF:
1105 case GNM_FUNC_HELP_DESCRIPTION:
1106 default:
1107 break;
1108 case GNM_FUNC_HELP_NAME: {
1109 const char *text = F2 (func, help->text);
1110 const char *colon = strchr (text, ':');
1111 return (colon ? colon + 1 : text);
1113 case GNM_FUNC_HELP_END:
1114 return "";
1120 static gchar *
1121 dialog_function_select_get_description (GnmFunc *func, PangoAttrList **pal)
1123 PangoAttribute *attr;
1124 char const *desc = dialog_function_select_peek_description (func);
1125 char const *here;
1126 GString* gstr = g_string_new (NULL);
1128 *pal = pango_attr_list_new ();
1130 if (desc != NULL) {
1131 while (*desc != '\0') {
1132 here = strstr (desc, "@{");
1133 if (here == NULL) {
1134 g_string_append (gstr, desc);
1135 break;
1137 g_string_append_len (gstr, desc, here - desc);
1138 attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
1139 attr->start_index = gstr->len;
1140 desc = here + 2;
1141 here = strchr (desc,'}');
1142 if (here == NULL) {
1143 g_string_append (gstr, desc);
1144 pango_attr_list_insert (*pal, attr);
1145 break;
1147 g_string_append_len (gstr, desc, here - desc);
1148 attr->end_index = gstr->len;
1149 pango_attr_list_insert (*pal, attr);
1150 desc = here + 1;
1154 return g_string_free (gstr, FALSE);
1159 static void
1160 dialog_function_select_load_tree (FunctionSelectState *state)
1162 GtkTreeIter iter;
1163 GnmFuncGroup const * cat;
1164 GSList *funcs = NULL, *ptr;
1165 GnmFunc *func;
1166 gint i = 0;
1167 PangoAttrList *pal;
1168 gchar *desc;
1170 gtk_list_store_clear (state->model_functions);
1172 while ((cat = gnm_func_group_get_nth (i++)) != NULL)
1173 funcs = g_slist_concat (funcs,
1174 g_slist_copy (cat->functions));
1176 funcs = g_slist_sort_with_data (funcs,
1177 dialog_function_select_by_name,
1178 state);
1180 for (ptr = funcs; ptr; ptr = ptr->next) {
1181 func = ptr->data;
1182 if (!(func->flags &
1183 (GNM_FUNC_INTERNAL | GNM_FUNC_IS_PLACEHOLDER))) {
1184 gtk_list_store_append (state->model_functions, &iter);
1185 gnm_func_inc_usage (func);
1186 desc = dialog_function_select_get_description (func, &pal);
1187 gtk_list_store_set
1188 (state->model_functions, &iter,
1189 FUN_NAME, gnm_func_get_name (func, state->localized_function_names),
1190 FUNCTION, func,
1191 FUNCTION_DESC, desc,
1192 FUNCTION_PAL, pal,
1193 FUNCTION_CAT, func->fn_group,
1194 FUNCTION_VISIBLE, TRUE,
1195 FUNCTION_RECENT, FALSE,
1196 FUNCTION_USED, (func->usage_count > 1),
1197 -1);
1198 g_free (desc);
1199 pango_attr_list_unref (pal);
1204 g_slist_free (funcs);
1207 static void
1208 dialog_function_select_init (FunctionSelectState *state)
1210 GtkTreeViewColumn *column;
1211 GtkTreeSelection *selection;
1212 GtkTextIter where;
1213 GtkTextBuffer *description;
1214 GtkCellRenderer *cell;
1215 GtkWidget *cancel_button;
1216 GtkWidget *close_button;
1218 g_object_set_data (G_OBJECT (state->dialog), FUNCTION_SELECT_DIALOG_KEY,
1219 state);
1221 /* Set-up combo box */
1222 state->cb = GTK_COMBO_BOX
1223 (go_gtk_builder_get_widget (state->gui, "category-box"));
1224 state->model = gtk_list_store_new
1225 (NUM_CAT_COLUMNS, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_BOOLEAN);
1227 gtk_combo_box_set_model (state->cb, GTK_TREE_MODEL (state->model));
1228 g_object_unref (state->model);
1229 cell = gtk_cell_renderer_text_new ();
1230 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (state->cb), cell, TRUE);
1231 gtk_cell_layout_add_attribute
1232 (GTK_CELL_LAYOUT (state->cb), cell, "text", CAT_NAME);
1233 dialog_function_select_load_cb (state);
1234 gtk_combo_box_set_row_separator_func
1235 (state->cb, dialog_function_select_cat_row_separator,
1236 state, NULL);
1237 g_signal_connect (state->cb, "changed",
1238 G_CALLBACK (dialog_function_select_cat_changed),
1239 state);
1240 /* Finished set-up of combo box */
1242 /* Set-up treeview */
1244 state->model_functions = gtk_list_store_new
1245 (NUM_COLUMNS,
1246 /* FUN_NAME, */
1247 /* FUNCTION, */
1248 /* FUNCTION_DESC, */
1249 /* FUNCTION_PAL, */
1250 /* FUNCTION_CAT, */
1251 /* FUNCTION_VISIBLE, */
1252 /* FUNCTION_RECENT, */
1253 /* FUNCTION_USED, */
1254 G_TYPE_STRING, G_TYPE_POINTER,
1255 G_TYPE_STRING, PANGO_TYPE_ATTR_LIST,
1256 G_TYPE_POINTER, G_TYPE_BOOLEAN,
1257 G_TYPE_BOOLEAN, G_TYPE_BOOLEAN);
1259 state->model_filter = gtk_tree_model_filter_new
1260 (GTK_TREE_MODEL (state->model_functions), NULL);
1261 g_object_unref (state->model_functions);
1262 gtk_tree_model_filter_set_visible_column
1263 (GTK_TREE_MODEL_FILTER (state->model_filter), FUNCTION_VISIBLE);
1265 state->treeview = GTK_TREE_VIEW
1266 (go_gtk_builder_get_widget (state->gui, "function-list"));
1267 gtk_tree_view_set_model (state->treeview,
1268 state->model_filter);
1269 g_object_unref (state->model_filter);
1271 selection = gtk_tree_view_get_selection (state->treeview);
1272 gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
1273 g_signal_connect (selection, "changed",
1274 G_CALLBACK
1275 (cb_dialog_function_select_fun_selection_changed),
1276 state);
1278 column = gtk_tree_view_column_new_with_attributes
1279 (_("Name"),
1280 gtk_cell_renderer_text_new (),
1281 "text", FUN_NAME, NULL);
1282 gtk_tree_view_append_column (state->treeview, column);
1283 column = gtk_tree_view_column_new_with_attributes
1284 (_("Description"),
1285 gtk_cell_renderer_text_new (),
1286 "text", FUNCTION_DESC,
1287 "attributes", FUNCTION_PAL, NULL);
1288 gtk_tree_view_append_column (state->treeview, column);
1290 gtk_tree_view_set_headers_visible (state->treeview, FALSE);
1291 /* Finished set-up of treeview */
1293 dialog_function_select_load_tree (state);
1294 dialog_function_load_recent_funcs (state);
1296 state->search_entry = go_gtk_builder_get_widget (state->gui,
1297 "search-entry");
1298 if (state->paste.prefix != NULL)
1299 gtk_entry_set_text (GTK_ENTRY (state->search_entry),
1300 state->paste.prefix);
1302 g_signal_connect (G_OBJECT (state->search_entry),
1303 "icon-press",
1304 G_CALLBACK
1305 (dialog_function_select_erase_search_entry),
1306 state);
1308 g_signal_connect (G_OBJECT (state->search_entry),
1309 "activate",
1310 G_CALLBACK (dialog_function_select_search),
1311 state);
1312 if (state->mode != HELP_MODE)
1313 g_signal_connect (G_OBJECT (state->treeview),
1314 "row-activated",
1315 G_CALLBACK (cb_dialog_function_row_activated),
1316 state);
1318 gtk_paned_set_position (GTK_PANED (go_gtk_builder_get_widget
1319 (state->gui, "vpaned1")), 300);
1321 state->description_view = GTK_TEXT_VIEW (go_gtk_builder_get_widget
1322 (state->gui, "description"));
1323 gtk_style_context_add_class
1324 (gtk_widget_get_style_context (GTK_WIDGET (state->description_view)),
1325 "function-help");
1326 description = gtk_text_view_get_buffer (state->description_view);
1327 gtk_text_buffer_get_start_iter (description, &where);
1328 gtk_text_buffer_create_mark (description, "start-mark", &where, TRUE);
1330 g_signal_connect_after (G_OBJECT (description),
1331 "mark-set",
1332 G_CALLBACK (cb_description_clicked), state);
1334 state->ok_button = go_gtk_builder_get_widget (state->gui, "ok_button");
1335 gtk_widget_set_sensitive (state->ok_button, FALSE);
1336 g_signal_connect (G_OBJECT (state->ok_button),
1337 "clicked",
1338 G_CALLBACK (cb_dialog_function_select_ok_clicked), state);
1339 state->paste_button = go_gtk_builder_get_widget (state->gui, "paste_button");
1340 gtk_widget_set_sensitive (state->paste_button, FALSE);
1341 g_signal_connect (G_OBJECT (state->paste_button),
1342 "clicked",
1343 G_CALLBACK (cb_dialog_function_select_paste_clicked), state);
1344 cancel_button = go_gtk_builder_get_widget (state->gui, "cancel_button");
1345 g_signal_connect (G_OBJECT (cancel_button), "clicked",
1346 G_CALLBACK (cb_dialog_function_select_cancel_clicked), state);
1347 close_button = go_gtk_builder_get_widget (state->gui, "close_button");
1348 g_signal_connect (G_OBJECT (close_button), "clicked",
1349 G_CALLBACK (cb_dialog_function_select_cancel_clicked), state);
1351 gnm_dialog_setup_destroy_handlers
1352 (GTK_DIALOG (state->dialog),
1353 state->wbcg,
1354 GNM_DIALOG_DESTROY_CURRENT_SHEET_REMOVED);
1356 gnm_init_help_button (
1357 go_gtk_builder_get_widget (state->gui, "help_button"),
1358 GNUMERIC_HELP_LINK_FUNCTION_SELECT);
1359 g_object_set_data_full
1360 (G_OBJECT (state->dialog),
1361 "state", state,
1362 (GDestroyNotify) cb_dialog_function_select_destroy);
1364 if (state->paste.prefix != NULL)
1365 dialog_function_select_search
1366 (GTK_ENTRY (state->search_entry), state);
1368 gtk_widget_set_visible (close_button, state->mode != GURU_MODE);
1369 gtk_widget_set_visible (go_gtk_builder_get_widget
1370 (state->gui, "help_button"),
1371 state->mode == GURU_MODE);
1372 gtk_widget_set_visible (cancel_button, state->mode == GURU_MODE);
1373 gtk_widget_set_visible (state->ok_button, state->mode == GURU_MODE);
1374 gtk_widget_set_visible (state->paste_button, state->mode == PASTE_MODE);
1375 gtk_widget_set_visible (go_gtk_builder_get_widget
1376 (state->gui, "title_label"),
1377 state->mode == GURU_MODE);
1378 gtk_combo_box_set_active (state->cb, state->mode == HELP_MODE ? 2 : 0);
1379 switch (state->mode) {
1380 case GURU_MODE:
1381 break;
1382 case HELP_MODE:
1383 gtk_window_set_title (GTK_WINDOW (state->dialog),
1384 _("Gnumeric Function Help Browser"));
1385 break;
1386 case PASTE_MODE:
1387 gtk_window_set_title (GTK_WINDOW (state->dialog),
1388 _("Paste Function Name dialog"));
1389 break;
1393 static void
1394 dialog_function_select_full (WBCGtk *wbcg, char const *guru_key,
1395 char const *key, DialogMode mode, gint from, gint to)
1397 FunctionSelectState* state;
1398 GtkBuilder *gui;
1400 g_return_if_fail (wbcg != NULL);
1402 if (gnm_dialog_raise_if_exists (wbcg, key))
1403 return;
1404 gui = gnm_gtk_builder_load ("res:ui/function-select.ui", NULL, GO_CMD_CONTEXT (wbcg));
1405 if (gui == NULL)
1406 return;
1408 state = g_new (FunctionSelectState, 1);
1409 state->wbcg = wbcg;
1410 state->sheet = wb_control_cur_sheet (GNM_WBC (wbcg));
1411 state->localized_function_names = state->sheet->convs->localized_function_names;
1412 state->wb = state->sheet->workbook;
1413 state->gui = gui;
1414 state->dialog = go_gtk_builder_get_widget (state->gui, "selection_dialog");
1415 state->formula_guru_key = guru_key;
1416 state->recent_funcs = NULL;
1417 state->mode = mode;
1418 state->paste.from = from;
1419 state->paste.to = to;
1421 gnm_dialog_setup_destroy_handlers (GTK_DIALOG (state->dialog),
1422 state->wbcg,
1423 GNM_DIALOG_DESTROY_SHEET_REMOVED);
1425 if (mode == PASTE_MODE && state->paste.from >= 0) {
1426 GtkEditable *entry
1427 = GTK_EDITABLE (wbcg_get_entry (state->wbcg));
1428 state->paste.prefix = gtk_editable_get_chars
1429 (entry, state->paste.from,
1430 state->paste.to);
1431 } else
1432 state->paste.prefix = NULL;
1434 dialog_function_select_init (state);
1435 gnm_keyed_dialog (state->wbcg, GTK_WINDOW (state->dialog),
1436 key);
1438 gtk_widget_show (state->dialog);
1441 void
1442 dialog_function_select (WBCGtk *wbcg, char const *key)
1444 dialog_function_select_full (wbcg, key,
1445 FUNCTION_SELECT_KEY, GURU_MODE, -1, -1);
1448 void
1449 dialog_function_select_help (WBCGtk *wbcg)
1451 dialog_function_select_full (wbcg, NULL,
1452 FUNCTION_SELECT_HELP_KEY, HELP_MODE,
1453 -1, -1);
1456 void
1457 dialog_function_select_paste (WBCGtk *wbcg, gint from, gint to)
1459 dialog_function_select_full (wbcg, NULL,
1460 FUNCTION_SELECT_PASTE_KEY, PASTE_MODE,
1461 from, to);