1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * application.c: Manage the data common to all workbooks
6 * Jody Goldberg <jody@gnome.org>
8 #include <gnumeric-config.h>
11 #include "application.h"
13 #include "clipboard.h"
14 #include "selection.h"
15 #include "workbook-control.h"
16 #include "workbook-view.h"
19 #include "sheet-view.h"
20 #include "sheet-private.h"
21 #include "sheet-object.h"
22 #include "auto-correct.h"
25 #include "sheet-object.h"
27 #include "gui-clipboard.h"
28 #include "expr-name.h"
29 #include "workbook-priv.h"
31 #include <gnumeric-conf.h>
32 #include <goffice/goffice.h>
33 #include <gsf/gsf-impl-utils.h>
37 #define GNM_APP(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GNM_APP_TYPE, GnmApp))
38 #define GNM_IS_APP(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GNM_APP_TYPE))
42 APPLICATION_PROP_FILE_HISTORY_LIST
57 static guint signals
[LAST_SIGNAL
] = { 0 };
63 SheetView
*clipboard_sheet_view
;
64 GnmCellRegion
*clipboard_copied_contents
;
65 GnmRange
*clipboard_cut_range
;
69 /* Recalculation manager. */
72 GtkRecentManager
*recent
;
79 void (*workbook_added
) (GnmApp
*gnm_app
, Workbook
*wb
);
80 void (*workbook_removed
) (GnmApp
*gnm_app
, Workbook
*wb
);
81 void (*window_list_changed
) (GnmApp
*gnm_app
);
82 void (*custom_ui_added
) (GnmApp
*gnm_app
, GnmAppExtraUI
*ui
);
83 void (*custom_ui_removed
) (GnmApp
*gnm_app
, GnmAppExtraUI
*ui
);
84 void (*clipboard_modified
) (GnmApp
*gnm_app
);
85 void (*recalc_finished
) (GnmApp
*gnm_app
);
86 void (*recalc_clear_caches
) (GnmApp
*gnm_app
);
89 static GObjectClass
*parent_klass
;
92 static Workbook
*gnm_app_workbook_get_by_uri (char const *uri
);
97 * Returns: (transfer none): the #GnmApp instance.
100 gnm_app_get_app (void)
102 return G_OBJECT (app
);
106 * gnm_app_workbook_list_add:
109 * Add @wb to the application's list of workbooks.
112 gnm_app_workbook_list_add (Workbook
*wb
)
114 g_return_if_fail (GNM_IS_WORKBOOK (wb
));
115 g_return_if_fail (app
!= NULL
);
117 app
->workbook_list
= g_list_prepend (app
->workbook_list
, wb
);
118 g_signal_connect (G_OBJECT (wb
),
120 G_CALLBACK (_gnm_app_flag_windows_changed
), NULL
);
121 _gnm_app_flag_windows_changed ();
122 g_signal_emit (G_OBJECT (app
), signals
[WORKBOOK_ADDED
], 0, wb
);
126 * gnm_app_workbook_list_remove:
129 * Remove @wb from the application's list of workbooks.
132 gnm_app_workbook_list_remove (Workbook
*wb
)
134 g_return_if_fail (wb
!= NULL
);
135 g_return_if_fail (app
!= NULL
);
137 app
->workbook_list
= g_list_remove (app
->workbook_list
, wb
);
138 g_signal_handlers_disconnect_by_func (G_OBJECT (wb
),
139 G_CALLBACK (_gnm_app_flag_windows_changed
), NULL
);
140 _gnm_app_flag_windows_changed ();
141 g_signal_emit (G_OBJECT (app
), signals
[WORKBOOK_REMOVED
], 0, wb
);
145 * gnm_app_workbook_list:
147 * Returns: (element-type Workbook) (transfer none): the workbook list.
150 gnm_app_workbook_list (void)
152 g_return_val_if_fail (app
!= NULL
, NULL
);
154 return app
->workbook_list
;
158 gnm_app_sanity_check (void)
161 gboolean err
= FALSE
;
162 for (l
= gnm_app_workbook_list (); l
; l
= l
->next
) {
163 Workbook
*wb
= l
->data
;
164 if (gnm_named_expr_collection_sanity_check (wb
->names
, "workbook"))
168 g_error ("Sanity check failed\n");
174 * gnm_app_clipboard_clear:
176 * Clear and free the contents of the clipboard if it is
180 gnm_app_clipboard_clear (gboolean drop_selection
)
182 g_return_if_fail (app
!= NULL
);
184 if (app
->clipboard_copied_contents
) {
185 cellregion_unref (app
->clipboard_copied_contents
);
186 app
->clipboard_copied_contents
= NULL
;
188 if (app
->clipboard_sheet_view
!= NULL
) {
189 gnm_sheet_view_unant (app
->clipboard_sheet_view
);
191 g_signal_emit (G_OBJECT (app
), signals
[CLIPBOARD_MODIFIED
], 0);
193 gnm_sheet_view_weak_unref (&(app
->clipboard_sheet_view
));
195 /* Release the selection */
197 gnm_x_disown_clipboard ();
202 gnm_app_clipboard_invalidate_sheet (Sheet
*sheet
)
204 /* Clear the cliboard to avoid dangling references to the deleted sheet */
205 if (sheet
== gnm_app_clipboard_sheet_get ())
206 gnm_app_clipboard_clear (TRUE
);
207 else if (app
->clipboard_copied_contents
)
208 cellregion_invalidate_sheet (app
->clipboard_copied_contents
, sheet
);
212 gnm_app_clipboard_unant (void)
214 g_return_if_fail (app
!= NULL
);
216 if (app
->clipboard_sheet_view
!= NULL
)
217 gnm_sheet_view_unant (app
->clipboard_sheet_view
);
221 * gnm_app_clipboard_cut_copy:
222 * @wbc: the workbook control that requested the operation.
223 * @is_cut: is this a cut or a copy.
224 * @sv: The source sheet for the copy.
225 * @area: A single rectangular range to be copied.
226 * @animate_range: Do we want ot add an animated cursor around things.
229 * Clear and free the contents of the clipboard and save the sheet and area
230 * to be cut. DO NOT ACTUALLY CUT! Paste will move the region if this was a
234 * Clear and free the contents of the clipboard and COPY the designated region
235 * into the clipboard.
237 * we need to pass @wbc as a control rather than a simple command-context so
238 * that the control can claim the selection.
241 gnm_app_clipboard_cut_copy (WorkbookControl
*wbc
, gboolean is_cut
,
242 SheetView
*sv
, GnmRange
const *area
,
243 gboolean animate_cursor
)
247 g_return_if_fail (GNM_IS_SHEET_VIEW (sv
));
248 g_return_if_fail (area
!= NULL
);
249 g_return_if_fail (app
!= NULL
);
251 gnm_app_clipboard_clear (FALSE
);
252 sheet
= sv_sheet (sv
);
253 g_free (app
->clipboard_cut_range
);
254 app
->clipboard_cut_range
= gnm_range_dup (area
);
255 gnm_sheet_view_weak_ref (sv
, &(app
->clipboard_sheet_view
));
258 app
->clipboard_copied_contents
=
259 clipboard_copy_range (sheet
, area
);
260 if (animate_cursor
) {
261 GList
*l
= g_list_append (NULL
, (gpointer
)area
);
262 gnm_sheet_view_ant (sv
, l
);
266 if (wb_control_claim_selection (wbc
)) {
267 g_signal_emit (G_OBJECT (app
), signals
[CLIPBOARD_MODIFIED
], 0);
269 gnm_app_clipboard_clear (FALSE
);
270 g_warning ("Unable to set selection ?");
275 * gnm_app_clipboard_cut_copy_obj:
276 * @wbc: #WorkbookControl
279 * @objects: (element-type SheetObject): a list of #SheetObject which is freed
281 * Different than copying/cutting a region, this can actually cuts an object
284 gnm_app_clipboard_cut_copy_obj (WorkbookControl
*wbc
, gboolean is_cut
,
285 SheetView
*sv
, GSList
*objects
)
287 g_return_if_fail (GNM_IS_SHEET_VIEW (sv
));
288 g_return_if_fail (objects
!= NULL
);
289 g_return_if_fail (app
!= NULL
);
291 gnm_app_clipboard_clear (FALSE
);
292 g_free (app
->clipboard_cut_range
);
293 app
->clipboard_cut_range
= NULL
;
294 gnm_sheet_view_weak_ref (sv
, &(app
->clipboard_sheet_view
));
295 app
->clipboard_copied_contents
296 = clipboard_copy_obj (sv_sheet (sv
), objects
);
298 cmd_objects_delete (wbc
, objects
, _("Cut Object"));
301 if (wb_control_claim_selection (wbc
)) {
302 g_signal_emit (G_OBJECT (app
), signals
[CLIPBOARD_MODIFIED
], 0);
304 gnm_app_clipboard_clear (FALSE
);
305 g_warning ("Unable to set selection ?");
307 g_slist_free (objects
);
311 gnm_app_clipboard_is_empty (void)
313 g_return_val_if_fail (app
!= NULL
, TRUE
);
315 return app
->clipboard_sheet_view
== NULL
;
319 gnm_app_clipboard_is_cut (void)
321 g_return_val_if_fail (app
!= NULL
, FALSE
);
323 if (app
->clipboard_sheet_view
!= NULL
)
324 return app
->clipboard_copied_contents
? FALSE
: TRUE
;
329 * gnm_app_clipboard_sheet_get:
331 * Returns: (transfer none): the current clipboard #Sheet.
334 gnm_app_clipboard_sheet_get (void)
336 g_return_val_if_fail (app
!= NULL
, NULL
);
338 if (app
->clipboard_sheet_view
== NULL
)
340 return sv_sheet (app
->clipboard_sheet_view
);
344 * gnm_app_clipboard_sheet_view_get:
346 * Returns: (transfer none): the current clipboard #SheetView.
349 gnm_app_clipboard_sheet_view_get (void)
351 g_return_val_if_fail (app
!= NULL
, NULL
);
352 return app
->clipboard_sheet_view
;
356 gnm_app_clipboard_contents_get (void)
358 g_return_val_if_fail (app
!= NULL
, NULL
);
359 return app
->clipboard_copied_contents
;
363 gnm_app_clipboard_area_get (void)
365 g_return_val_if_fail (app
!= NULL
, NULL
);
367 * Only return the range if the sheet has been set.
368 * The range will still contain data even after
369 * the clipboard has been cleared so we need to be extra
370 * safe and only return a range if there is a valid selection
372 if (app
->clipboard_sheet_view
!= NULL
)
373 return app
->clipboard_cut_range
;
378 * gnm_app_workbook_get_by_name:
379 * @name: the workbook name.
382 * Returns: (transfer none): the #Workbook or %NULL.
385 gnm_app_workbook_get_by_name (char const *name
,
389 char *filename
= NULL
;
391 if (name
== NULL
|| *name
== 0)
395 wb
= gnm_app_workbook_get_by_uri (name
);
399 filename
= g_filename_from_utf8 (name
, -1, NULL
, NULL
, NULL
);
401 /* Try as absolute filename. */
402 if (filename
&& g_path_is_absolute (filename
)) {
403 char *uri
= go_filename_to_uri (filename
);
405 wb
= gnm_app_workbook_get_by_uri (uri
);
412 if (filename
&& ref_uri
) {
413 char *rel_uri
= go_url_encode (filename
, 1);
414 char *uri
= go_url_resolve_relative (ref_uri
, rel_uri
);
417 wb
= gnm_app_workbook_get_by_uri (uri
);
429 struct wb_uri_closure
{
435 cb_workbook_uri (Workbook
* wb
, gpointer closure
)
437 struct wb_uri_closure
*dat
= closure
;
438 char const *wb_uri
= go_doc_get_uri (GO_DOC (wb
));
440 if (wb_uri
&& strcmp (wb_uri
, dat
->uri
) == 0) {
448 gnm_app_workbook_foreach (GnmWbIterFunc cback
, gpointer data
);
451 gnm_app_workbook_get_by_uri (char const *uri
)
453 struct wb_uri_closure closure
;
455 g_return_val_if_fail (uri
!= NULL
, NULL
);
459 gnm_app_workbook_foreach (&cb_workbook_uri
, &closure
);
465 gnm_app_workbook_foreach (GnmWbIterFunc cback
, gpointer data
)
469 g_return_val_if_fail (app
!= NULL
, FALSE
);
471 for (l
= app
->workbook_list
; l
; l
= l
->next
){
472 Workbook
*wb
= l
->data
;
474 if (!(*cback
)(wb
, data
))
481 * gnm_app_workbook_get_by_index:
484 * Get nth workbook. Index is zero-based.
485 * Return value: (transfer none): the nth workbook if any.
488 gnm_app_workbook_get_by_index (int i
)
490 return g_list_nth_data (app
->workbook_list
, i
);
494 gnm_app_display_dpi_get (gboolean horizontal
)
497 ? gnm_conf_get_core_gui_screen_horizontaldpi ()
498 : gnm_conf_get_core_gui_screen_verticaldpi ();
502 gnm_app_dpi_to_pixels (void)
504 return MIN (gnm_app_display_dpi_get (TRUE
),
505 gnm_app_display_dpi_get (FALSE
)) / 72.;
510 * gnm_app_create_opener_filter:
511 * @openers: (element-type GOFileOpener): a list of file openers.
513 * Creates a #GtkFileFilter from the list of file types supported by the
514 * openers in the list.
515 * Returns: (transfer full): the newly allocated #GtkFileFilter.
518 gnm_app_create_opener_filter (GList
*openers
)
521 static const char *const bad_suffixes
[] = {
528 GtkFileFilter
*filter
= gtk_file_filter_new ();
529 gboolean for_history
= (openers
== NULL
);
532 openers
= go_get_file_openers ();
534 for (; openers
; openers
= openers
->next
) {
535 GOFileOpener
*opener
= openers
->data
;
536 if (opener
!= NULL
) {
537 const GSList
*mimes
= go_file_opener_get_mimes (opener
);
538 const GSList
*suffixes
= go_file_opener_get_suffixes (opener
);
542 const char *mime
= mimes
->data
;
544 * See 438918. Too many things
545 * like *.xml and *.txt get added
546 * to be useful for the file history
548 gtk_file_filter_add_mime_type (filter
, mime
);
550 g_print ("%s: Adding mime %s\n", go_file_opener_get_description (opener
), mime
);
555 const char *suffix
= suffixes
->data
;
560 for (i
= 0; bad_suffixes
[i
]; i
++)
561 if (strcmp (suffix
, bad_suffixes
[i
]) == 0)
564 /* Create "*.[xX][lL][sS]" */
565 pattern
= g_string_new ("*.");
567 gunichar uc
= g_utf8_get_char (suffix
);
568 suffix
= g_utf8_next_char (suffix
);
569 if (g_unichar_islower (uc
)) {
570 g_string_append_c (pattern
, '[');
571 g_string_append_unichar (pattern
, uc
);
572 uc
= g_unichar_toupper (uc
);
573 g_string_append_unichar (pattern
, uc
);
574 g_string_append_c (pattern
, ']');
576 g_string_append_unichar (pattern
, uc
);
579 gtk_file_filter_add_pattern (filter
, pattern
->str
);
581 g_print ("%s: Adding %s\n", go_file_opener_get_description (opener
), pattern
->str
);
582 g_string_free (pattern
, TRUE
);
585 suffixes
= suffixes
->next
;
593 compare_mru (GtkRecentInfo
*a
, GtkRecentInfo
*b
)
595 time_t ta
= MAX (gtk_recent_info_get_visited (a
), gtk_recent_info_get_modified (a
));
596 time_t tb
= MAX (gtk_recent_info_get_visited (b
), gtk_recent_info_get_modified (b
));
602 * gnm_app_history_get_list:
604 * creating it if necessary.
606 * Return value: (element-type utf8) (transfer full): the list, which must be
607 * freed along with the strings in it.
610 gnm_app_history_get_list (int max_elements
)
614 GtkFileFilter
*filter
;
617 if (app
->recent
== NULL
)
620 items
= gtk_recent_manager_get_items (app
->recent
);
621 items
= g_list_sort (items
, (GCompareFunc
)compare_mru
);
623 filter
= gnm_app_create_opener_filter (NULL
);
625 for (l
= items
; l
&& n_elements
< max_elements
; l
= l
->next
) {
626 GtkRecentInfo
*ri
= l
->data
;
627 const char *uri
= gtk_recent_info_get_uri (ri
);
630 if (gtk_recent_info_has_application (ri
, g_get_application_name ())) {
633 GtkFileFilterInfo fi
;
634 char *display_name
= g_filename_display_basename (uri
);
636 memset (&fi
, 0, sizeof (fi
));
637 fi
.contains
= (GTK_FILE_FILTER_MIME_TYPE
|
638 GTK_FILE_FILTER_URI
|
639 GTK_FILE_FILTER_DISPLAY_NAME
);
641 fi
.mime_type
= gtk_recent_info_get_mime_type (ri
);
642 fi
.display_name
= display_name
;
643 want_it
= gtk_file_filter_filter (filter
, &fi
);
644 g_free (display_name
);
648 char *filename
= go_filename_from_uri (uri
);
649 if (filename
&& !g_file_test (filename
, G_FILE_TEST_EXISTS
))
655 res
= g_slist_prepend (res
, g_strdup (uri
));
660 g_list_free_full (items
, (GDestroyNotify
)gtk_recent_info_unref
);
661 g_object_ref_sink (filter
);
662 g_object_unref (filter
);
664 return g_slist_reverse (res
);
668 * application_history_update_list:
671 * Adds @uri to the application's history of files.
674 gnm_app_history_add (char const *uri
, const char *mimetype
)
678 if (app
->recent
== NULL
)
681 memset (&rd
, 0, sizeof (rd
));
684 g_print ("uri: %s\nmime: %s\n\n", uri
, mimetype
? mimetype
: "-");
688 g_strdup (mimetype
? mimetype
: "application/octet-stream");
690 rd
.app_name
= g_strdup (g_get_application_name ());
691 rd
.app_exec
= g_strjoin (" ", g_get_prgname (), "%u", NULL
);
693 rd
.is_private
= FALSE
;
695 if (!gtk_recent_manager_add_full (app
->recent
, uri
, &rd
)) {
697 g_printerr ("Warning: failed to update recent document.\n");
700 g_free (rd
.mime_type
);
701 g_free (rd
.app_name
);
702 g_free (rd
.app_exec
);
704 g_object_notify (G_OBJECT (app
), "file-history-list");
708 cb_recent_changed (G_GNUC_UNUSED GtkRecentManager
*recent
, GnmApp
*app
)
710 g_object_notify (G_OBJECT (app
), "file-history-list");
714 gnumeric_application_finalize (GObject
*obj
)
716 GnmApp
*application
= GNM_APP (obj
);
718 g_free (application
->clipboard_cut_range
);
719 application
->clipboard_cut_range
= NULL
;
721 application
->recent
= NULL
;
723 if (app
== application
)
726 G_OBJECT_CLASS (parent_klass
)->finalize (obj
);
730 gnumeric_application_get_property (GObject
*obj
, guint param_id
,
731 GValue
*value
, GParamSpec
*pspec
)
734 GnmApp
*application
= GNM_APP (obj
);
737 case APPLICATION_PROP_FILE_HISTORY_LIST
:
738 g_value_set_pointer (value
, gnm_app_history_get_list (G_MAXINT
));
740 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj
, param_id
, pspec
);
746 gnm_app_class_init (GObjectClass
*gobject_klass
)
748 parent_klass
= g_type_class_peek_parent (gobject_klass
);
750 /* Object class method overrides */
751 gobject_klass
->finalize
= gnumeric_application_finalize
;
752 gobject_klass
->get_property
= gnumeric_application_get_property
;
753 g_object_class_install_property (gobject_klass
, APPLICATION_PROP_FILE_HISTORY_LIST
,
754 g_param_spec_pointer ("file-history-list",
755 P_("File History List"),
756 P_("A list of filenames that have been read recently"),
757 GSF_PARAM_STATIC
| G_PARAM_READABLE
));
759 signals
[WORKBOOK_ADDED
] = g_signal_new ("workbook_added",
762 G_STRUCT_OFFSET (GnmAppClass
, workbook_added
),
764 g_cclosure_marshal_VOID__OBJECT
,
765 G_TYPE_NONE
, 1, GNM_WORKBOOK_TYPE
);
766 signals
[WORKBOOK_REMOVED
] = g_signal_new ("workbook_removed",
769 G_STRUCT_OFFSET (GnmAppClass
, workbook_removed
),
771 g_cclosure_marshal_VOID__POINTER
,
772 G_TYPE_NONE
, 1, G_TYPE_POINTER
);
773 signals
[WINDOW_LIST_CHANGED
] = g_signal_new ("window-list-changed",
776 G_STRUCT_OFFSET (GnmAppClass
, window_list_changed
),
778 g_cclosure_marshal_VOID__VOID
,
780 signals
[CUSTOM_UI_ADDED
] = g_signal_new ("custom-ui-added",
783 G_STRUCT_OFFSET (GnmAppClass
, custom_ui_added
),
785 g_cclosure_marshal_VOID__POINTER
,
786 G_TYPE_NONE
, 1, G_TYPE_POINTER
);
787 signals
[CUSTOM_UI_REMOVED
] = g_signal_new ("custom-ui-removed",
790 G_STRUCT_OFFSET (GnmAppClass
, custom_ui_removed
),
792 g_cclosure_marshal_VOID__POINTER
,
793 G_TYPE_NONE
, 1, G_TYPE_POINTER
);
794 signals
[CLIPBOARD_MODIFIED
] = g_signal_new ("clipboard_modified",
797 G_STRUCT_OFFSET (GnmAppClass
, clipboard_modified
),
799 g_cclosure_marshal_VOID__VOID
,
801 signals
[RECALC_FINISHED
] = g_signal_new ("recalc_finished",
804 G_STRUCT_OFFSET (GnmAppClass
, recalc_finished
),
806 g_cclosure_marshal_VOID__VOID
,
808 signals
[RECALC_FINISHED
] = g_signal_new ("recalc_clear_caches",
811 G_STRUCT_OFFSET (GnmAppClass
, recalc_clear_caches
),
813 g_cclosure_marshal_VOID__VOID
,
818 gnm_app_init (GObject
*obj
)
820 GnmApp
*gnm_app
= GNM_APP (obj
);
822 gnm_app
->clipboard_copied_contents
= NULL
;
823 gnm_app
->clipboard_sheet_view
= NULL
;
825 gnm_app
->workbook_list
= NULL
;
827 if (gdk_display_get_default ()) {
829 * Only allocate a GtkRecentManager if we have a gui.
830 * This is, in part, because it currently throws an error.
833 gnm_app
->recent
= gtk_recent_manager_get_default ();
834 g_signal_connect_object (G_OBJECT (gnm_app
->recent
),
836 G_CALLBACK (cb_recent_changed
),
843 GSF_CLASS (GnmApp
, gnm_app
,
844 gnm_app_class_init
, gnm_app_init
,
847 /**********************************************************************/
848 static GSList
*extra_uis
= NULL
;
855 * @always_available: whether the action should always be available.
856 * @handler: (scope async): the handler.
858 * Returns: (transfer full): the newly allocated #GnmAction.
861 gnm_action_new (char const *id
, char const *label
,
862 char const *icon_name
, gboolean always_available
,
863 GnmActionHandler handler
)
865 GnmAction
*res
= g_new0 (GnmAction
, 1);
866 res
->id
= g_strdup (id
);
867 res
->label
= g_strdup (label
);
868 res
->icon_name
= g_strdup (icon_name
);
869 res
->always_available
= always_available
;
870 res
->handler
= handler
;
875 gnm_action_free (GnmAction
*action
)
877 if (NULL
!= action
) {
879 g_free (action
->label
);
880 g_free (action
->icon_name
);
886 gnm_action_copy (GnmAction
const *action
)
888 return gnm_action_new (action
->id
, action
->label
, action
->icon_name
,
889 action
->always_available
, action
->handler
);
893 gnm_action_get_type (void)
898 t
= g_boxed_type_register_static ("GnmAction",
899 (GBoxedCopyFunc
)gnm_action_copy
,
900 (GBoxedFreeFunc
)gnm_action_free
);
906 * making GnmAppExtraUI a boxed type for introspection. copy and free don't do
907 anything which might be critical, crossing fingers.*/
909 static GnmAppExtraUI
*
910 gnm_app_extra_ui_ref (GnmAppExtraUI
*ui
)
916 static GnmAppExtraUI
*
917 gnm_app_extra_ui_unref (GnmAppExtraUI
*ui
)
924 gnm_app_extra_ui_get_type (void)
929 t
= g_boxed_type_register_static ("GnmAppExtraUI",
930 (GBoxedCopyFunc
)gnm_app_extra_ui_ref
,
931 (GBoxedFreeFunc
)gnm_app_extra_ui_unref
);
937 * gnm_app_add_extra_ui:
938 * @group_name: action group name.
939 * @actions: (element-type GnmAction): list of actions.
940 * @layout: the xml string describing the menus and toolbars.
941 * @domain: localization domain.
942 * @user_data: user data
944 * Returns: (transfer full): the newly allocated #GnmAppExtraUI.
947 gnm_app_add_extra_ui (char const *group_name
,
953 GnmAppExtraUI
*extra_ui
= g_new0 (GnmAppExtraUI
, 1);
954 extra_uis
= g_slist_prepend (extra_uis
, extra_ui
);
955 extra_ui
->group_name
= g_strdup (group_name
);
956 extra_ui
->actions
= actions
;
957 extra_ui
->layout
= g_strdup (layout
);
958 extra_ui
->user_data
= user_data
;
959 g_signal_emit (G_OBJECT (app
), signals
[CUSTOM_UI_ADDED
], 0, extra_ui
);
960 if (gnm_debug_flag ("extra-ui"))
961 g_printerr ("Adding extra ui [%s] %p\n", group_name
, extra_ui
);
966 gnm_app_remove_extra_ui (GnmAppExtraUI
*extra_ui
)
968 if (gnm_debug_flag ("extra-ui"))
969 g_printerr ("Removing extra ui %p\n", extra_ui
);
970 extra_uis
= g_slist_remove (extra_uis
, extra_ui
);
971 g_signal_emit (G_OBJECT (app
), signals
[CUSTOM_UI_REMOVED
], 0, extra_ui
);
972 g_free (extra_ui
->group_name
);
973 g_free (extra_ui
->layout
);
978 * gnm_app_foreach_extra_ui:
979 * @func: (scope call): #GFunc
982 * Applies @func to each #GnmAppExtraUI.
985 gnm_app_foreach_extra_ui (GFunc func
, gpointer data
)
987 g_slist_foreach (extra_uis
, func
, data
);
990 /**********************************************************************/
992 static gint windows_update_timer
= 0;
994 cb_flag_windows_changed (void)
997 g_signal_emit (G_OBJECT (app
), signals
[WINDOW_LIST_CHANGED
], 0);
998 windows_update_timer
= 0;
1003 * _gnm_app_flag_windows_changed:
1005 * An internal utility routine to flag a regeneration of the window lists
1008 _gnm_app_flag_windows_changed (void)
1010 if (windows_update_timer
== 0)
1011 windows_update_timer
= g_timeout_add (100,
1012 (GSourceFunc
)cb_flag_windows_changed
, NULL
);
1015 /**********************************************************************/
1020 * Recalculate everything dirty in all workbooks that have automatic
1024 gnm_app_recalc (void)
1028 g_return_if_fail (app
!= NULL
);
1030 gnm_app_recalc_start ();
1032 for (l
= app
->workbook_list
; l
; l
= l
->next
) {
1033 Workbook
*wb
= l
->data
;
1035 if (workbook_get_recalcmode (wb
))
1036 workbook_recalc (wb
);
1039 gnm_app_recalc_finish ();
1043 gnm_app_recalc_start (void)
1045 g_return_if_fail (app
->recalc_count
>= 0);
1046 app
->recalc_count
++;
1050 gnm_app_recalc_finish (void)
1052 g_return_if_fail (app
->recalc_count
> 0);
1053 app
->recalc_count
--;
1054 if (app
->recalc_count
== 0) {
1055 gnm_app_recalc_clear_caches ();
1056 g_signal_emit_by_name (gnm_app_get_app (), "recalc-finished");
1061 gnm_app_recalc_clear_caches (void)
1063 g_signal_emit_by_name (gnm_app_get_app (), "recalc-clear-caches");