1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * dialog-function-select.c: Implements the function selector
6 * Michael Meeks <michael@ximian.com>
7 * Andreas J. Guelzow <aguelzow@pyrshep.ca>
9 * Copyright (C) 2003-2010 Andreas J. Guelzow <aguelzow@pyrshep.ca>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, see <https://www.gnu.org/licenses/>.
25 #include <gnumeric-config.h>
26 #include <glib/gi18n-lib.h>
36 #include <application.h>
41 #include <gnumeric-conf.h>
42 #include <gnm-format.h>
43 #include <auto-format.h>
45 #include <gsf/gsf-impl-utils.h>
49 #define F2(func,s) dgettext ((func)->tdomain->str, (s))
51 #define FUNCTION_SELECT_KEY "function-selector-dialog"
52 #define FUNCTION_SELECT_HELP_KEY "function-selector-dialog-help-mode"
53 #define FUNCTION_SELECT_PASTE_KEY "function-selector-dialog-paste-mode"
54 #define FUNCTION_SELECT_DIALOG_KEY "function-selector-dialog"
56 #define UNICODE_ELLIPSIS "\xe2\x80\xa6"
69 gboolean localized_function_names
;
74 GtkWidget
*paste_button
;
77 GtkListStore
*model_functions
;
78 GtkTreeModel
*model_filter
;
79 GtkTreeView
*treeview
;
80 GtkTextView
*description_view
;
81 GtkWidget
*search_entry
;
92 char const *formula_guru_key
;
93 } FunctionSelectState
;
113 /*************************************************************************/
114 /* Search Functions */
115 /*************************************************************************/
119 gboolean recent_only
;
121 GnmFuncGroup
const * cat
;
125 cb_dialog_function_select_search_all (GtkTreeModel
*model
,
126 G_GNUC_UNUSED GtkTreePath
*path
,
127 GtkTreeIter
*iter
, gpointer data
)
129 search_t
*specs
= data
;
132 gboolean visible
, was_visible
, recent
, used
;
133 GnmFuncGroup
const * cat
;
135 gtk_tree_model_get (model
, iter
,
137 FUNCTION_DESC
, &desc
,
138 FUNCTION_VISIBLE
, &was_visible
,
139 FUNCTION_RECENT
, &recent
,
140 FUNCTION_USED
, &used
,
144 if (specs
->recent_only
&& !recent
)
146 else if (specs
->used_only
&& !used
)
148 else if (specs
->cat
!= NULL
&& specs
->cat
!= cat
)
150 else if (specs
->text
== NULL
)
153 gchar
*name_n
, *name_cf
, *text_n
, *text_cf
;
155 text_n
= g_utf8_normalize (specs
->text
, -1, G_NORMALIZE_ALL
);
156 text_cf
= g_utf8_casefold(text_n
, -1);
158 name_n
= g_utf8_normalize (name
, -1, G_NORMALIZE_ALL
);
159 name_cf
= g_utf8_casefold(name_n
, -1);
160 visible
= (NULL
!= g_strstr_len (name_cf
, -1, text_cf
));
165 name_n
= g_utf8_normalize (desc
, -1, G_NORMALIZE_ALL
);
166 name_cf
= g_utf8_casefold(name_n
, -1);
167 visible
= (NULL
!= g_strstr_len (name_cf
, -1, text_cf
));
179 if (visible
!= was_visible
)
180 gtk_list_store_set (GTK_LIST_STORE (model
), iter
,
181 FUNCTION_VISIBLE
, visible
,
187 dialog_function_select_search (GtkEntry
*entry
, gpointer data
)
189 search_t specs
= {NULL
, FALSE
, FALSE
, NULL
};
190 FunctionSelectState
*state
= data
;
193 if (0 != gtk_entry_get_text_length (entry
))
194 specs
.text
= gtk_entry_get_text (entry
);
196 if (gtk_combo_box_get_active_iter (state
->cb
, &iter
)) {
197 gtk_tree_model_get (GTK_TREE_MODEL (state
->model
), &iter
,
198 CATEGORY
, &specs
.cat
,
201 = (specs
.cat
!= NULL
&&
202 specs
.cat
== GINT_TO_POINTER(-1));
204 = (specs
.cat
!= NULL
&&
205 specs
.cat
== GINT_TO_POINTER(-2));
206 if (specs
.recent_only
|| specs
.used_only
)
210 gtk_tree_model_foreach (GTK_TREE_MODEL (state
->model_functions
),
211 cb_dialog_function_select_search_all
,
216 dialog_function_select_erase_search_entry (GtkEntry
*entry
,
217 G_GNUC_UNUSED GtkEntryIconPosition icon_pos
,
218 G_GNUC_UNUSED GdkEvent
*event
,
221 gtk_entry_set_text (entry
, "");
222 dialog_function_select_search (entry
, data
);
226 dialog_function_select_cat_changed (G_GNUC_UNUSED GtkComboBox
*widget
,
229 FunctionSelectState
*state
= data
;
231 dialog_function_select_search (GTK_ENTRY (state
->search_entry
),
235 /*************************************************************************/
238 cb_dialog_function_load_recent_funcs(GtkTreeModel
*model
,
239 G_GNUC_UNUSED GtkTreePath
*path
,
245 gtk_tree_model_get (model
, iter
,
249 gtk_list_store_set (GTK_LIST_STORE (model
), iter
,
250 FUNCTION_RECENT
, TRUE
,
258 dialog_function_load_recent_funcs (FunctionSelectState
*state
)
260 GSList
const *recent_funcs
;
262 for (recent_funcs
= gnm_conf_get_functionselector_recentfunctions ();
264 recent_funcs
= recent_funcs
->next
) {
265 char const *name
= recent_funcs
->data
;
271 fd
= gnm_func_lookup (name
, NULL
);
273 state
->recent_funcs
= g_slist_prepend (state
->recent_funcs
, fd
);
274 gtk_tree_model_foreach (GTK_TREE_MODEL (state
->model_functions
),
275 cb_dialog_function_load_recent_funcs
,
282 dialog_function_write_recent_func (FunctionSelectState
*state
, GnmFunc
const *fd
)
285 GSList
*gconf_value_list
= NULL
;
286 guint ulimit
= gnm_conf_get_functionselector_num_of_recent ();
288 state
->recent_funcs
= g_slist_remove (state
->recent_funcs
, (gpointer
) fd
);
289 state
->recent_funcs
= g_slist_prepend (state
->recent_funcs
, (gpointer
) fd
);
291 while (g_slist_length (state
->recent_funcs
) > ulimit
)
292 state
->recent_funcs
= g_slist_remove (state
->recent_funcs
,
293 g_slist_last (state
->recent_funcs
)->data
);
295 for (rec_funcs
= state
->recent_funcs
; rec_funcs
; rec_funcs
= rec_funcs
->next
) {
296 gconf_value_list
= g_slist_prepend
298 g_strdup (gnm_func_get_name (rec_funcs
->data
,
299 state
->localized_function_names
)));
301 gnm_conf_set_functionselector_recentfunctions (gconf_value_list
);
302 g_slist_free_full (gconf_value_list
, g_free
);
306 cb_unref (GtkTreeModel
*model
, G_GNUC_UNUSED GtkTreePath
*path
,
307 GtkTreeIter
*iter
, G_GNUC_UNUSED gpointer data
)
311 gtk_tree_model_get (model
, iter
,
319 cb_dialog_function_select_destroy (FunctionSelectState
*state
)
321 if (state
->formula_guru_key
&&
322 gnm_dialog_raise_if_exists (state
->wbcg
, state
->formula_guru_key
)) {
323 /* The formula guru is waiting for us.*/
324 state
->formula_guru_key
= NULL
;
325 dialog_formula_guru (state
->wbcg
, NULL
);
328 if (state
->gui
!= NULL
)
329 g_object_unref (state
->gui
);
330 g_slist_free (state
->recent_funcs
);
331 gtk_tree_model_foreach (GTK_TREE_MODEL (state
->model_functions
),
334 g_free (state
->paste
.prefix
);
339 * cb_dialog_function_select_cancel_clicked:
343 * Close (destroy) the dialog
346 cb_dialog_function_select_cancel_clicked (G_GNUC_UNUSED GtkWidget
*button
,
347 FunctionSelectState
*state
)
349 gtk_widget_destroy (state
->dialog
);
354 * cb_dialog_function_select_ok_clicked:
358 * Close (destroy) the dialog
361 cb_dialog_function_select_ok_clicked (G_GNUC_UNUSED GtkWidget
*button
,
362 FunctionSelectState
*state
)
367 GtkTreeSelection
*the_selection
= gtk_tree_view_get_selection (state
->treeview
);
369 if (state
->formula_guru_key
!= NULL
&&
370 gtk_tree_selection_get_selected (the_selection
, &model
, &iter
)) {
371 WBCGtk
*wbcg
= state
->wbcg
;
372 gtk_tree_model_get (model
, &iter
,
375 dialog_function_write_recent_func (state
, func
);
376 state
->formula_guru_key
= NULL
;
377 gtk_widget_destroy (state
->dialog
);
378 dialog_formula_guru (wbcg
, func
);
382 gtk_widget_destroy (state
->dialog
);
387 * cb_dialog_function_select_paste_clicked:
391 * Close (destroy) the dialog
394 cb_dialog_function_select_paste_clicked (G_GNUC_UNUSED GtkWidget
*button
,
395 FunctionSelectState
*state
)
400 GtkTreeSelection
*the_selection
= gtk_tree_view_get_selection (state
->treeview
);
402 if (gtk_tree_selection_get_selected (the_selection
, &model
, &iter
) &&
403 wbcg_edit_start (state
->wbcg
, FALSE
, FALSE
)) {
405 = GTK_EDITABLE (wbcg_get_entry (state
->wbcg
));
407 gtk_tree_model_get (model
, &iter
,
411 dialog_function_write_recent_func (state
, func
);
412 if (state
->paste
.from
>= 0)
413 gtk_editable_select_region
414 (entry
, state
->paste
.from
,
416 gtk_editable_delete_selection (entry
);
417 position
= gtk_editable_get_position (entry
);
418 gtk_editable_insert_text
419 (entry
, func
->name
, -1, &position
);
420 gtk_editable_set_position (entry
, position
);
424 gtk_widget_destroy (state
->dialog
);
429 cb_dialog_function_row_activated (G_GNUC_UNUSED GtkTreeView
*tree_view
,
430 G_GNUC_UNUSED GtkTreePath
*path
,
431 G_GNUC_UNUSED GtkTreeViewColumn
*column
,
432 FunctionSelectState
*state
)
434 switch (state
->mode
) {
436 cb_dialog_function_select_ok_clicked (NULL
, state
);
439 cb_dialog_function_select_paste_clicked (NULL
, state
);
447 dialog_function_select_by_name (gconstpointer a_
, gconstpointer b_
,
450 GnmFunc
const * const a
= (GnmFunc
const * const)a_
;
451 GnmFunc
const * const b
= (GnmFunc
const * const)b_
;
452 FunctionSelectState
const *state
= user
;
453 gboolean localized
= state
->localized_function_names
;
455 return g_utf8_collate (gnm_func_get_name (a
, localized
),
456 gnm_func_get_name (b
, localized
));
459 /*************************************************************************/
460 /* Functions related to the category selector */
461 /*************************************************************************/
466 } dialog_function_select_load_cb_t
;
469 cb_dialog_function_select_load_cb (GtkTreeModel
*model
,
470 G_GNUC_UNUSED GtkTreePath
*path
,
474 dialog_function_select_load_cb_t
*specs
= data
;
479 gtk_tree_model_get (model
, iter
,
484 if (ptr
== NULL
|| ptr
== GINT_TO_POINTER(-1)
485 || ptr
== GINT_TO_POINTER(-2))
487 else if (go_utf8_collate_casefold (specs
->name
, name
) < 0) {
488 specs
->iter
= gtk_tree_iter_copy (iter
);
499 dialog_function_select_load_cb (FunctionSelectState
*state
)
503 GnmFuncGroup
const * cat
;
505 gtk_list_store_clear (state
->model
);
507 gtk_list_store_insert_before (state
->model
, &p_iter
, NULL
);
508 gtk_list_store_set (state
->model
, &p_iter
,
509 CAT_NAME
, _("All Functions"),
511 CAT_SEPARATOR
, FALSE
,
513 gtk_list_store_insert_before (state
->model
, &p_iter
, NULL
);
514 gtk_list_store_set (state
->model
, &p_iter
,
515 CAT_NAME
, _("Recently Used"),
516 CATEGORY
, GINT_TO_POINTER(-1),
517 CAT_SEPARATOR
, FALSE
,
519 gtk_list_store_insert_before (state
->model
, &p_iter
, NULL
);
520 gtk_list_store_set (state
->model
, &p_iter
,
521 CAT_NAME
, _("In Use"),
522 CATEGORY
, GINT_TO_POINTER(-2),
523 CAT_SEPARATOR
, FALSE
,
526 gtk_list_store_insert_before (state
->model
, &p_iter
, NULL
);
527 gtk_list_store_set (state
->model
, &p_iter
,
533 while ((cat
= gnm_func_group_get_nth (i
++)) != NULL
) {
534 dialog_function_select_load_cb_t specs
;
535 specs
.name
= _(cat
->display_name
->str
);
538 gtk_tree_model_foreach (GTK_TREE_MODEL (state
->model
),
539 cb_dialog_function_select_load_cb
,
542 gtk_list_store_insert_before (state
->model
, &p_iter
, specs
.iter
);
543 gtk_list_store_set (state
->model
, &p_iter
,
544 CAT_NAME
, specs
.name
,
546 CAT_SEPARATOR
, FALSE
,
548 if (specs
.iter
!= NULL
)
549 gtk_tree_iter_free (specs
.iter
);
554 dialog_function_select_cat_row_separator (GtkTreeModel
*model
,
556 G_GNUC_UNUSED gpointer data
)
560 gtk_tree_model_get (model
, iter
,
567 /*************************************************************************/
568 /* Functions related to the description field */
569 /*************************************************************************/
572 make_link (GtkTextBuffer
*description
, GtkWidget
*target
, const char *name
,
573 GCallback cb
, gpointer user
)
576 gtk_text_tag_table_lookup
577 (gtk_text_buffer_get_tag_table (description
), name
);
581 char *link_color_text
;
583 gnm_get_link_color (target
, &link_color
);
584 link_color_text
= gdk_rgba_to_string (&link_color
);
586 link
= gtk_text_buffer_create_tag
588 "underline", PANGO_UNDERLINE_SINGLE
,
589 "foreground", link_color_text
,
592 g_free (link_color_text
);
595 g_signal_connect (link
, "event", cb
, user
);
602 cb_link_event (GtkTextTag
*link
, G_GNUC_UNUSED GObject
*trigger
,
603 GdkEvent
*event
, G_GNUC_UNUSED GtkTextIter
*iter
,
604 G_GNUC_UNUSED gpointer user
)
606 switch (event
->type
) {
607 case GDK_BUTTON_PRESS
:
608 case GDK_2BUTTON_PRESS
:
609 case GDK_3BUTTON_PRESS
: {
610 GdkEventButton
*eb
= (GdkEventButton
*)event
;
611 const char *uri
= g_object_get_data (G_OBJECT (link
), "uri");
612 GError
*error
= NULL
;
616 if (event
->type
!= GDK_BUTTON_PRESS
)
619 error
= go_gtk_url_show (uri
, gdk_event_get_screen (event
));
621 g_printerr ("Failed to show %s\n(%s)\n",
624 g_error_free (error
);
631 case GDK_ENTER_NOTIFY
:
632 case GDK_LEAVE_NOTIFY
:
633 /* We aren't getting these. */
643 make_expr_example (Sheet
*sheet
, const char *text
,
644 gboolean localized
, gboolean consider_format
)
646 GnmLocale
*oldlocale
= NULL
;
647 GnmExprTop
const *texpr
;
651 GnmConventions
const *convs
= gnm_conventions_default
;
652 char *tmp_text
= NULL
;
653 GOFormat
const *fmt
= NULL
;
655 if (consider_format
&&
656 g_ascii_strncasecmp (text
, "TEXT(", 5) == 0 &&
657 text
[strlen(text
) - 1] == ')') {
659 tmp_text
= g_strdup (text
+ 5);
660 p
= tmp_text
+ strlen (tmp_text
) - 1;
661 while (p
>= tmp_text
&& p
[0] != '"') p
--;
663 while (p
>= tmp_text
&& p
[0] != '"') p
--;
664 fmt
= go_format_new_from_XL (p
+ 1);
665 while (p
>= tmp_text
&& p
[0] != ',') p
--;
669 eval_pos_init_sheet (&ep
, sheet
);
670 parse_pos_init_evalpos (&pp
, &ep
);
673 oldlocale
= gnm_push_C_locale ();
674 texpr
= gnm_expr_parse_str (text
, &pp
,
675 GNM_EXPR_PARSE_DEFAULT
,
679 gnm_pop_C_locale (oldlocale
);
682 char *etxt
= gnm_expr_top_as_string (texpr
, &pp
, convs
);
683 GnmValue
*val
= gnm_expr_top_eval
684 (texpr
, &ep
, GNM_EXPR_EVAL_PERMIT_NON_SCALAR
);
688 fmt
= gnm_auto_style_format_suggest (texpr
, &ep
);
689 vtxt
= format_value (fmt
, val
, -1, sheet_date_conv (sheet
));
691 gnm_expr_top_unref (texpr
);
694 res
= g_strdup_printf (_("%s evaluates to %s."), etxt
, vtxt
);
699 g_warning ("Failed to parse [%s]", text
);
704 go_format_unref (fmt
);
711 #define ADD_LTEXT(text,len) gtk_text_buffer_insert (description, &ti, (text), (len))
712 #define ADD_TEXT(text) ADD_LTEXT((text),-1)
713 #define ADD_BOLD_TEXT(text,len) gtk_text_buffer_insert_with_tags (description, &ti, (text), (len), bold, NULL)
714 #define ADD_LINK_TEXT(text,len) gtk_text_buffer_insert_with_tags (description, &ti, (text), (len), link, NULL)
715 #define ADD_TEXT_WITH_ARGS(text) { const char *t = text; while (*t) { const char *at = strstr (t, "@{"); \
716 if (at == NULL) { ADD_TEXT(t); break;} ADD_LTEXT(t, at - t); t = at + 2; at = strchr (t,'}'); \
717 if (at != NULL) { ADD_BOLD_TEXT(t, at - t); t = at + 1; } else {ADD_TEXT (t); break;}}}
718 #define FINISH_ARGS if (seen_args && !args_finished) {\
720 gnm_func_count_args (func, &min, &max);\
721 if (max == G_MAXINT) { \
722 ADD_BOLD_TEXT(UNICODE_ELLIPSIS, strlen(UNICODE_ELLIPSIS)); \
724 args_finished = TRUE; \
729 describe_new_style (GtkTextBuffer
*description
,
731 GnmFunc
const *func
, Sheet
*sheet
)
733 GnmFuncHelp
const *help
;
736 gtk_text_buffer_create_tag
738 "weight", PANGO_WEIGHT_BOLD
,
740 gboolean seen_args
= FALSE
;
741 gboolean args_finished
= FALSE
;
742 gboolean seen_examples
= FALSE
;
743 gboolean seen_extref
= FALSE
;
745 g_ascii_strcasecmp (gnm_func_get_name (func
, FALSE
), "TEXT") == 0;
747 gtk_text_buffer_get_end_iter (description
, &ti
);
749 for (help
= func
->help
; 1; help
++) {
750 switch (help
->type
) {
751 case GNM_FUNC_HELP_NAME
: {
752 const char *text
= F2 (func
, help
->text
);
753 const char *colon
= strchr (text
, ':');
756 ADD_BOLD_TEXT (text
, colon
- text
);
758 ADD_TEXT_WITH_ARGS (colon
+ 1);
762 case GNM_FUNC_HELP_ARG
: {
763 const char *text
= F2 (func
, help
->text
);
764 const char *colon
= strchr (text
, ':');
770 ADD_TEXT (_("Arguments:"));
774 ADD_BOLD_TEXT (text
, colon
- text
);
776 ADD_TEXT_WITH_ARGS (colon
+ 1);
780 case GNM_FUNC_HELP_DESCRIPTION
: {
781 const char *text
= F2 (func
, help
->text
);
784 ADD_TEXT_WITH_ARGS (text
);
788 case GNM_FUNC_HELP_NOTE
: {
789 const char *text
= F2 (func
, help
->text
);
792 ADD_TEXT (_("Note: "));
793 ADD_TEXT_WITH_ARGS (text
);
797 case GNM_FUNC_HELP_EXAMPLES
: {
798 const char *text
= F2 (func
, help
->text
);
799 gboolean was_translated
= (text
!= help
->text
);
802 if (!seen_examples
) {
803 seen_examples
= TRUE
;
805 ADD_TEXT (_("Examples:"));
809 if (text
[0] == '=') {
811 make_expr_example (sheet
, text
+ 1,
817 ADD_TEXT_WITH_ARGS (text
);
822 case GNM_FUNC_HELP_SEEALSO
: {
823 const char *text
= help
->text
; /* Not translated */
824 const char *pre
= _("See also: ");
826 make_link (description
, target
, "LINK",
833 const char *end
= strchr (text
, ',');
834 if (!end
) end
= text
+ strlen (text
);
837 ADD_LINK_TEXT (text
, end
- text
);
839 text
= *end
? end
+ 1 : end
;
846 case GNM_FUNC_HELP_END
:
849 case GNM_FUNC_HELP_EXTREF
: {
856 * We put in just one link and let the web page handle
857 * the rest. In particular, we do not even look at
858 * what the help->text is here.
863 uri
= g_strdup_printf ("http://www.gnumeric.org/func-doc.shtml?%s", func
->name
);
865 tagname
= g_strdup_printf ("EXTLINK-%s", func
->name
);
867 (description
, target
, tagname
,
868 G_CALLBACK (cb_link_event
), NULL
);
870 g_object_set_data_full (G_OBJECT (link
),
874 ADD_TEXT (_("Further information: "));
876 text
= _("online descriptions");
877 ADD_LINK_TEXT (text
, strlen (text
));
884 case GNM_FUNC_HELP_EXCEL
: {
885 const char *text
= F2 (func
, help
->text
);
888 ADD_TEXT (_("Microsoft Excel: "));
889 ADD_TEXT_WITH_ARGS (text
);
893 case GNM_FUNC_HELP_ODF
: {
894 const char *text
= F2 (func
, help
->text
);
897 ADD_TEXT (_("ODF (OpenFormula): "));
898 ADD_TEXT_WITH_ARGS (text
);
908 #undef ADD_TEXT_WITH_ARGS
917 FunctionSelectState
*state
;
919 } dialog_function_select_find_func_t
;
923 dialog_function_select_search_func (GtkTreeModel
*model
,
929 dialog_function_select_find_func_t
*data
= dt
;
931 gtk_tree_model_get (model
, iter
,
934 if (fd
== data
->fd
) {
935 data
->path
= gtk_tree_path_copy (path
);
942 dialog_function_select_find_func (FunctionSelectState
*state
, char* name
)
949 fd
= gnm_func_lookup (name
, state
->wb
);
951 dialog_function_select_find_func_t data
= {fd
, state
, NULL
};
952 GtkTreeSelection
*selection
= gtk_tree_view_get_selection
956 gtk_tree_model_foreach (GTK_TREE_MODEL (state
->model_functions
),
957 dialog_function_select_search_func
,
959 if (data
.path
!= NULL
) {
961 if (gtk_tree_model_get_iter
962 (GTK_TREE_MODEL (state
->model_functions
), &iter
,
964 gtk_list_store_set (state
->model_functions
,
966 FUNCTION_VISIBLE
, TRUE
,
969 path
= gtk_tree_model_filter_convert_child_path_to_path
970 (GTK_TREE_MODEL_FILTER (state
->model_filter
),
973 gtk_tree_selection_select_path (selection
,
975 gtk_tree_view_scroll_to_cell (state
->treeview
, path
,
976 NULL
, FALSE
, 0., 0.);
977 gtk_tree_path_free (path
);
978 gtk_tree_path_free (data
.path
);
980 g_warning ("Function %s was not found in its category", name
);
983 g_warning ("Function %s was not found", name
);
987 FunctionSelectState
*state
;
989 } cb_dialog_function_select_idle_handler_t
;
992 cb_dialog_function_select_idle_handler (gpointer dt
)
994 cb_dialog_function_select_idle_handler_t
*data
= dt
;
996 dialog_function_select_find_func (data
->state
, data
->name
);
1005 cb_description_clicked (GtkTextBuffer
*textbuffer
,
1006 GtkTextIter
*location
,
1008 FunctionSelectState
*state
)
1010 const char * mark_name
;
1014 cb_dialog_function_select_idle_handler_t
*data
;
1016 if ((mark
== NULL
) || ((mark_name
= gtk_text_mark_get_name (mark
)) == NULL
)
1017 || (strcmp(mark_name
, "selection_bound") != 0))
1020 link
= gtk_text_tag_table_lookup
1021 (gtk_text_buffer_get_tag_table (textbuffer
), "LINK");
1023 if ((link
== NULL
) || !gtk_text_iter_has_tag (location
, link
))
1026 start
= gtk_text_iter_copy (location
);
1027 end
= gtk_text_iter_copy (location
);
1029 if (!gtk_text_iter_begins_tag (start
, link
))
1030 gtk_text_iter_backward_to_tag_toggle (start
, link
);
1031 if (!gtk_text_iter_ends_tag (end
, link
))
1032 gtk_text_iter_forward_to_tag_toggle (end
, link
);
1034 data
= g_new(cb_dialog_function_select_idle_handler_t
, 1);
1036 data
->name
= gtk_text_buffer_get_text (textbuffer
, start
, end
, FALSE
);
1037 gtk_text_iter_free (start
);
1038 gtk_text_iter_free (end
);
1039 data
->state
= state
;
1041 g_idle_add_full (G_PRIORITY_HIGH_IDLE
, cb_dialog_function_select_idle_handler
,
1046 cb_dialog_function_select_fun_selection_changed (GtkTreeSelection
*selection
,
1047 FunctionSelectState
*state
)
1050 GtkTreeModel
*model
;
1051 GnmFunc
const *func
;
1052 GtkTextBuffer
*description
;
1054 gboolean active
= FALSE
;
1056 description
= gtk_text_view_get_buffer (state
->description_view
);
1058 mark
= gtk_text_buffer_get_mark (description
, "start-mark");
1059 gtk_text_view_scroll_to_mark (state
->description_view
, mark
,
1060 0.1, TRUE
, 0.0, 0.0);
1061 gtk_text_buffer_set_text (description
, "", 0);
1063 if (gtk_tree_selection_get_selected (selection
, &model
, &iter
)) {
1064 gtk_tree_model_get (model
, &iter
,
1068 gnm_func_load_if_stub ((GnmFunc
*)func
);
1070 if (func
->help
== NULL
)
1071 gtk_text_buffer_set_text (description
, "?", -1);
1073 describe_new_style (description
,
1074 GTK_WIDGET (state
->description_view
),
1075 func
, state
->sheet
);
1078 gtk_widget_set_sensitive (state
->ok_button
, active
);
1079 gtk_widget_set_sensitive (state
->paste_button
, active
);
1083 /**********************************************************************/
1084 /* Setup Functions */
1085 /**********************************************************************/
1087 static const gchar
*
1088 dialog_function_select_peek_description (GnmFunc
*func
)
1090 GnmFuncHelp
const *help
;
1092 gnm_func_load_if_stub (func
);
1098 for (; TRUE
; help
++) {
1099 switch (help
->type
) {
1100 case GNM_FUNC_HELP_ARG
:
1101 case GNM_FUNC_HELP_NOTE
:
1102 case GNM_FUNC_HELP_EXAMPLES
:
1103 case GNM_FUNC_HELP_SEEALSO
:
1104 case GNM_FUNC_HELP_EXTREF
:
1105 case GNM_FUNC_HELP_EXCEL
:
1106 case GNM_FUNC_HELP_ODF
:
1107 case GNM_FUNC_HELP_DESCRIPTION
:
1110 case GNM_FUNC_HELP_NAME
: {
1111 const char *text
= F2 (func
, help
->text
);
1112 const char *colon
= strchr (text
, ':');
1113 return (colon
? colon
+ 1 : text
);
1115 case GNM_FUNC_HELP_END
:
1123 dialog_function_select_get_description (GnmFunc
*func
, PangoAttrList
**pal
)
1125 PangoAttribute
*attr
;
1126 char const *desc
= dialog_function_select_peek_description (func
);
1128 GString
* gstr
= g_string_new (NULL
);
1130 *pal
= pango_attr_list_new ();
1133 while (*desc
!= '\0') {
1134 here
= strstr (desc
, "@{");
1136 g_string_append (gstr
, desc
);
1139 g_string_append_len (gstr
, desc
, here
- desc
);
1140 attr
= pango_attr_weight_new (PANGO_WEIGHT_BOLD
);
1141 attr
->start_index
= gstr
->len
;
1143 here
= strchr (desc
,'}');
1145 g_string_append (gstr
, desc
);
1146 pango_attr_list_insert (*pal
, attr
);
1149 g_string_append_len (gstr
, desc
, here
- desc
);
1150 attr
->end_index
= gstr
->len
;
1151 pango_attr_list_insert (*pal
, attr
);
1156 return g_string_free (gstr
, FALSE
);
1162 dialog_function_select_load_tree (FunctionSelectState
*state
)
1165 GnmFuncGroup
const * cat
;
1166 GSList
*funcs
= NULL
, *ptr
;
1172 gtk_list_store_clear (state
->model_functions
);
1174 while ((cat
= gnm_func_group_get_nth (i
++)) != NULL
)
1175 funcs
= g_slist_concat (funcs
,
1176 g_slist_copy (cat
->functions
));
1178 funcs
= g_slist_sort_with_data (funcs
,
1179 dialog_function_select_by_name
,
1182 for (ptr
= funcs
; ptr
; ptr
= ptr
->next
) {
1185 (GNM_FUNC_INTERNAL
| GNM_FUNC_IS_PLACEHOLDER
))) {
1186 gtk_list_store_append (state
->model_functions
, &iter
);
1187 gnm_func_ref (func
);
1188 desc
= dialog_function_select_get_description (func
, &pal
);
1190 (state
->model_functions
, &iter
,
1191 FUN_NAME
, gnm_func_get_name (func
, state
->localized_function_names
),
1193 FUNCTION_DESC
, desc
,
1195 FUNCTION_CAT
, func
->fn_group
,
1196 FUNCTION_VISIBLE
, TRUE
,
1197 FUNCTION_RECENT
, FALSE
,
1198 FUNCTION_USED
, (func
->usage_count
> 1),
1201 pango_attr_list_unref (pal
);
1206 g_slist_free (funcs
);
1210 dialog_function_select_init (FunctionSelectState
*state
)
1212 GtkTreeViewColumn
*column
;
1213 GtkTreeSelection
*selection
;
1215 GtkTextBuffer
*description
;
1216 GtkCellRenderer
*cell
;
1217 GtkWidget
*cancel_button
;
1218 GtkWidget
*close_button
;
1220 g_object_set_data (G_OBJECT (state
->dialog
), FUNCTION_SELECT_DIALOG_KEY
,
1223 /* Set-up combo box */
1224 state
->cb
= GTK_COMBO_BOX
1225 (go_gtk_builder_get_widget (state
->gui
, "category-box"));
1226 state
->model
= gtk_list_store_new
1227 (NUM_CAT_COLUMNS
, G_TYPE_STRING
, G_TYPE_POINTER
, G_TYPE_BOOLEAN
);
1229 gtk_combo_box_set_model (state
->cb
, GTK_TREE_MODEL (state
->model
));
1230 g_object_unref (state
->model
);
1231 cell
= gtk_cell_renderer_text_new ();
1232 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (state
->cb
), cell
, TRUE
);
1233 gtk_cell_layout_add_attribute
1234 (GTK_CELL_LAYOUT (state
->cb
), cell
, "text", CAT_NAME
);
1235 dialog_function_select_load_cb (state
);
1236 gtk_combo_box_set_row_separator_func
1237 (state
->cb
, dialog_function_select_cat_row_separator
,
1239 g_signal_connect (state
->cb
, "changed",
1240 G_CALLBACK (dialog_function_select_cat_changed
),
1242 /* Finished set-up of combo box */
1244 /* Set-up treeview */
1246 state
->model_functions
= gtk_list_store_new
1250 /* FUNCTION_DESC, */
1253 /* FUNCTION_VISIBLE, */
1254 /* FUNCTION_RECENT, */
1255 /* FUNCTION_USED, */
1256 G_TYPE_STRING
, G_TYPE_POINTER
,
1257 G_TYPE_STRING
, PANGO_TYPE_ATTR_LIST
,
1258 G_TYPE_POINTER
, G_TYPE_BOOLEAN
,
1259 G_TYPE_BOOLEAN
, G_TYPE_BOOLEAN
);
1261 state
->model_filter
= gtk_tree_model_filter_new
1262 (GTK_TREE_MODEL (state
->model_functions
), NULL
);
1263 g_object_unref (state
->model_functions
);
1264 gtk_tree_model_filter_set_visible_column
1265 (GTK_TREE_MODEL_FILTER (state
->model_filter
), FUNCTION_VISIBLE
);
1267 state
->treeview
= GTK_TREE_VIEW
1268 (go_gtk_builder_get_widget (state
->gui
, "function-list"));
1269 gtk_tree_view_set_model (state
->treeview
,
1270 state
->model_filter
);
1271 g_object_unref (state
->model_filter
);
1273 selection
= gtk_tree_view_get_selection (state
->treeview
);
1274 gtk_tree_selection_set_mode (selection
, GTK_SELECTION_BROWSE
);
1275 g_signal_connect (selection
, "changed",
1277 (cb_dialog_function_select_fun_selection_changed
),
1280 column
= gtk_tree_view_column_new_with_attributes
1282 gtk_cell_renderer_text_new (),
1283 "text", FUN_NAME
, NULL
);
1284 gtk_tree_view_append_column (state
->treeview
, column
);
1285 column
= gtk_tree_view_column_new_with_attributes
1287 gtk_cell_renderer_text_new (),
1288 "text", FUNCTION_DESC
,
1289 "attributes", FUNCTION_PAL
, NULL
);
1290 gtk_tree_view_append_column (state
->treeview
, column
);
1292 gtk_tree_view_set_headers_visible (state
->treeview
, FALSE
);
1293 /* Finished set-up of treeview */
1295 dialog_function_select_load_tree (state
);
1296 dialog_function_load_recent_funcs (state
);
1298 state
->search_entry
= go_gtk_builder_get_widget (state
->gui
,
1300 if (state
->paste
.prefix
!= NULL
)
1301 gtk_entry_set_text (GTK_ENTRY (state
->search_entry
),
1302 state
->paste
.prefix
);
1304 g_signal_connect (G_OBJECT (state
->search_entry
),
1307 (dialog_function_select_erase_search_entry
),
1310 g_signal_connect (G_OBJECT (state
->search_entry
),
1312 G_CALLBACK (dialog_function_select_search
),
1314 if (state
->mode
!= HELP_MODE
)
1315 g_signal_connect (G_OBJECT (state
->treeview
),
1317 G_CALLBACK (cb_dialog_function_row_activated
),
1320 gtk_paned_set_position (GTK_PANED (go_gtk_builder_get_widget
1321 (state
->gui
, "vpaned1")), 300);
1323 state
->description_view
= GTK_TEXT_VIEW (go_gtk_builder_get_widget
1324 (state
->gui
, "description"));
1325 gtk_style_context_add_class
1326 (gtk_widget_get_style_context (GTK_WIDGET (state
->description_view
)),
1328 description
= gtk_text_view_get_buffer (state
->description_view
);
1329 gtk_text_buffer_get_start_iter (description
, &where
);
1330 gtk_text_buffer_create_mark (description
, "start-mark", &where
, TRUE
);
1332 g_signal_connect_after (G_OBJECT (description
),
1334 G_CALLBACK (cb_description_clicked
), state
);
1336 state
->ok_button
= go_gtk_builder_get_widget (state
->gui
, "ok_button");
1337 gtk_widget_set_sensitive (state
->ok_button
, FALSE
);
1338 g_signal_connect (G_OBJECT (state
->ok_button
),
1340 G_CALLBACK (cb_dialog_function_select_ok_clicked
), state
);
1341 state
->paste_button
= go_gtk_builder_get_widget (state
->gui
, "paste_button");
1342 gtk_widget_set_sensitive (state
->paste_button
, FALSE
);
1343 g_signal_connect (G_OBJECT (state
->paste_button
),
1345 G_CALLBACK (cb_dialog_function_select_paste_clicked
), state
);
1346 cancel_button
= go_gtk_builder_get_widget (state
->gui
, "cancel_button");
1347 g_signal_connect (G_OBJECT (cancel_button
), "clicked",
1348 G_CALLBACK (cb_dialog_function_select_cancel_clicked
), state
);
1349 close_button
= go_gtk_builder_get_widget (state
->gui
, "close_button");
1350 g_signal_connect (G_OBJECT (close_button
), "clicked",
1351 G_CALLBACK (cb_dialog_function_select_cancel_clicked
), state
);
1353 gnm_dialog_setup_destroy_handlers
1354 (GTK_DIALOG (state
->dialog
),
1356 GNM_DIALOG_DESTROY_CURRENT_SHEET_REMOVED
);
1358 gnm_init_help_button (
1359 go_gtk_builder_get_widget (state
->gui
, "help_button"),
1360 GNUMERIC_HELP_LINK_FUNCTION_SELECT
);
1361 g_object_set_data_full
1362 (G_OBJECT (state
->dialog
),
1364 (GDestroyNotify
) cb_dialog_function_select_destroy
);
1366 if (state
->paste
.prefix
!= NULL
)
1367 dialog_function_select_search
1368 (GTK_ENTRY (state
->search_entry
), state
);
1370 gtk_widget_set_visible (close_button
, state
->mode
!= GURU_MODE
);
1371 gtk_widget_set_visible (go_gtk_builder_get_widget
1372 (state
->gui
, "help_button"),
1373 state
->mode
== GURU_MODE
);
1374 gtk_widget_set_visible (cancel_button
, state
->mode
== GURU_MODE
);
1375 gtk_widget_set_visible (state
->ok_button
, state
->mode
== GURU_MODE
);
1376 gtk_widget_set_visible (state
->paste_button
, state
->mode
== PASTE_MODE
);
1377 gtk_widget_set_visible (go_gtk_builder_get_widget
1378 (state
->gui
, "title_label"),
1379 state
->mode
== GURU_MODE
);
1380 gtk_combo_box_set_active (state
->cb
, state
->mode
== HELP_MODE
? 2 : 0);
1381 switch (state
->mode
) {
1385 gtk_window_set_title (GTK_WINDOW (state
->dialog
),
1386 _("Gnumeric Function Help Browser"));
1389 gtk_window_set_title (GTK_WINDOW (state
->dialog
),
1390 _("Paste Function Name dialog"));
1396 dialog_function_select_full (WBCGtk
*wbcg
, char const *guru_key
,
1397 char const *key
, DialogMode mode
, gint from
, gint to
)
1399 FunctionSelectState
* state
;
1402 g_return_if_fail (wbcg
!= NULL
);
1404 if (gnm_dialog_raise_if_exists (wbcg
, key
))
1406 gui
= gnm_gtk_builder_load ("function-select.ui", NULL
, GO_CMD_CONTEXT (wbcg
));
1410 state
= g_new (FunctionSelectState
, 1);
1412 state
->sheet
= wb_control_cur_sheet (GNM_WBC (wbcg
));
1413 state
->localized_function_names
= state
->sheet
->convs
->localized_function_names
;
1414 state
->wb
= state
->sheet
->workbook
;
1416 state
->dialog
= go_gtk_builder_get_widget (state
->gui
, "selection_dialog");
1417 state
->formula_guru_key
= guru_key
;
1418 state
->recent_funcs
= NULL
;
1420 state
->paste
.from
= from
;
1421 state
->paste
.to
= to
;
1423 gnm_dialog_setup_destroy_handlers (GTK_DIALOG (state
->dialog
),
1425 GNM_DIALOG_DESTROY_SHEET_REMOVED
);
1427 if (mode
== PASTE_MODE
&& state
->paste
.from
>= 0) {
1429 = GTK_EDITABLE (wbcg_get_entry (state
->wbcg
));
1430 state
->paste
.prefix
= gtk_editable_get_chars
1431 (entry
, state
->paste
.from
,
1434 state
->paste
.prefix
= NULL
;
1436 dialog_function_select_init (state
);
1437 gnm_keyed_dialog (state
->wbcg
, GTK_WINDOW (state
->dialog
),
1440 gtk_widget_show (state
->dialog
);
1444 dialog_function_select (WBCGtk
*wbcg
, char const *key
)
1446 dialog_function_select_full (wbcg
, key
,
1447 FUNCTION_SELECT_KEY
, GURU_MODE
, -1, -1);
1451 dialog_function_select_help (WBCGtk
*wbcg
)
1453 dialog_function_select_full (wbcg
, NULL
,
1454 FUNCTION_SELECT_HELP_KEY
, HELP_MODE
,
1459 dialog_function_select_paste (WBCGtk
*wbcg
, gint from
, gint to
)
1461 dialog_function_select_full (wbcg
, NULL
,
1462 FUNCTION_SELECT_PASTE_KEY
, PASTE_MODE
,