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 FUNCTION_SELECT_KEY "function-selector-dialog"
48 #define FUNCTION_SELECT_HELP_KEY "function-selector-dialog-help-mode"
49 #define FUNCTION_SELECT_PASTE_KEY "function-selector-dialog-paste-mode"
50 #define FUNCTION_SELECT_DIALOG_KEY "function-selector-dialog"
52 #define UNICODE_ELLIPSIS "\xe2\x80\xa6"
65 gboolean localized_function_names
;
70 GtkWidget
*paste_button
;
73 GtkListStore
*model_functions
;
74 GtkTreeModel
*model_filter
;
75 GtkTreeView
*treeview
;
76 GtkTextView
*description_view
;
77 GtkWidget
*search_entry
;
88 char const *formula_guru_key
;
89 } FunctionSelectState
;
109 /*************************************************************************/
110 /* Search Functions */
111 /*************************************************************************/
115 gboolean recent_only
;
117 GnmFuncGroup
const * cat
;
121 cb_dialog_function_select_search_all (GtkTreeModel
*model
,
122 G_GNUC_UNUSED GtkTreePath
*path
,
123 GtkTreeIter
*iter
, gpointer data
)
125 search_t
*specs
= data
;
128 gboolean visible
, was_visible
, recent
, used
;
129 GnmFuncGroup
const * cat
;
131 gtk_tree_model_get (model
, iter
,
133 FUNCTION_DESC
, &desc
,
134 FUNCTION_VISIBLE
, &was_visible
,
135 FUNCTION_RECENT
, &recent
,
136 FUNCTION_USED
, &used
,
140 if (specs
->recent_only
&& !recent
)
142 else if (specs
->used_only
&& !used
)
144 else if (specs
->cat
!= NULL
&& specs
->cat
!= cat
)
146 else if (specs
->text
== NULL
)
149 gchar
*name_n
, *name_cf
, *text_n
, *text_cf
;
151 text_n
= g_utf8_normalize (specs
->text
, -1, G_NORMALIZE_ALL
);
152 text_cf
= g_utf8_casefold(text_n
, -1);
154 name_n
= g_utf8_normalize (name
, -1, G_NORMALIZE_ALL
);
155 name_cf
= g_utf8_casefold(name_n
, -1);
156 visible
= (NULL
!= g_strstr_len (name_cf
, -1, text_cf
));
161 name_n
= g_utf8_normalize (desc
, -1, G_NORMALIZE_ALL
);
162 name_cf
= g_utf8_casefold(name_n
, -1);
163 visible
= (NULL
!= g_strstr_len (name_cf
, -1, text_cf
));
175 if (visible
!= was_visible
)
176 gtk_list_store_set (GTK_LIST_STORE (model
), iter
,
177 FUNCTION_VISIBLE
, visible
,
183 dialog_function_select_search (GtkEntry
*entry
, gpointer data
)
185 search_t specs
= {NULL
, FALSE
, FALSE
, NULL
};
186 FunctionSelectState
*state
= data
;
189 if (0 != gtk_entry_get_text_length (entry
))
190 specs
.text
= gtk_entry_get_text (entry
);
192 if (gtk_combo_box_get_active_iter (state
->cb
, &iter
)) {
193 gtk_tree_model_get (GTK_TREE_MODEL (state
->model
), &iter
,
194 CATEGORY
, &specs
.cat
,
197 = (specs
.cat
!= NULL
&&
198 specs
.cat
== GINT_TO_POINTER(-1));
200 = (specs
.cat
!= NULL
&&
201 specs
.cat
== GINT_TO_POINTER(-2));
202 if (specs
.recent_only
|| specs
.used_only
)
206 gtk_tree_model_foreach (GTK_TREE_MODEL (state
->model_functions
),
207 cb_dialog_function_select_search_all
,
212 dialog_function_select_erase_search_entry (GtkEntry
*entry
,
213 G_GNUC_UNUSED GtkEntryIconPosition icon_pos
,
214 G_GNUC_UNUSED GdkEvent
*event
,
217 gtk_entry_set_text (entry
, "");
218 dialog_function_select_search (entry
, data
);
222 dialog_function_select_cat_changed (G_GNUC_UNUSED GtkComboBox
*widget
,
225 FunctionSelectState
*state
= data
;
227 dialog_function_select_search (GTK_ENTRY (state
->search_entry
),
231 /*************************************************************************/
234 cb_dialog_function_load_recent_funcs(GtkTreeModel
*model
,
235 G_GNUC_UNUSED GtkTreePath
*path
,
241 gtk_tree_model_get (model
, iter
,
245 gtk_list_store_set (GTK_LIST_STORE (model
), iter
,
246 FUNCTION_RECENT
, TRUE
,
254 dialog_function_load_recent_funcs (FunctionSelectState
*state
)
256 GSList
const *recent_funcs
;
258 for (recent_funcs
= gnm_conf_get_functionselector_recentfunctions ();
260 recent_funcs
= recent_funcs
->next
) {
261 char const *name
= recent_funcs
->data
;
267 fd
= gnm_func_lookup (name
, NULL
);
269 state
->recent_funcs
= g_slist_prepend (state
->recent_funcs
, fd
);
270 gtk_tree_model_foreach (GTK_TREE_MODEL (state
->model_functions
),
271 cb_dialog_function_load_recent_funcs
,
278 dialog_function_write_recent_func (FunctionSelectState
*state
, GnmFunc
const *fd
)
281 GSList
*gconf_value_list
= NULL
;
282 guint ulimit
= gnm_conf_get_functionselector_num_of_recent ();
284 state
->recent_funcs
= g_slist_remove (state
->recent_funcs
, (gpointer
) fd
);
285 state
->recent_funcs
= g_slist_prepend (state
->recent_funcs
, (gpointer
) fd
);
287 while (g_slist_length (state
->recent_funcs
) > ulimit
)
288 state
->recent_funcs
= g_slist_remove (state
->recent_funcs
,
289 g_slist_last (state
->recent_funcs
)->data
);
291 for (rec_funcs
= state
->recent_funcs
; rec_funcs
; rec_funcs
= rec_funcs
->next
) {
292 gconf_value_list
= g_slist_prepend
294 g_strdup (gnm_func_get_name (rec_funcs
->data
,
295 state
->localized_function_names
)));
297 gnm_conf_set_functionselector_recentfunctions (gconf_value_list
);
298 g_slist_free_full (gconf_value_list
, g_free
);
302 cb_unref (GtkTreeModel
*model
, G_GNUC_UNUSED GtkTreePath
*path
,
303 GtkTreeIter
*iter
, G_GNUC_UNUSED gpointer data
)
307 gtk_tree_model_get (model
, iter
,
310 gnm_func_dec_usage (f
);
315 cb_dialog_function_select_destroy (FunctionSelectState
*state
)
317 if (state
->formula_guru_key
&&
318 gnm_dialog_raise_if_exists (state
->wbcg
, state
->formula_guru_key
)) {
319 /* The formula guru is waiting for us.*/
320 state
->formula_guru_key
= NULL
;
321 dialog_formula_guru (state
->wbcg
, NULL
);
324 if (state
->gui
!= NULL
)
325 g_object_unref (state
->gui
);
326 g_slist_free (state
->recent_funcs
);
327 gtk_tree_model_foreach (GTK_TREE_MODEL (state
->model_functions
),
330 g_free (state
->paste
.prefix
);
335 * cb_dialog_function_select_cancel_clicked:
339 * Close (destroy) the dialog
342 cb_dialog_function_select_cancel_clicked (G_GNUC_UNUSED GtkWidget
*button
,
343 FunctionSelectState
*state
)
345 gtk_widget_destroy (state
->dialog
);
350 * cb_dialog_function_select_ok_clicked:
354 * Close (destroy) the dialog
357 cb_dialog_function_select_ok_clicked (G_GNUC_UNUSED GtkWidget
*button
,
358 FunctionSelectState
*state
)
363 GtkTreeSelection
*the_selection
= gtk_tree_view_get_selection (state
->treeview
);
365 if (state
->formula_guru_key
!= NULL
&&
366 gtk_tree_selection_get_selected (the_selection
, &model
, &iter
)) {
367 WBCGtk
*wbcg
= state
->wbcg
;
368 gtk_tree_model_get (model
, &iter
,
371 dialog_function_write_recent_func (state
, func
);
372 state
->formula_guru_key
= NULL
;
373 gtk_widget_destroy (state
->dialog
);
374 dialog_formula_guru (wbcg
, func
);
378 gtk_widget_destroy (state
->dialog
);
383 * cb_dialog_function_select_paste_clicked:
387 * Close (destroy) the dialog
390 cb_dialog_function_select_paste_clicked (G_GNUC_UNUSED GtkWidget
*button
,
391 FunctionSelectState
*state
)
396 GtkTreeSelection
*the_selection
= gtk_tree_view_get_selection (state
->treeview
);
398 if (gtk_tree_selection_get_selected (the_selection
, &model
, &iter
) &&
399 wbcg_edit_start (state
->wbcg
, FALSE
, FALSE
)) {
401 = GTK_EDITABLE (wbcg_get_entry (state
->wbcg
));
403 gtk_tree_model_get (model
, &iter
,
407 dialog_function_write_recent_func (state
, func
);
408 if (state
->paste
.from
>= 0)
409 gtk_editable_select_region
410 (entry
, state
->paste
.from
,
412 gtk_editable_delete_selection (entry
);
413 position
= gtk_editable_get_position (entry
);
414 gtk_editable_insert_text
415 (entry
, func
->name
, -1, &position
);
416 gtk_editable_set_position (entry
, position
);
420 gtk_widget_destroy (state
->dialog
);
425 cb_dialog_function_row_activated (G_GNUC_UNUSED GtkTreeView
*tree_view
,
426 G_GNUC_UNUSED GtkTreePath
*path
,
427 G_GNUC_UNUSED GtkTreeViewColumn
*column
,
428 FunctionSelectState
*state
)
430 switch (state
->mode
) {
432 cb_dialog_function_select_ok_clicked (NULL
, state
);
435 cb_dialog_function_select_paste_clicked (NULL
, state
);
443 dialog_function_select_by_name (gconstpointer a_
, gconstpointer b_
,
446 GnmFunc
const * const a
= (GnmFunc
const * const)a_
;
447 GnmFunc
const * const b
= (GnmFunc
const * const)b_
;
448 FunctionSelectState
const *state
= user
;
449 gboolean localized
= state
->localized_function_names
;
451 return g_utf8_collate (gnm_func_get_name (a
, localized
),
452 gnm_func_get_name (b
, localized
));
455 /*************************************************************************/
456 /* Functions related to the category selector */
457 /*************************************************************************/
462 } dialog_function_select_load_cb_t
;
465 cb_dialog_function_select_load_cb (GtkTreeModel
*model
,
466 G_GNUC_UNUSED GtkTreePath
*path
,
470 dialog_function_select_load_cb_t
*specs
= data
;
475 gtk_tree_model_get (model
, iter
,
480 if (ptr
== NULL
|| ptr
== GINT_TO_POINTER(-1)
481 || ptr
== GINT_TO_POINTER(-2))
483 else if (go_utf8_collate_casefold (specs
->name
, name
) < 0) {
484 specs
->iter
= gtk_tree_iter_copy (iter
);
495 dialog_function_select_load_cb (FunctionSelectState
*state
)
499 GnmFuncGroup
const * cat
;
501 gtk_list_store_clear (state
->model
);
503 gtk_list_store_insert_before (state
->model
, &p_iter
, NULL
);
504 gtk_list_store_set (state
->model
, &p_iter
,
505 CAT_NAME
, _("All Functions"),
507 CAT_SEPARATOR
, FALSE
,
509 gtk_list_store_insert_before (state
->model
, &p_iter
, NULL
);
510 gtk_list_store_set (state
->model
, &p_iter
,
511 CAT_NAME
, _("Recently Used"),
512 CATEGORY
, GINT_TO_POINTER(-1),
513 CAT_SEPARATOR
, FALSE
,
515 gtk_list_store_insert_before (state
->model
, &p_iter
, NULL
);
516 gtk_list_store_set (state
->model
, &p_iter
,
517 CAT_NAME
, _("In Use"),
518 CATEGORY
, GINT_TO_POINTER(-2),
519 CAT_SEPARATOR
, FALSE
,
522 gtk_list_store_insert_before (state
->model
, &p_iter
, NULL
);
523 gtk_list_store_set (state
->model
, &p_iter
,
529 while ((cat
= gnm_func_group_get_nth (i
++)) != NULL
) {
530 dialog_function_select_load_cb_t specs
;
531 specs
.name
= _(cat
->display_name
->str
);
534 gtk_tree_model_foreach (GTK_TREE_MODEL (state
->model
),
535 cb_dialog_function_select_load_cb
,
538 gtk_list_store_insert_before (state
->model
, &p_iter
, specs
.iter
);
539 gtk_list_store_set (state
->model
, &p_iter
,
540 CAT_NAME
, specs
.name
,
542 CAT_SEPARATOR
, FALSE
,
544 if (specs
.iter
!= NULL
)
545 gtk_tree_iter_free (specs
.iter
);
550 dialog_function_select_cat_row_separator (GtkTreeModel
*model
,
552 G_GNUC_UNUSED gpointer data
)
556 gtk_tree_model_get (model
, iter
,
563 /*************************************************************************/
564 /* Functions related to the description field */
565 /*************************************************************************/
568 make_link (GtkTextBuffer
*description
, GtkWidget
*target
, const char *name
,
569 GCallback cb
, gpointer user
)
572 gtk_text_tag_table_lookup
573 (gtk_text_buffer_get_tag_table (description
), name
);
577 char *link_color_text
;
579 gnm_get_link_color (target
, &link_color
);
580 link_color_text
= gdk_rgba_to_string (&link_color
);
582 link
= gtk_text_buffer_create_tag
584 "underline", PANGO_UNDERLINE_SINGLE
,
585 "foreground", link_color_text
,
588 g_free (link_color_text
);
591 g_signal_connect (link
, "event", cb
, user
);
598 cb_link_event (GtkTextTag
*link
, G_GNUC_UNUSED GObject
*trigger
,
599 GdkEvent
*event
, G_GNUC_UNUSED GtkTextIter
*iter
,
600 G_GNUC_UNUSED gpointer user
)
602 switch (event
->type
) {
603 case GDK_BUTTON_PRESS
:
604 case GDK_2BUTTON_PRESS
:
605 case GDK_3BUTTON_PRESS
: {
606 GdkEventButton
*eb
= (GdkEventButton
*)event
;
607 const char *uri
= g_object_get_data (G_OBJECT (link
), "uri");
608 GError
*error
= NULL
;
612 if (event
->type
!= GDK_BUTTON_PRESS
)
615 error
= go_gtk_url_show (uri
, gdk_event_get_screen (event
));
617 g_printerr ("Failed to show %s\n(%s)\n",
620 g_error_free (error
);
627 case GDK_ENTER_NOTIFY
:
628 case GDK_LEAVE_NOTIFY
:
629 /* We aren't getting these. */
639 make_expr_example (Sheet
*sheet
, const char *text
,
640 gboolean localized
, gboolean consider_format
)
642 GnmLocale
*oldlocale
= NULL
;
643 GnmExprTop
const *texpr
;
647 GnmConventions
const *convs
= gnm_conventions_default
;
648 char *tmp_text
= NULL
;
649 GOFormat
const *fmt
= NULL
;
651 if (consider_format
&&
652 g_ascii_strncasecmp (text
, "TEXT(", 5) == 0 &&
653 text
[strlen(text
) - 1] == ')') {
655 tmp_text
= g_strdup (text
+ 5);
656 p
= tmp_text
+ strlen (tmp_text
) - 1;
657 while (p
>= tmp_text
&& p
[0] != '"') p
--;
659 while (p
>= tmp_text
&& p
[0] != '"') p
--;
660 fmt
= go_format_new_from_XL (p
+ 1);
661 while (p
>= tmp_text
&& p
[0] != ',') p
--;
665 eval_pos_init_sheet (&ep
, sheet
);
666 parse_pos_init_evalpos (&pp
, &ep
);
669 oldlocale
= gnm_push_C_locale ();
670 texpr
= gnm_expr_parse_str (text
, &pp
,
671 GNM_EXPR_PARSE_DEFAULT
,
675 gnm_pop_C_locale (oldlocale
);
678 char *etxt
= gnm_expr_top_as_string (texpr
, &pp
, convs
);
679 GnmValue
*val
= gnm_expr_top_eval
680 (texpr
, &ep
, GNM_EXPR_EVAL_PERMIT_NON_SCALAR
);
684 fmt
= gnm_auto_style_format_suggest (texpr
, &ep
);
685 vtxt
= format_value (fmt
, val
, -1, sheet_date_conv (sheet
));
687 gnm_expr_top_unref (texpr
);
690 res
= g_strdup_printf (_("%s evaluates to %s."), etxt
, vtxt
);
695 g_warning ("Failed to parse [%s]", text
);
700 go_format_unref (fmt
);
707 #define ADD_LTEXT(text,len) gtk_text_buffer_insert (description, &ti, (text), (len))
708 #define ADD_TEXT(text) ADD_LTEXT((text),-1)
709 #define ADD_BOLD_TEXT(text,len) gtk_text_buffer_insert_with_tags (description, &ti, (text), (len), bold, NULL)
710 #define ADD_LINK_TEXT(text,len) gtk_text_buffer_insert_with_tags (description, &ti, (text), (len), link, NULL)
711 #define ADD_TEXT_WITH_ARGS(text) { const char *t = text; while (*t) { const char *at = strstr (t, "@{"); \
712 if (at == NULL) { ADD_TEXT(t); break;} ADD_LTEXT(t, at - t); t = at + 2; at = strchr (t,'}'); \
713 if (at != NULL) { ADD_BOLD_TEXT(t, at - t); t = at + 1; } else {ADD_TEXT (t); break;}}}
714 #define FINISH_ARGS if (seen_args && !args_finished) {\
716 gnm_func_count_args (func, &min, &max);\
717 if (max == G_MAXINT) { \
718 ADD_BOLD_TEXT(UNICODE_ELLIPSIS, strlen(UNICODE_ELLIPSIS)); \
720 args_finished = TRUE; \
725 describe_new_style (GtkTextBuffer
*description
,
727 GnmFunc
*func
, Sheet
*sheet
)
729 GnmFuncHelp
const *help
;
732 gtk_text_buffer_create_tag
734 "weight", PANGO_WEIGHT_BOLD
,
736 gboolean seen_args
= FALSE
;
737 gboolean args_finished
= FALSE
;
738 gboolean seen_examples
= FALSE
;
739 gboolean seen_extref
= FALSE
;
741 g_ascii_strcasecmp (gnm_func_get_name (func
, FALSE
), "TEXT") == 0;
744 gtk_text_buffer_get_end_iter (description
, &ti
);
746 help
= gnm_func_get_help (func
, &n
);
748 for (; n
-- > 0; help
++) {
749 switch (help
->type
) {
750 case GNM_FUNC_HELP_NAME
: {
751 const char *text
= gnm_func_gettext (func
, help
->text
);
752 const char *colon
= strchr (text
, ':');
755 ADD_BOLD_TEXT (text
, colon
- text
);
757 ADD_TEXT_WITH_ARGS (colon
+ 1);
761 case GNM_FUNC_HELP_ARG
: {
762 const char *text
= gnm_func_gettext (func
, help
->text
);
763 const char *colon
= strchr (text
, ':');
769 ADD_TEXT (_("Arguments:"));
773 ADD_BOLD_TEXT (text
, colon
- text
);
775 ADD_TEXT_WITH_ARGS (colon
+ 1);
779 case GNM_FUNC_HELP_DESCRIPTION
: {
780 const char *text
= gnm_func_gettext (func
, help
->text
);
783 ADD_TEXT_WITH_ARGS (text
);
787 case GNM_FUNC_HELP_NOTE
: {
788 const char *text
= gnm_func_gettext (func
, help
->text
);
791 ADD_TEXT (_("Note: "));
792 ADD_TEXT_WITH_ARGS (text
);
796 case GNM_FUNC_HELP_EXAMPLES
: {
797 const char *text
= gnm_func_gettext (func
, help
->text
);
798 gboolean was_translated
= (text
!= help
->text
);
801 if (!seen_examples
) {
802 seen_examples
= TRUE
;
804 ADD_TEXT (_("Examples:"));
808 if (text
[0] == '=') {
810 make_expr_example (sheet
, text
+ 1,
816 ADD_TEXT_WITH_ARGS (text
);
821 case GNM_FUNC_HELP_SEEALSO
: {
822 const char *text
= help
->text
; /* Not translated */
823 const char *pre
= _("See also: ");
825 make_link (description
, target
, "LINK",
832 const char *end
= strchr (text
, ',');
833 if (!end
) end
= text
+ strlen (text
);
836 ADD_LINK_TEXT (text
, end
- text
);
838 text
= *end
? end
+ 1 : end
;
845 case GNM_FUNC_HELP_EXTREF
: {
852 * We put in just one link and let the web page handle
853 * the rest. In particular, we do not even look at
854 * what the help->text is here.
859 uri
= g_strdup_printf ("http://www.gnumeric.org/func-doc.shtml?%s", func
->name
);
861 tagname
= g_strdup_printf ("EXTLINK-%s", func
->name
);
863 (description
, target
, tagname
,
864 G_CALLBACK (cb_link_event
), NULL
);
866 g_object_set_data_full (G_OBJECT (link
),
870 ADD_TEXT (_("Further information: "));
872 text
= _("online descriptions");
873 ADD_LINK_TEXT (text
, strlen (text
));
880 case GNM_FUNC_HELP_EXCEL
: {
881 const char *text
= gnm_func_gettext (func
, help
->text
);
884 ADD_TEXT (_("Microsoft Excel: "));
885 ADD_TEXT_WITH_ARGS (text
);
889 case GNM_FUNC_HELP_ODF
: {
890 const char *text
= gnm_func_gettext (func
, help
->text
);
893 ADD_TEXT (_("ODF (OpenFormula): "));
894 ADD_TEXT_WITH_ARGS (text
);
905 #undef ADD_TEXT_WITH_ARGS
914 FunctionSelectState
*state
;
916 } dialog_function_select_find_func_t
;
920 dialog_function_select_search_func (GtkTreeModel
*model
,
926 dialog_function_select_find_func_t
*data
= dt
;
928 gtk_tree_model_get (model
, iter
,
931 if (fd
== data
->fd
) {
932 data
->path
= gtk_tree_path_copy (path
);
939 dialog_function_select_find_func (FunctionSelectState
*state
, char* name
)
946 fd
= gnm_func_lookup (name
, state
->wb
);
948 dialog_function_select_find_func_t data
= {fd
, state
, NULL
};
949 GtkTreeSelection
*selection
= gtk_tree_view_get_selection
953 gtk_tree_model_foreach (GTK_TREE_MODEL (state
->model_functions
),
954 dialog_function_select_search_func
,
956 if (data
.path
!= NULL
) {
958 if (gtk_tree_model_get_iter
959 (GTK_TREE_MODEL (state
->model_functions
), &iter
,
961 gtk_list_store_set (state
->model_functions
,
963 FUNCTION_VISIBLE
, TRUE
,
966 path
= gtk_tree_model_filter_convert_child_path_to_path
967 (GTK_TREE_MODEL_FILTER (state
->model_filter
),
970 gtk_tree_selection_select_path (selection
,
972 gtk_tree_view_scroll_to_cell (state
->treeview
, path
,
973 NULL
, FALSE
, 0., 0.);
974 gtk_tree_path_free (path
);
975 gtk_tree_path_free (data
.path
);
977 g_warning ("Function %s was not found in its category", name
);
980 g_warning ("Function %s was not found", name
);
984 FunctionSelectState
*state
;
986 } cb_dialog_function_select_idle_handler_t
;
989 cb_dialog_function_select_idle_handler (gpointer dt
)
991 cb_dialog_function_select_idle_handler_t
*data
= dt
;
993 dialog_function_select_find_func (data
->state
, data
->name
);
1002 cb_description_clicked (GtkTextBuffer
*textbuffer
,
1003 GtkTextIter
*location
,
1005 FunctionSelectState
*state
)
1007 const char * mark_name
;
1011 cb_dialog_function_select_idle_handler_t
*data
;
1013 if ((mark
== NULL
) || ((mark_name
= gtk_text_mark_get_name (mark
)) == NULL
)
1014 || (strcmp(mark_name
, "selection_bound") != 0))
1017 link
= gtk_text_tag_table_lookup
1018 (gtk_text_buffer_get_tag_table (textbuffer
), "LINK");
1020 if ((link
== NULL
) || !gtk_text_iter_has_tag (location
, link
))
1023 start
= gtk_text_iter_copy (location
);
1024 end
= gtk_text_iter_copy (location
);
1026 if (!gtk_text_iter_begins_tag (start
, link
))
1027 gtk_text_iter_backward_to_tag_toggle (start
, link
);
1028 if (!gtk_text_iter_ends_tag (end
, link
))
1029 gtk_text_iter_forward_to_tag_toggle (end
, link
);
1031 data
= g_new(cb_dialog_function_select_idle_handler_t
, 1);
1033 data
->name
= gtk_text_buffer_get_text (textbuffer
, start
, end
, FALSE
);
1034 gtk_text_iter_free (start
);
1035 gtk_text_iter_free (end
);
1036 data
->state
= state
;
1038 g_idle_add_full (G_PRIORITY_HIGH_IDLE
, cb_dialog_function_select_idle_handler
,
1043 cb_dialog_function_select_fun_selection_changed (GtkTreeSelection
*selection
,
1044 FunctionSelectState
*state
)
1047 GtkTreeModel
*model
;
1049 GtkTextBuffer
*description
;
1051 gboolean active
= FALSE
;
1053 description
= gtk_text_view_get_buffer (state
->description_view
);
1055 mark
= gtk_text_buffer_get_mark (description
, "start-mark");
1056 gtk_text_view_scroll_to_mark (state
->description_view
, mark
,
1057 0.1, TRUE
, 0.0, 0.0);
1058 gtk_text_buffer_set_text (description
, "", 0);
1060 if (gtk_tree_selection_get_selected (selection
, &model
, &iter
)) {
1061 gtk_tree_model_get (model
, &iter
,
1065 gnm_func_load_if_stub (func
);
1067 if (gnm_func_get_help (func
, NULL
) == NULL
)
1068 gtk_text_buffer_set_text (description
, "?", -1);
1070 describe_new_style (description
,
1071 GTK_WIDGET (state
->description_view
),
1072 func
, state
->sheet
);
1075 gtk_widget_set_sensitive (state
->ok_button
, active
);
1076 gtk_widget_set_sensitive (state
->paste_button
, active
);
1080 /**********************************************************************/
1081 /* Setup Functions */
1082 /**********************************************************************/
1084 static const gchar
*
1085 dialog_function_select_peek_description (GnmFunc
*func
)
1087 GnmFuncHelp
const *help
;
1090 gnm_func_load_if_stub (func
);
1092 help
= gnm_func_get_help (func
, &n
);
1096 for (; n
-- > 0; 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
= gnm_func_gettext (func
, help
->text
);
1110 const char *colon
= strchr (text
, ':');
1111 return (colon
? colon
+ 1 : text
);
1120 dialog_function_select_get_description (GnmFunc
*func
, PangoAttrList
**pal
)
1122 PangoAttribute
*attr
;
1123 char const *desc
= dialog_function_select_peek_description (func
);
1125 GString
* gstr
= g_string_new (NULL
);
1127 *pal
= pango_attr_list_new ();
1130 while (*desc
!= '\0') {
1131 here
= strstr (desc
, "@{");
1133 g_string_append (gstr
, desc
);
1136 g_string_append_len (gstr
, desc
, here
- desc
);
1137 attr
= pango_attr_weight_new (PANGO_WEIGHT_BOLD
);
1138 attr
->start_index
= gstr
->len
;
1140 here
= strchr (desc
,'}');
1142 g_string_append (gstr
, desc
);
1143 pango_attr_list_insert (*pal
, attr
);
1146 g_string_append_len (gstr
, desc
, here
- desc
);
1147 attr
->end_index
= gstr
->len
;
1148 pango_attr_list_insert (*pal
, attr
);
1153 return g_string_free (gstr
, FALSE
);
1159 dialog_function_select_load_tree (FunctionSelectState
*state
)
1162 GnmFuncGroup
const * cat
;
1163 GSList
*funcs
= NULL
, *ptr
;
1169 gtk_list_store_clear (state
->model_functions
);
1171 while ((cat
= gnm_func_group_get_nth (i
++)) != NULL
)
1172 funcs
= g_slist_concat (funcs
,
1173 g_slist_copy (cat
->functions
));
1175 funcs
= g_slist_sort_with_data (funcs
,
1176 dialog_function_select_by_name
,
1179 for (ptr
= funcs
; ptr
; ptr
= ptr
->next
) {
1181 if (!(gnm_func_get_flags (func
) &
1182 (GNM_FUNC_INTERNAL
| GNM_FUNC_IS_PLACEHOLDER
))) {
1183 gboolean in_use
= gnm_func_get_in_use (func
);
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
, gnm_func_get_function_group (func
),
1194 FUNCTION_VISIBLE
, TRUE
,
1195 FUNCTION_RECENT
, FALSE
,
1196 FUNCTION_USED
, in_use
,
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
,