2 * dialog-function-select.c: Implements the function selector
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>
27 #include <dialogs/dialogs.h>
28 #include <dialogs/help.h>
35 #include <application.h>
40 #include <gnumeric-conf.h>
41 #include <gnm-format.h>
42 #include <auto-format.h>
44 #include <gsf/gsf-impl-utils.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"
67 gboolean localized_function_names
;
72 GtkWidget
*paste_button
;
75 GtkListStore
*model_functions
;
76 GtkTreeModel
*model_filter
;
77 GtkTreeView
*treeview
;
78 GtkTextView
*description_view
;
79 GtkWidget
*search_entry
;
90 char const *formula_guru_key
;
91 } FunctionSelectState
;
111 /*************************************************************************/
112 /* Search Functions */
113 /*************************************************************************/
117 gboolean recent_only
;
119 GnmFuncGroup
const * cat
;
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
;
130 gboolean visible
, was_visible
, recent
, used
;
131 GnmFuncGroup
const * cat
;
133 gtk_tree_model_get (model
, iter
,
135 FUNCTION_DESC
, &desc
,
136 FUNCTION_VISIBLE
, &was_visible
,
137 FUNCTION_RECENT
, &recent
,
138 FUNCTION_USED
, &used
,
142 if (specs
->recent_only
&& !recent
)
144 else if (specs
->used_only
&& !used
)
146 else if (specs
->cat
!= NULL
&& specs
->cat
!= cat
)
148 else if (specs
->text
== NULL
)
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
));
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
));
177 if (visible
!= was_visible
)
178 gtk_list_store_set (GTK_LIST_STORE (model
), iter
,
179 FUNCTION_VISIBLE
, visible
,
185 dialog_function_select_search (GtkEntry
*entry
, gpointer data
)
187 search_t specs
= {NULL
, FALSE
, FALSE
, NULL
};
188 FunctionSelectState
*state
= data
;
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
,
199 = (specs
.cat
!= NULL
&&
200 specs
.cat
== GINT_TO_POINTER(-1));
202 = (specs
.cat
!= NULL
&&
203 specs
.cat
== GINT_TO_POINTER(-2));
204 if (specs
.recent_only
|| specs
.used_only
)
208 gtk_tree_model_foreach (GTK_TREE_MODEL (state
->model_functions
),
209 cb_dialog_function_select_search_all
,
214 dialog_function_select_erase_search_entry (GtkEntry
*entry
,
215 G_GNUC_UNUSED GtkEntryIconPosition icon_pos
,
216 G_GNUC_UNUSED GdkEvent
*event
,
219 gtk_entry_set_text (entry
, "");
220 dialog_function_select_search (entry
, data
);
224 dialog_function_select_cat_changed (G_GNUC_UNUSED GtkComboBox
*widget
,
227 FunctionSelectState
*state
= data
;
229 dialog_function_select_search (GTK_ENTRY (state
->search_entry
),
233 /*************************************************************************/
236 cb_dialog_function_load_recent_funcs(GtkTreeModel
*model
,
237 G_GNUC_UNUSED GtkTreePath
*path
,
243 gtk_tree_model_get (model
, iter
,
247 gtk_list_store_set (GTK_LIST_STORE (model
), iter
,
248 FUNCTION_RECENT
, TRUE
,
256 dialog_function_load_recent_funcs (FunctionSelectState
*state
)
258 GSList
const *recent_funcs
;
260 for (recent_funcs
= gnm_conf_get_functionselector_recentfunctions ();
262 recent_funcs
= recent_funcs
->next
) {
263 char const *name
= recent_funcs
->data
;
269 fd
= gnm_func_lookup (name
, NULL
);
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
,
280 dialog_function_write_recent_func (FunctionSelectState
*state
, GnmFunc
const *fd
)
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
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
);
304 cb_unref (GtkTreeModel
*model
, G_GNUC_UNUSED GtkTreePath
*path
,
305 GtkTreeIter
*iter
, G_GNUC_UNUSED gpointer data
)
309 gtk_tree_model_get (model
, iter
,
312 gnm_func_dec_usage (f
);
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
),
332 g_free (state
->paste
.prefix
);
337 * cb_dialog_function_select_cancel_clicked:
341 * Close (destroy) the dialog
344 cb_dialog_function_select_cancel_clicked (G_GNUC_UNUSED GtkWidget
*button
,
345 FunctionSelectState
*state
)
347 gtk_widget_destroy (state
->dialog
);
352 * cb_dialog_function_select_ok_clicked:
356 * Close (destroy) the dialog
359 cb_dialog_function_select_ok_clicked (G_GNUC_UNUSED GtkWidget
*button
,
360 FunctionSelectState
*state
)
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
,
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
);
380 gtk_widget_destroy (state
->dialog
);
385 * cb_dialog_function_select_paste_clicked:
389 * Close (destroy) the dialog
392 cb_dialog_function_select_paste_clicked (G_GNUC_UNUSED GtkWidget
*button
,
393 FunctionSelectState
*state
)
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
)) {
403 = GTK_EDITABLE (wbcg_get_entry (state
->wbcg
));
405 gtk_tree_model_get (model
, &iter
,
409 dialog_function_write_recent_func (state
, func
);
410 if (state
->paste
.from
>= 0)
411 gtk_editable_select_region
412 (entry
, state
->paste
.from
,
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
);
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
) {
434 cb_dialog_function_select_ok_clicked (NULL
, state
);
437 cb_dialog_function_select_paste_clicked (NULL
, state
);
445 dialog_function_select_by_name (gconstpointer a_
, gconstpointer b_
,
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 /*************************************************************************/
464 } dialog_function_select_load_cb_t
;
467 cb_dialog_function_select_load_cb (GtkTreeModel
*model
,
468 G_GNUC_UNUSED GtkTreePath
*path
,
472 dialog_function_select_load_cb_t
*specs
= data
;
477 gtk_tree_model_get (model
, iter
,
482 if (ptr
== NULL
|| ptr
== GINT_TO_POINTER(-1)
483 || ptr
== GINT_TO_POINTER(-2))
485 else if (go_utf8_collate_casefold (specs
->name
, name
) < 0) {
486 specs
->iter
= gtk_tree_iter_copy (iter
);
497 dialog_function_select_load_cb (FunctionSelectState
*state
)
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"),
509 CAT_SEPARATOR
, FALSE
,
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
,
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
,
524 gtk_list_store_insert_before (state
->model
, &p_iter
, NULL
);
525 gtk_list_store_set (state
->model
, &p_iter
,
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
);
536 gtk_tree_model_foreach (GTK_TREE_MODEL (state
->model
),
537 cb_dialog_function_select_load_cb
,
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
,
544 CAT_SEPARATOR
, FALSE
,
546 if (specs
.iter
!= NULL
)
547 gtk_tree_iter_free (specs
.iter
);
552 dialog_function_select_cat_row_separator (GtkTreeModel
*model
,
554 G_GNUC_UNUSED gpointer data
)
558 gtk_tree_model_get (model
, iter
,
565 /*************************************************************************/
566 /* Functions related to the description field */
567 /*************************************************************************/
570 make_link (GtkTextBuffer
*description
, GtkWidget
*target
, const char *name
,
571 GCallback cb
, gpointer user
)
574 gtk_text_tag_table_lookup
575 (gtk_text_buffer_get_tag_table (description
), name
);
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
586 "underline", PANGO_UNDERLINE_SINGLE
,
587 "foreground", link_color_text
,
590 g_free (link_color_text
);
593 g_signal_connect (link
, "event", cb
, user
);
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
;
614 if (event
->type
!= GDK_BUTTON_PRESS
)
617 error
= go_gtk_url_show (uri
, gdk_event_get_screen (event
));
619 g_printerr ("Failed to show %s\n(%s)\n",
622 g_error_free (error
);
629 case GDK_ENTER_NOTIFY
:
630 case GDK_LEAVE_NOTIFY
:
631 /* We aren't getting these. */
641 make_expr_example (Sheet
*sheet
, const char *text
,
642 gboolean localized
, gboolean consider_format
)
644 GnmLocale
*oldlocale
= NULL
;
645 GnmExprTop
const *texpr
;
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] == ')') {
657 tmp_text
= g_strdup (text
+ 5);
658 p
= tmp_text
+ strlen (tmp_text
) - 1;
659 while (p
>= tmp_text
&& p
[0] != '"') p
--;
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
--;
667 eval_pos_init_sheet (&ep
, sheet
);
668 parse_pos_init_evalpos (&pp
, &ep
);
671 oldlocale
= gnm_push_C_locale ();
672 texpr
= gnm_expr_parse_str (text
, &pp
,
673 GNM_EXPR_PARSE_DEFAULT
,
677 gnm_pop_C_locale (oldlocale
);
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
);
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
);
692 res
= g_strdup_printf (_("%s evaluates to %s."), etxt
, vtxt
);
697 g_warning ("Failed to parse [%s]", text
);
702 go_format_unref (fmt
);
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) {\
718 gnm_func_count_args (func, &min, &max);\
719 if (max == G_MAXINT) { \
720 ADD_BOLD_TEXT(UNICODE_ELLIPSIS, strlen(UNICODE_ELLIPSIS)); \
722 args_finished = TRUE; \
727 describe_new_style (GtkTextBuffer
*description
,
729 GnmFunc
const *func
, Sheet
*sheet
)
731 GnmFuncHelp
const *help
;
734 gtk_text_buffer_create_tag
736 "weight", PANGO_WEIGHT_BOLD
,
738 gboolean seen_args
= FALSE
;
739 gboolean args_finished
= FALSE
;
740 gboolean seen_examples
= FALSE
;
741 gboolean seen_extref
= FALSE
;
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
, ':');
754 ADD_BOLD_TEXT (text
, colon
- text
);
756 ADD_TEXT_WITH_ARGS (colon
+ 1);
760 case GNM_FUNC_HELP_ARG
: {
761 const char *text
= F2 (func
, help
->text
);
762 const char *colon
= strchr (text
, ':');
768 ADD_TEXT (_("Arguments:"));
772 ADD_BOLD_TEXT (text
, colon
- text
);
774 ADD_TEXT_WITH_ARGS (colon
+ 1);
778 case GNM_FUNC_HELP_DESCRIPTION
: {
779 const char *text
= F2 (func
, help
->text
);
782 ADD_TEXT_WITH_ARGS (text
);
786 case GNM_FUNC_HELP_NOTE
: {
787 const char *text
= F2 (func
, help
->text
);
790 ADD_TEXT (_("Note: "));
791 ADD_TEXT_WITH_ARGS (text
);
795 case GNM_FUNC_HELP_EXAMPLES
: {
796 const char *text
= F2 (func
, help
->text
);
797 gboolean was_translated
= (text
!= help
->text
);
800 if (!seen_examples
) {
801 seen_examples
= TRUE
;
803 ADD_TEXT (_("Examples:"));
807 if (text
[0] == '=') {
809 make_expr_example (sheet
, text
+ 1,
815 ADD_TEXT_WITH_ARGS (text
);
820 case GNM_FUNC_HELP_SEEALSO
: {
821 const char *text
= help
->text
; /* Not translated */
822 const char *pre
= _("See also: ");
824 make_link (description
, target
, "LINK",
831 const char *end
= strchr (text
, ',');
832 if (!end
) end
= text
+ strlen (text
);
835 ADD_LINK_TEXT (text
, end
- text
);
837 text
= *end
? end
+ 1 : end
;
844 case GNM_FUNC_HELP_END
:
847 case GNM_FUNC_HELP_EXTREF
: {
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.
861 uri
= g_strdup_printf ("http://www.gnumeric.org/func-doc.shtml?%s", func
->name
);
863 tagname
= g_strdup_printf ("EXTLINK-%s", func
->name
);
865 (description
, target
, tagname
,
866 G_CALLBACK (cb_link_event
), NULL
);
868 g_object_set_data_full (G_OBJECT (link
),
872 ADD_TEXT (_("Further information: "));
874 text
= _("online descriptions");
875 ADD_LINK_TEXT (text
, strlen (text
));
882 case GNM_FUNC_HELP_EXCEL
: {
883 const char *text
= F2 (func
, help
->text
);
886 ADD_TEXT (_("Microsoft Excel: "));
887 ADD_TEXT_WITH_ARGS (text
);
891 case GNM_FUNC_HELP_ODF
: {
892 const char *text
= F2 (func
, help
->text
);
895 ADD_TEXT (_("ODF (OpenFormula): "));
896 ADD_TEXT_WITH_ARGS (text
);
906 #undef ADD_TEXT_WITH_ARGS
915 FunctionSelectState
*state
;
917 } dialog_function_select_find_func_t
;
921 dialog_function_select_search_func (GtkTreeModel
*model
,
927 dialog_function_select_find_func_t
*data
= dt
;
929 gtk_tree_model_get (model
, iter
,
932 if (fd
== data
->fd
) {
933 data
->path
= gtk_tree_path_copy (path
);
940 dialog_function_select_find_func (FunctionSelectState
*state
, char* name
)
947 fd
= gnm_func_lookup (name
, state
->wb
);
949 dialog_function_select_find_func_t data
= {fd
, state
, NULL
};
950 GtkTreeSelection
*selection
= gtk_tree_view_get_selection
954 gtk_tree_model_foreach (GTK_TREE_MODEL (state
->model_functions
),
955 dialog_function_select_search_func
,
957 if (data
.path
!= NULL
) {
959 if (gtk_tree_model_get_iter
960 (GTK_TREE_MODEL (state
->model_functions
), &iter
,
962 gtk_list_store_set (state
->model_functions
,
964 FUNCTION_VISIBLE
, TRUE
,
967 path
= gtk_tree_model_filter_convert_child_path_to_path
968 (GTK_TREE_MODEL_FILTER (state
->model_filter
),
971 gtk_tree_selection_select_path (selection
,
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
);
978 g_warning ("Function %s was not found in its category", name
);
981 g_warning ("Function %s was not found", name
);
985 FunctionSelectState
*state
;
987 } cb_dialog_function_select_idle_handler_t
;
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
);
1003 cb_description_clicked (GtkTextBuffer
*textbuffer
,
1004 GtkTextIter
*location
,
1006 FunctionSelectState
*state
)
1008 const char * mark_name
;
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))
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
))
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
,
1044 cb_dialog_function_select_fun_selection_changed (GtkTreeSelection
*selection
,
1045 FunctionSelectState
*state
)
1048 GtkTreeModel
*model
;
1049 GnmFunc
const *func
;
1050 GtkTextBuffer
*description
;
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
,
1066 gnm_func_load_if_stub ((GnmFunc
*)func
);
1068 if (func
->help
== NULL
)
1069 gtk_text_buffer_set_text (description
, "?", -1);
1071 describe_new_style (description
,
1072 GTK_WIDGET (state
->description_view
),
1073 func
, state
->sheet
);
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
);
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
:
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
:
1121 dialog_function_select_get_description (GnmFunc
*func
, PangoAttrList
**pal
)
1123 PangoAttribute
*attr
;
1124 char const *desc
= dialog_function_select_peek_description (func
);
1126 GString
* gstr
= g_string_new (NULL
);
1128 *pal
= pango_attr_list_new ();
1131 while (*desc
!= '\0') {
1132 here
= strstr (desc
, "@{");
1134 g_string_append (gstr
, desc
);
1137 g_string_append_len (gstr
, desc
, here
- desc
);
1138 attr
= pango_attr_weight_new (PANGO_WEIGHT_BOLD
);
1139 attr
->start_index
= gstr
->len
;
1141 here
= strchr (desc
,'}');
1143 g_string_append (gstr
, desc
);
1144 pango_attr_list_insert (*pal
, attr
);
1147 g_string_append_len (gstr
, desc
, here
- desc
);
1148 attr
->end_index
= gstr
->len
;
1149 pango_attr_list_insert (*pal
, attr
);
1154 return g_string_free (gstr
, FALSE
);
1160 dialog_function_select_load_tree (FunctionSelectState
*state
)
1163 GnmFuncGroup
const * cat
;
1164 GSList
*funcs
= NULL
, *ptr
;
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
,
1180 for (ptr
= funcs
; ptr
; ptr
= ptr
->next
) {
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
);
1188 (state
->model_functions
, &iter
,
1189 FUN_NAME
, gnm_func_get_name (func
, state
->localized_function_names
),
1191 FUNCTION_DESC
, desc
,
1193 FUNCTION_CAT
, func
->fn_group
,
1194 FUNCTION_VISIBLE
, TRUE
,
1195 FUNCTION_RECENT
, FALSE
,
1196 FUNCTION_USED
, (func
->usage_count
> 1),
1199 pango_attr_list_unref (pal
);
1204 g_slist_free (funcs
);
1208 dialog_function_select_init (FunctionSelectState
*state
)
1210 GtkTreeViewColumn
*column
;
1211 GtkTreeSelection
*selection
;
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
,
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
,
1237 g_signal_connect (state
->cb
, "changed",
1238 G_CALLBACK (dialog_function_select_cat_changed
),
1240 /* Finished set-up of combo box */
1242 /* Set-up treeview */
1244 state
->model_functions
= gtk_list_store_new
1248 /* FUNCTION_DESC, */
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",
1275 (cb_dialog_function_select_fun_selection_changed
),
1278 column
= gtk_tree_view_column_new_with_attributes
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
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
,
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
),
1305 (dialog_function_select_erase_search_entry
),
1308 g_signal_connect (G_OBJECT (state
->search_entry
),
1310 G_CALLBACK (dialog_function_select_search
),
1312 if (state
->mode
!= HELP_MODE
)
1313 g_signal_connect (G_OBJECT (state
->treeview
),
1315 G_CALLBACK (cb_dialog_function_row_activated
),
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
)),
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
),
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
),
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
),
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
),
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
),
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
) {
1383 gtk_window_set_title (GTK_WINDOW (state
->dialog
),
1384 _("Gnumeric Function Help Browser"));
1387 gtk_window_set_title (GTK_WINDOW (state
->dialog
),
1388 _("Paste Function Name dialog"));
1394 dialog_function_select_full (WBCGtk
*wbcg
, char const *guru_key
,
1395 char const *key
, DialogMode mode
, gint from
, gint to
)
1397 FunctionSelectState
* state
;
1400 g_return_if_fail (wbcg
!= NULL
);
1402 if (gnm_dialog_raise_if_exists (wbcg
, key
))
1404 gui
= gnm_gtk_builder_load ("res:ui/function-select.ui", NULL
, GO_CMD_CONTEXT (wbcg
));
1408 state
= g_new (FunctionSelectState
, 1);
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
;
1414 state
->dialog
= go_gtk_builder_get_widget (state
->gui
, "selection_dialog");
1415 state
->formula_guru_key
= guru_key
;
1416 state
->recent_funcs
= NULL
;
1418 state
->paste
.from
= from
;
1419 state
->paste
.to
= to
;
1421 gnm_dialog_setup_destroy_handlers (GTK_DIALOG (state
->dialog
),
1423 GNM_DIALOG_DESTROY_SHEET_REMOVED
);
1425 if (mode
== PASTE_MODE
&& state
->paste
.from
>= 0) {
1427 = GTK_EDITABLE (wbcg_get_entry (state
->wbcg
));
1428 state
->paste
.prefix
= gtk_editable_get_chars
1429 (entry
, state
->paste
.from
,
1432 state
->paste
.prefix
= NULL
;
1434 dialog_function_select_init (state
);
1435 gnm_keyed_dialog (state
->wbcg
, GTK_WINDOW (state
->dialog
),
1438 gtk_widget_show (state
->dialog
);
1442 dialog_function_select (WBCGtk
*wbcg
, char const *key
)
1444 dialog_function_select_full (wbcg
, key
,
1445 FUNCTION_SELECT_KEY
, GURU_MODE
, -1, -1);
1449 dialog_function_select_help (WBCGtk
*wbcg
)
1451 dialog_function_select_full (wbcg
, NULL
,
1452 FUNCTION_SELECT_HELP_KEY
, HELP_MODE
,
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
,