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))
44 PROP_INITIAL_OPEN_COMPLETE
59 static guint signals
[LAST_SIGNAL
] = { 0 };
65 SheetView
*clipboard_sheet_view
;
66 GnmCellRegion
*clipboard_copied_contents
;
67 GnmRange
*clipboard_cut_range
;
71 /* Recalculation manager. */
74 GtkRecentManager
*recent
;
77 gboolean shutting_down
;
78 gboolean initial_open_complete
;
84 void (*workbook_added
) (GnmApp
*gnm_app
, Workbook
*wb
);
85 void (*workbook_removed
) (GnmApp
*gnm_app
, Workbook
*wb
);
86 void (*window_list_changed
) (GnmApp
*gnm_app
);
87 void (*custom_ui_added
) (GnmApp
*gnm_app
, GnmAppExtraUI
*ui
);
88 void (*custom_ui_removed
) (GnmApp
*gnm_app
, GnmAppExtraUI
*ui
);
89 void (*clipboard_modified
) (GnmApp
*gnm_app
);
90 void (*recalc_finished
) (GnmApp
*gnm_app
);
91 void (*recalc_clear_caches
) (GnmApp
*gnm_app
);
94 static GObjectClass
*parent_klass
;
97 static Workbook
*gnm_app_workbook_get_by_uri (char const *uri
);
102 * Returns: (transfer none): the #GnmApp instance.
105 gnm_app_get_app (void)
107 return G_OBJECT (app
);
111 * gnm_app_workbook_list_add:
114 * Add @wb to the application's list of workbooks.
117 gnm_app_workbook_list_add (Workbook
*wb
)
119 g_return_if_fail (GNM_IS_WORKBOOK (wb
));
120 g_return_if_fail (app
!= NULL
);
122 app
->workbook_list
= g_list_prepend (app
->workbook_list
, wb
);
123 g_signal_connect (G_OBJECT (wb
),
125 G_CALLBACK (_gnm_app_flag_windows_changed
), NULL
);
126 _gnm_app_flag_windows_changed ();
127 g_signal_emit (G_OBJECT (app
), signals
[WORKBOOK_ADDED
], 0, wb
);
131 * gnm_app_workbook_list_remove:
134 * Remove @wb from the application's list of workbooks.
137 gnm_app_workbook_list_remove (Workbook
*wb
)
139 g_return_if_fail (wb
!= NULL
);
140 g_return_if_fail (app
!= NULL
);
142 app
->workbook_list
= g_list_remove (app
->workbook_list
, wb
);
143 g_signal_handlers_disconnect_by_func (G_OBJECT (wb
),
144 G_CALLBACK (_gnm_app_flag_windows_changed
), NULL
);
145 _gnm_app_flag_windows_changed ();
146 g_signal_emit (G_OBJECT (app
), signals
[WORKBOOK_REMOVED
], 0, wb
);
150 * gnm_app_workbook_list:
152 * Returns: (element-type Workbook) (transfer none): the workbook list.
155 gnm_app_workbook_list (void)
157 g_return_val_if_fail (app
!= NULL
, NULL
);
159 return app
->workbook_list
;
163 gnm_app_sanity_check (void)
166 gboolean err
= FALSE
;
167 for (l
= gnm_app_workbook_list (); l
; l
= l
->next
) {
168 Workbook
*wb
= l
->data
;
169 if (gnm_named_expr_collection_sanity_check (wb
->names
, "workbook"))
173 g_error ("Sanity check failed\n");
179 * gnm_app_clipboard_clear:
181 * Clear and free the contents of the clipboard if it is
185 gnm_app_clipboard_clear (gboolean drop_selection
)
187 g_return_if_fail (app
!= NULL
);
189 if (app
->clipboard_copied_contents
) {
190 cellregion_unref (app
->clipboard_copied_contents
);
191 app
->clipboard_copied_contents
= NULL
;
193 if (app
->clipboard_sheet_view
!= NULL
) {
194 gnm_sheet_view_unant (app
->clipboard_sheet_view
);
196 g_signal_emit (G_OBJECT (app
), signals
[CLIPBOARD_MODIFIED
], 0);
198 gnm_sheet_view_weak_unref (&(app
->clipboard_sheet_view
));
200 /* Release the selection */
202 gnm_x_disown_clipboard ();
207 gnm_app_clipboard_invalidate_sheet (Sheet
*sheet
)
209 /* Clear the cliboard to avoid dangling references to the deleted sheet */
210 if (sheet
== gnm_app_clipboard_sheet_get ())
211 gnm_app_clipboard_clear (TRUE
);
212 else if (app
->clipboard_copied_contents
)
213 cellregion_invalidate_sheet (app
->clipboard_copied_contents
, sheet
);
217 gnm_app_clipboard_unant (void)
219 g_return_if_fail (app
!= NULL
);
221 if (app
->clipboard_sheet_view
!= NULL
)
222 gnm_sheet_view_unant (app
->clipboard_sheet_view
);
226 * gnm_app_clipboard_cut_copy:
227 * @wbc: the workbook control that requested the operation.
228 * @is_cut: is this a cut or a copy.
229 * @sv: The source sheet for the copy.
230 * @area: A single rectangular range to be copied.
231 * @animate_range: Do we want ot add an animated cursor around things.
234 * Clear and free the contents of the clipboard and save the sheet and area
235 * to be cut. DO NOT ACTUALLY CUT! Paste will move the region if this was a
239 * Clear and free the contents of the clipboard and COPY the designated region
240 * into the clipboard.
242 * we need to pass @wbc as a control rather than a simple command-context so
243 * that the control can claim the selection.
246 gnm_app_clipboard_cut_copy (WorkbookControl
*wbc
, gboolean is_cut
,
247 SheetView
*sv
, GnmRange
const *area
,
248 gboolean animate_cursor
)
252 g_return_if_fail (GNM_IS_SHEET_VIEW (sv
));
253 g_return_if_fail (area
!= NULL
);
254 g_return_if_fail (app
!= NULL
);
256 gnm_app_clipboard_clear (FALSE
);
257 sheet
= sv_sheet (sv
);
258 g_free (app
->clipboard_cut_range
);
259 app
->clipboard_cut_range
= gnm_range_dup (area
);
260 gnm_sheet_view_weak_ref (sv
, &(app
->clipboard_sheet_view
));
263 app
->clipboard_copied_contents
=
264 clipboard_copy_range (sheet
, area
);
265 if (animate_cursor
) {
266 GList
*l
= g_list_append (NULL
, (gpointer
)area
);
267 gnm_sheet_view_ant (sv
, l
);
271 if (wb_control_claim_selection (wbc
)) {
272 g_signal_emit (G_OBJECT (app
), signals
[CLIPBOARD_MODIFIED
], 0);
274 gnm_app_clipboard_clear (FALSE
);
275 g_warning ("Unable to set selection?");
280 * gnm_app_clipboard_cut_copy_obj:
281 * @wbc: #WorkbookControl
282 * @is_cut: %TRUE for cut, %FALSE for copy
284 * @objects: (element-type SheetObject) (transfer container): a list
287 * Different than copying/cutting a region, this can actually cut an object
290 gnm_app_clipboard_cut_copy_obj (WorkbookControl
*wbc
, gboolean is_cut
,
291 SheetView
*sv
, GSList
*objects
)
293 g_return_if_fail (GNM_IS_SHEET_VIEW (sv
));
294 g_return_if_fail (objects
!= NULL
);
295 g_return_if_fail (app
!= NULL
);
297 gnm_app_clipboard_clear (FALSE
);
298 g_free (app
->clipboard_cut_range
);
299 app
->clipboard_cut_range
= NULL
;
300 gnm_sheet_view_weak_ref (sv
, &(app
->clipboard_sheet_view
));
301 app
->clipboard_copied_contents
302 = clipboard_copy_obj (sv_sheet (sv
), objects
);
304 cmd_objects_delete (wbc
, objects
, _("Cut Object"));
307 if (wb_control_claim_selection (wbc
)) {
308 g_signal_emit (G_OBJECT (app
), signals
[CLIPBOARD_MODIFIED
], 0);
310 gnm_app_clipboard_clear (FALSE
);
311 g_warning ("Unable to set selection?");
313 g_slist_free (objects
);
317 gnm_app_clipboard_is_empty (void)
319 g_return_val_if_fail (app
!= NULL
, TRUE
);
321 return app
->clipboard_sheet_view
== NULL
;
325 gnm_app_clipboard_is_cut (void)
327 g_return_val_if_fail (app
!= NULL
, FALSE
);
329 if (app
->clipboard_sheet_view
!= NULL
)
330 return app
->clipboard_copied_contents
? FALSE
: TRUE
;
335 * gnm_app_clipboard_sheet_get:
337 * Returns: (transfer none) (nullable): the current clipboard #Sheet.
340 gnm_app_clipboard_sheet_get (void)
342 g_return_val_if_fail (app
!= NULL
, NULL
);
344 if (app
->clipboard_sheet_view
== NULL
)
346 return sv_sheet (app
->clipboard_sheet_view
);
350 * gnm_app_clipboard_sheet_view_get:
352 * Returns: (transfer none) (nullable): the current clipboard #SheetView.
355 gnm_app_clipboard_sheet_view_get (void)
357 g_return_val_if_fail (app
!= NULL
, NULL
);
358 return app
->clipboard_sheet_view
;
362 * gnm_app_clipboard_contents_get:
364 * Returns: (nullable) (transfer none): the current contents of the clipboard.
367 gnm_app_clipboard_contents_get (void)
369 g_return_val_if_fail (app
!= NULL
, NULL
);
370 return app
->clipboard_copied_contents
;
374 * gnm_app_clipboard_area_get:
376 * Returns: (nullable) (transfer none): the current range in the clipboard.
379 gnm_app_clipboard_area_get (void)
381 g_return_val_if_fail (app
!= NULL
, NULL
);
383 * Only return the range if the sheet has been set.
384 * The range will still contain data even after
385 * the clipboard has been cleared so we need to be extra
386 * safe and only return a range if there is a valid selection
388 if (app
->clipboard_sheet_view
!= NULL
)
389 return app
->clipboard_cut_range
;
394 * gnm_app_workbook_get_by_name:
395 * @name: the workbook name.
398 * Returns: (transfer none): the #Workbook or %NULL.
401 gnm_app_workbook_get_by_name (char const *name
,
405 char *filename
= NULL
;
407 if (name
== NULL
|| *name
== 0)
411 wb
= gnm_app_workbook_get_by_uri (name
);
415 filename
= g_filename_from_utf8 (name
, -1, NULL
, NULL
, NULL
);
417 /* Try as absolute filename. */
418 if (filename
&& g_path_is_absolute (filename
)) {
419 char *uri
= go_filename_to_uri (filename
);
421 wb
= gnm_app_workbook_get_by_uri (uri
);
428 if (filename
&& ref_uri
) {
429 char *rel_uri
= go_url_encode (filename
, 1);
430 char *uri
= go_url_resolve_relative (ref_uri
, rel_uri
);
433 wb
= gnm_app_workbook_get_by_uri (uri
);
445 struct wb_uri_closure
{
451 cb_workbook_uri (Workbook
* wb
, gpointer closure
)
453 struct wb_uri_closure
*dat
= closure
;
454 char const *wb_uri
= go_doc_get_uri (GO_DOC (wb
));
456 if (wb_uri
&& strcmp (wb_uri
, dat
->uri
) == 0) {
464 gnm_app_workbook_foreach (GnmWbIterFunc cback
, gpointer data
);
467 gnm_app_workbook_get_by_uri (char const *uri
)
469 struct wb_uri_closure closure
;
471 g_return_val_if_fail (uri
!= NULL
, NULL
);
475 gnm_app_workbook_foreach (&cb_workbook_uri
, &closure
);
481 gnm_app_workbook_foreach (GnmWbIterFunc cback
, gpointer data
)
485 g_return_val_if_fail (app
!= NULL
, FALSE
);
487 for (l
= app
->workbook_list
; l
; l
= l
->next
){
488 Workbook
*wb
= l
->data
;
490 if (!(*cback
)(wb
, data
))
497 * gnm_app_workbook_get_by_index:
500 * Get nth workbook. Index is zero-based.
501 * Return value: (nullable) (transfer none): the nth workbook, if any.
504 gnm_app_workbook_get_by_index (int i
)
506 return g_list_nth_data (app
->workbook_list
, i
);
510 gnm_app_display_dpi_get (gboolean horizontal
)
513 ? gnm_conf_get_core_gui_screen_horizontaldpi ()
514 : gnm_conf_get_core_gui_screen_verticaldpi ();
518 gnm_app_dpi_to_pixels (void)
520 return MIN (gnm_app_display_dpi_get (TRUE
),
521 gnm_app_display_dpi_get (FALSE
)) / 72.;
526 * gnm_app_create_opener_filter:
527 * @openers: (element-type GOFileOpener): a list of file openers.
529 * Creates a #GtkFileFilter from the list of file types supported by the
530 * openers in the list.
531 * Returns: (transfer full): the newly allocated #GtkFileFilter.
534 gnm_app_create_opener_filter (GList
*openers
)
537 static const char *const bad_suffixes
[] = {
544 GtkFileFilter
*filter
= gtk_file_filter_new ();
545 gboolean for_history
= (openers
== NULL
);
548 openers
= go_get_file_openers ();
550 for (; openers
; openers
= openers
->next
) {
551 GOFileOpener
*opener
= openers
->data
;
552 if (opener
!= NULL
) {
553 const GSList
*mimes
= go_file_opener_get_mimes (opener
);
554 const GSList
*suffixes
= go_file_opener_get_suffixes (opener
);
558 const char *mime
= mimes
->data
;
560 * See 438918. Too many things
561 * like *.xml and *.txt get added
562 * to be useful for the file history
564 gtk_file_filter_add_mime_type (filter
, mime
);
566 g_print ("%s: Adding mime %s\n", go_file_opener_get_description (opener
), mime
);
571 const char *suffix
= suffixes
->data
;
576 for (i
= 0; bad_suffixes
[i
]; i
++)
577 if (strcmp (suffix
, bad_suffixes
[i
]) == 0)
580 /* Create "*.[xX][lL][sS]" */
581 pattern
= g_string_new ("*.");
583 gunichar uc
= g_utf8_get_char (suffix
);
584 suffix
= g_utf8_next_char (suffix
);
585 if (g_unichar_islower (uc
)) {
586 g_string_append_c (pattern
, '[');
587 g_string_append_unichar (pattern
, uc
);
588 uc
= g_unichar_toupper (uc
);
589 g_string_append_unichar (pattern
, uc
);
590 g_string_append_c (pattern
, ']');
592 g_string_append_unichar (pattern
, uc
);
595 gtk_file_filter_add_pattern (filter
, pattern
->str
);
597 g_print ("%s: Adding %s\n", go_file_opener_get_description (opener
), pattern
->str
);
598 g_string_free (pattern
, TRUE
);
601 suffixes
= suffixes
->next
;
609 compare_mru (GtkRecentInfo
*a
, GtkRecentInfo
*b
)
611 time_t ta
= MAX (gtk_recent_info_get_visited (a
), gtk_recent_info_get_modified (a
));
612 time_t tb
= MAX (gtk_recent_info_get_visited (b
), gtk_recent_info_get_modified (b
));
618 * gnm_app_history_get_list:
620 * creating it if necessary.
622 * Return value: (element-type utf8) (transfer full): the list, which must be
623 * freed along with the strings in it.
626 gnm_app_history_get_list (int max_elements
)
630 GtkFileFilter
*filter
;
633 if (app
->recent
== NULL
)
636 items
= gtk_recent_manager_get_items (app
->recent
);
637 items
= g_list_sort (items
, (GCompareFunc
)compare_mru
);
639 filter
= gnm_app_create_opener_filter (NULL
);
641 for (l
= items
; l
&& n_elements
< max_elements
; l
= l
->next
) {
642 GtkRecentInfo
*ri
= l
->data
;
643 const char *uri
= gtk_recent_info_get_uri (ri
);
646 if (gtk_recent_info_has_application (ri
, g_get_application_name ())) {
649 GtkFileFilterInfo fi
;
650 char *display_name
= g_filename_display_basename (uri
);
652 memset (&fi
, 0, sizeof (fi
));
653 fi
.contains
= (GTK_FILE_FILTER_MIME_TYPE
|
654 GTK_FILE_FILTER_URI
|
655 GTK_FILE_FILTER_DISPLAY_NAME
);
657 fi
.mime_type
= gtk_recent_info_get_mime_type (ri
);
658 fi
.display_name
= display_name
;
659 want_it
= gtk_file_filter_filter (filter
, &fi
);
660 g_free (display_name
);
664 char *filename
= go_filename_from_uri (uri
);
665 if (filename
&& !g_file_test (filename
, G_FILE_TEST_EXISTS
))
671 res
= g_slist_prepend (res
, g_strdup (uri
));
676 g_list_free_full (items
, (GDestroyNotify
)gtk_recent_info_unref
);
677 g_object_ref_sink (filter
);
678 g_object_unref (filter
);
680 return g_slist_reverse (res
);
684 * application_history_update_list:
686 * @mimetype: (nullable): the mime type for @uri
688 * Adds @uri to the application's history of files.
691 gnm_app_history_add (char const *uri
, const char *mimetype
)
695 if (app
->recent
== NULL
)
698 memset (&rd
, 0, sizeof (rd
));
701 g_print ("uri: %s\nmime: %s\n\n", uri
, mimetype
? mimetype
: "-");
705 g_strdup (mimetype
? mimetype
: "application/octet-stream");
707 rd
.app_name
= g_strdup (g_get_application_name ());
708 rd
.app_exec
= g_strjoin (" ", g_get_prgname (), "%u", NULL
);
710 rd
.is_private
= FALSE
;
712 if (!gtk_recent_manager_add_full (app
->recent
, uri
, &rd
)) {
714 g_printerr ("Warning: failed to update recent document.\n");
717 g_free (rd
.mime_type
);
718 g_free (rd
.app_name
);
719 g_free (rd
.app_exec
);
721 g_object_notify (G_OBJECT (app
), "file-history-list");
725 cb_recent_changed (G_GNUC_UNUSED GtkRecentManager
*recent
, GnmApp
*app
)
727 g_object_notify (G_OBJECT (app
), "file-history-list");
731 gnm_app_finalize (GObject
*obj
)
733 GnmApp
*application
= GNM_APP (obj
);
735 g_free (application
->clipboard_cut_range
);
736 application
->clipboard_cut_range
= NULL
;
738 application
->recent
= NULL
;
740 if (app
== application
)
743 G_OBJECT_CLASS (parent_klass
)->finalize (obj
);
747 gnm_app_get_property (GObject
*obj
, guint param_id
,
748 GValue
*value
, GParamSpec
*pspec
)
751 GnmApp
*application
= GNM_APP (obj
);
755 case PROP_HISTORY_LIST
:
756 g_value_set_pointer (value
, gnm_app_history_get_list (G_MAXINT
));
758 case PROP_SHUTTING_DOWN
:
759 g_value_set_boolean (value
, gnm_app_shutting_down ());
761 case PROP_INITIAL_OPEN_COMPLETE
:
762 g_value_set_boolean (value
, gnm_app_initial_open_complete ());
764 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj
, param_id
, pspec
);
770 gnm_app_set_property (GObject
*object
, guint property_id
,
771 GValue
const *value
, GParamSpec
*pspec
)
773 GnmApp
*app
= (GnmApp
*)object
;
775 switch (property_id
) {
776 case PROP_SHUTTING_DOWN
:
777 app
->shutting_down
= g_value_get_boolean (value
);
779 case PROP_INITIAL_OPEN_COMPLETE
:
780 app
->initial_open_complete
= g_value_get_boolean (value
);
783 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
789 gnm_app_class_init (GObjectClass
*gobject_klass
)
791 parent_klass
= g_type_class_peek_parent (gobject_klass
);
793 /* Object class method overrides */
794 gobject_klass
->finalize
= gnm_app_finalize
;
795 gobject_klass
->get_property
= gnm_app_get_property
;
796 gobject_klass
->set_property
= gnm_app_set_property
;
798 g_object_class_install_property (gobject_klass
, PROP_HISTORY_LIST
,
799 g_param_spec_pointer ("file-history-list",
800 P_("File History List"),
801 P_("A list of filenames that have been read recently"),
802 GSF_PARAM_STATIC
| G_PARAM_READABLE
));
803 g_object_class_install_property (gobject_klass
, PROP_SHUTTING_DOWN
,
804 g_param_spec_boolean ("shutting-down",
806 P_("In the process of shutting down?"),
808 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
809 g_object_class_install_property (gobject_klass
, PROP_INITIAL_OPEN_COMPLETE
,
810 g_param_spec_boolean ("initial-open-complete",
811 P_("Initial Open Complete"),
812 P_("All command-line files open?"),
814 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
817 signals
[WORKBOOK_ADDED
] = g_signal_new ("workbook_added",
820 G_STRUCT_OFFSET (GnmAppClass
, workbook_added
),
822 g_cclosure_marshal_VOID__OBJECT
,
823 G_TYPE_NONE
, 1, GNM_WORKBOOK_TYPE
);
824 signals
[WORKBOOK_REMOVED
] = g_signal_new ("workbook_removed",
827 G_STRUCT_OFFSET (GnmAppClass
, workbook_removed
),
829 g_cclosure_marshal_VOID__POINTER
,
830 G_TYPE_NONE
, 1, G_TYPE_POINTER
);
831 signals
[WINDOW_LIST_CHANGED
] = g_signal_new ("window-list-changed",
834 G_STRUCT_OFFSET (GnmAppClass
, window_list_changed
),
836 g_cclosure_marshal_VOID__VOID
,
838 signals
[CUSTOM_UI_ADDED
] = g_signal_new ("custom-ui-added",
841 G_STRUCT_OFFSET (GnmAppClass
, custom_ui_added
),
843 g_cclosure_marshal_VOID__POINTER
,
844 G_TYPE_NONE
, 1, G_TYPE_POINTER
);
845 signals
[CUSTOM_UI_REMOVED
] = g_signal_new ("custom-ui-removed",
848 G_STRUCT_OFFSET (GnmAppClass
, custom_ui_removed
),
850 g_cclosure_marshal_VOID__POINTER
,
851 G_TYPE_NONE
, 1, G_TYPE_POINTER
);
852 signals
[CLIPBOARD_MODIFIED
] = g_signal_new ("clipboard_modified",
855 G_STRUCT_OFFSET (GnmAppClass
, clipboard_modified
),
857 g_cclosure_marshal_VOID__VOID
,
859 signals
[RECALC_FINISHED
] = g_signal_new ("recalc_finished",
862 G_STRUCT_OFFSET (GnmAppClass
, recalc_finished
),
864 g_cclosure_marshal_VOID__VOID
,
866 signals
[RECALC_FINISHED
] = g_signal_new ("recalc_clear_caches",
869 G_STRUCT_OFFSET (GnmAppClass
, recalc_clear_caches
),
871 g_cclosure_marshal_VOID__VOID
,
876 gnm_app_init (GObject
*obj
)
878 GnmApp
*gnm_app
= GNM_APP (obj
);
880 gnm_app
->clipboard_copied_contents
= NULL
;
881 gnm_app
->clipboard_sheet_view
= NULL
;
883 gnm_app
->workbook_list
= NULL
;
885 if (gdk_display_get_default ()) {
887 * Only allocate a GtkRecentManager if we have a gui.
888 * This is, in part, because it currently throws an error.
891 gnm_app
->recent
= gtk_recent_manager_get_default ();
892 g_signal_connect_object (G_OBJECT (gnm_app
->recent
),
894 G_CALLBACK (cb_recent_changed
),
901 GSF_CLASS (GnmApp
, gnm_app
,
902 gnm_app_class_init
, gnm_app_init
,
905 /**********************************************************************/
906 static GSList
*extra_uis
= NULL
;
913 * @always_available: whether the action should always be available.
914 * @handler: (scope async): the handler.
916 * Returns: (transfer full): the newly allocated #GnmAction.
919 gnm_action_new (char const *id
, char const *label
,
920 char const *icon_name
, gboolean always_available
,
921 GnmActionHandler handler
)
923 GnmAction
*res
= g_new0 (GnmAction
, 1);
924 res
->id
= g_strdup (id
);
925 res
->label
= g_strdup (label
);
926 res
->icon_name
= g_strdup (icon_name
);
927 res
->always_available
= always_available
;
928 res
->handler
= handler
;
933 gnm_action_free (GnmAction
*action
)
935 if (NULL
!= action
) {
937 g_free (action
->label
);
938 g_free (action
->icon_name
);
944 gnm_action_copy (GnmAction
const *action
)
946 return gnm_action_new (action
->id
, action
->label
, action
->icon_name
,
947 action
->always_available
, action
->handler
);
951 gnm_action_get_type (void)
956 t
= g_boxed_type_register_static ("GnmAction",
957 (GBoxedCopyFunc
)gnm_action_copy
,
958 (GBoxedFreeFunc
)gnm_action_free
);
964 * making GnmAppExtraUI a boxed type for introspection. copy and free don't do
965 anything which might be critical, crossing fingers.*/
967 static GnmAppExtraUI
*
968 gnm_app_extra_ui_ref (GnmAppExtraUI
*ui
)
974 static GnmAppExtraUI
*
975 gnm_app_extra_ui_unref (GnmAppExtraUI
*ui
)
982 gnm_app_extra_ui_get_type (void)
987 t
= g_boxed_type_register_static ("GnmAppExtraUI",
988 (GBoxedCopyFunc
)gnm_app_extra_ui_ref
,
989 (GBoxedFreeFunc
)gnm_app_extra_ui_unref
);
995 * gnm_app_add_extra_ui:
996 * @group_name: action group name.
997 * @actions: (element-type GnmAction): list of actions.
998 * @layout: the xml string describing the menus and toolbars.
999 * @domain: localization domain.
1000 * @user_data: user data
1002 * Returns: (transfer full): the newly allocated #GnmAppExtraUI.
1005 gnm_app_add_extra_ui (char const *group_name
,
1011 GnmAppExtraUI
*extra_ui
= g_new0 (GnmAppExtraUI
, 1);
1012 extra_uis
= g_slist_prepend (extra_uis
, extra_ui
);
1013 extra_ui
->group_name
= g_strdup (group_name
);
1014 extra_ui
->actions
= actions
;
1015 extra_ui
->layout
= g_strdup (layout
);
1016 extra_ui
->user_data
= user_data
;
1017 g_signal_emit (G_OBJECT (app
), signals
[CUSTOM_UI_ADDED
], 0, extra_ui
);
1018 if (gnm_debug_flag ("extra-ui"))
1019 g_printerr ("Adding extra ui [%s] %p\n", group_name
, extra_ui
);
1024 gnm_app_remove_extra_ui (GnmAppExtraUI
*extra_ui
)
1026 if (gnm_debug_flag ("extra-ui"))
1027 g_printerr ("Removing extra ui %p\n", extra_ui
);
1028 extra_uis
= g_slist_remove (extra_uis
, extra_ui
);
1029 g_signal_emit (G_OBJECT (app
), signals
[CUSTOM_UI_REMOVED
], 0, extra_ui
);
1030 g_free (extra_ui
->group_name
);
1031 g_free (extra_ui
->layout
);
1036 * gnm_app_foreach_extra_ui:
1037 * @func: (scope call): #GFunc
1040 * Applies @func to each #GnmAppExtraUI.
1043 gnm_app_foreach_extra_ui (GFunc func
, gpointer data
)
1045 g_slist_foreach (extra_uis
, func
, data
);
1048 /**********************************************************************/
1050 static gint windows_update_timer
= 0;
1052 cb_flag_windows_changed (void)
1055 g_signal_emit (G_OBJECT (app
), signals
[WINDOW_LIST_CHANGED
], 0);
1056 windows_update_timer
= 0;
1061 * _gnm_app_flag_windows_changed:
1063 * An internal utility routine to flag a regeneration of the window lists
1066 _gnm_app_flag_windows_changed (void)
1068 if (windows_update_timer
== 0)
1069 windows_update_timer
= g_timeout_add (100,
1070 (GSourceFunc
)cb_flag_windows_changed
, NULL
);
1073 /**********************************************************************/
1078 * Recalculate everything dirty in all workbooks that have automatic
1082 gnm_app_recalc (void)
1086 g_return_if_fail (app
!= NULL
);
1088 gnm_app_recalc_start ();
1090 for (l
= app
->workbook_list
; l
; l
= l
->next
) {
1091 Workbook
*wb
= l
->data
;
1093 if (workbook_get_recalcmode (wb
))
1094 workbook_recalc (wb
);
1097 gnm_app_recalc_finish ();
1101 gnm_app_recalc_start (void)
1103 g_return_if_fail (app
->recalc_count
>= 0);
1104 app
->recalc_count
++;
1108 gnm_app_recalc_finish (void)
1110 g_return_if_fail (app
->recalc_count
> 0);
1111 app
->recalc_count
--;
1112 if (app
->recalc_count
== 0) {
1113 gnm_app_recalc_clear_caches ();
1114 g_signal_emit_by_name (gnm_app_get_app (), "recalc-finished");
1119 gnm_app_recalc_clear_caches (void)
1121 g_signal_emit_by_name (gnm_app_get_app (), "recalc-clear-caches");
1125 gnm_app_shutting_down (void)
1127 return app
->shutting_down
;
1131 gnm_app_initial_open_complete (void)
1133 return app
->initial_open_complete
;