2 * application.c: Manage the data common to all workbooks
5 * Jody Goldberg <jody@gnome.org>
7 #include <gnumeric-config.h>
10 #include <application.h>
12 #include <clipboard.h>
13 #include <selection.h>
14 #include <workbook-control.h>
15 #include <workbook-view.h>
18 #include <sheet-view.h>
19 #include <sheet-private.h>
20 #include <sheet-object.h>
23 #include <sheet-object.h>
25 #include <gui-clipboard.h>
26 #include <expr-name.h>
27 #include <workbook-priv.h>
29 #include <gnumeric-conf.h>
30 #include <goffice/goffice.h>
31 #include <gsf/gsf-impl-utils.h>
34 #define GNM_APP(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GNM_APP_TYPE, GnmApp))
35 #define GNM_IS_APP(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GNM_APP_TYPE))
41 PROP_INITIAL_OPEN_COMPLETE
56 static guint signals
[LAST_SIGNAL
] = { 0 };
62 SheetView
*clipboard_sheet_view
;
63 GnmCellRegion
*clipboard_copied_contents
;
64 GnmRange
*clipboard_cut_range
;
68 /* Recalculation manager. */
71 GtkRecentManager
*recent
;
74 gboolean shutting_down
;
75 gboolean initial_open_complete
;
81 void (*workbook_added
) (GnmApp
*gnm_app
, Workbook
*wb
);
82 void (*workbook_removed
) (GnmApp
*gnm_app
, Workbook
*wb
);
83 void (*window_list_changed
) (GnmApp
*gnm_app
);
84 void (*custom_ui_added
) (GnmApp
*gnm_app
, GnmAppExtraUI
*ui
);
85 void (*custom_ui_removed
) (GnmApp
*gnm_app
, GnmAppExtraUI
*ui
);
86 void (*clipboard_modified
) (GnmApp
*gnm_app
);
87 void (*recalc_finished
) (GnmApp
*gnm_app
);
88 void (*recalc_clear_caches
) (GnmApp
*gnm_app
);
91 static GObjectClass
*parent_klass
;
94 static Workbook
*gnm_app_workbook_get_by_uri (char const *uri
);
99 * Returns: (transfer none): the #GnmApp instance.
102 gnm_app_get_app (void)
104 return G_OBJECT (app
);
108 * gnm_app_workbook_list_add:
111 * Add @wb to the application's list of workbooks.
114 gnm_app_workbook_list_add (Workbook
*wb
)
116 g_return_if_fail (GNM_IS_WORKBOOK (wb
));
117 g_return_if_fail (app
!= NULL
);
119 app
->workbook_list
= g_list_prepend (app
->workbook_list
, wb
);
120 g_signal_connect (G_OBJECT (wb
),
122 G_CALLBACK (gnm_app_flag_windows_changed_
), NULL
);
123 gnm_app_flag_windows_changed_ ();
124 g_signal_emit (G_OBJECT (app
), signals
[WORKBOOK_ADDED
], 0, wb
);
128 * gnm_app_workbook_list_remove:
131 * Remove @wb from the application's list of workbooks.
134 gnm_app_workbook_list_remove (Workbook
*wb
)
136 g_return_if_fail (wb
!= NULL
);
137 g_return_if_fail (app
!= NULL
);
139 app
->workbook_list
= g_list_remove (app
->workbook_list
, wb
);
140 g_signal_handlers_disconnect_by_func (G_OBJECT (wb
),
141 G_CALLBACK (gnm_app_flag_windows_changed_
), NULL
);
142 gnm_app_flag_windows_changed_ ();
143 g_signal_emit (G_OBJECT (app
), signals
[WORKBOOK_REMOVED
], 0, wb
);
147 * gnm_app_workbook_list:
149 * Returns: (element-type Workbook) (transfer none): the workbook list.
152 gnm_app_workbook_list (void)
154 g_return_val_if_fail (app
!= NULL
, NULL
);
156 return app
->workbook_list
;
160 gnm_app_sanity_check (void)
163 gboolean err
= FALSE
;
164 for (l
= gnm_app_workbook_list (); l
; l
= l
->next
) {
165 Workbook
*wb
= l
->data
;
166 if (gnm_named_expr_collection_sanity_check (wb
->names
, "workbook"))
170 g_error ("Sanity check failed\n");
176 * gnm_app_clipboard_clear:
178 * Clear and free the contents of the clipboard if it is
182 gnm_app_clipboard_clear (gboolean drop_selection
)
184 g_return_if_fail (app
!= NULL
);
186 if (app
->clipboard_copied_contents
) {
187 cellregion_unref (app
->clipboard_copied_contents
);
188 app
->clipboard_copied_contents
= NULL
;
190 if (app
->clipboard_sheet_view
!= NULL
) {
191 gnm_sheet_view_unant (app
->clipboard_sheet_view
);
193 g_signal_emit (G_OBJECT (app
), signals
[CLIPBOARD_MODIFIED
], 0);
195 gnm_sheet_view_weak_unref (&(app
->clipboard_sheet_view
));
197 /* Release the selection */
199 gnm_x_disown_clipboard ();
204 gnm_app_clipboard_invalidate_sheet (Sheet
*sheet
)
206 /* Clear the cliboard to avoid dangling references to the deleted sheet */
207 if (sheet
== gnm_app_clipboard_sheet_get ())
208 gnm_app_clipboard_clear (TRUE
);
209 else if (app
->clipboard_copied_contents
)
210 cellregion_invalidate_sheet (app
->clipboard_copied_contents
, sheet
);
214 gnm_app_clipboard_unant (void)
216 g_return_if_fail (app
!= NULL
);
218 if (app
->clipboard_sheet_view
!= NULL
)
219 gnm_sheet_view_unant (app
->clipboard_sheet_view
);
223 * gnm_app_clipboard_cut_copy:
224 * @wbc: the workbook control that requested the operation.
225 * @is_cut: is this a cut or a copy.
226 * @sv: The source sheet for the copy.
227 * @area: A single rectangular range to be copied.
228 * @animate_range: Do we want ot add an animated cursor around things.
231 * Clear and free the contents of the clipboard and save the sheet and area
232 * to be cut. DO NOT ACTUALLY CUT! Paste will move the region if this was a
236 * Clear and free the contents of the clipboard and COPY the designated region
237 * into the clipboard.
239 * we need to pass @wbc as a control rather than a simple command-context so
240 * that the control can claim the selection.
243 gnm_app_clipboard_cut_copy (WorkbookControl
*wbc
, gboolean is_cut
,
244 SheetView
*sv
, GnmRange
const *area
,
245 gboolean animate_cursor
)
249 g_return_if_fail (GNM_IS_SHEET_VIEW (sv
));
250 g_return_if_fail (area
!= NULL
);
251 g_return_if_fail (app
!= NULL
);
253 gnm_app_clipboard_clear (FALSE
);
254 sheet
= sv_sheet (sv
);
255 g_free (app
->clipboard_cut_range
);
256 app
->clipboard_cut_range
= gnm_range_dup (area
);
257 gnm_sheet_view_weak_ref (sv
, &(app
->clipboard_sheet_view
));
260 app
->clipboard_copied_contents
=
261 clipboard_copy_range (sheet
, area
);
262 if (animate_cursor
) {
263 GList
*l
= g_list_append (NULL
, (gpointer
)area
);
264 gnm_sheet_view_ant (sv
, l
);
270 } else if (wb_control_claim_selection (wbc
)) {
271 g_signal_emit (G_OBJECT (app
), signals
[CLIPBOARD_MODIFIED
], 0);
273 gnm_app_clipboard_clear (FALSE
);
274 g_warning ("Unable to set selection?");
279 * gnm_app_clipboard_cut_copy_obj:
280 * @wbc: #WorkbookControl
281 * @is_cut: %TRUE for cut, %FALSE for copy
283 * @objects: (element-type SheetObject) (transfer container): a list
286 * Different than copying/cutting a region, this can actually cut an object
289 gnm_app_clipboard_cut_copy_obj (WorkbookControl
*wbc
, gboolean is_cut
,
290 SheetView
*sv
, GSList
*objects
)
292 g_return_if_fail (GNM_IS_SHEET_VIEW (sv
));
293 g_return_if_fail (objects
!= NULL
);
294 g_return_if_fail (app
!= NULL
);
296 gnm_app_clipboard_clear (FALSE
);
297 g_free (app
->clipboard_cut_range
);
298 app
->clipboard_cut_range
= NULL
;
299 gnm_sheet_view_weak_ref (sv
, &(app
->clipboard_sheet_view
));
300 app
->clipboard_copied_contents
301 = clipboard_copy_obj (sv_sheet (sv
), objects
);
303 cmd_objects_delete (wbc
, objects
, _("Cut Object"));
306 if (wb_control_claim_selection (wbc
)) {
307 g_signal_emit (G_OBJECT (app
), signals
[CLIPBOARD_MODIFIED
], 0);
309 gnm_app_clipboard_clear (FALSE
);
310 g_warning ("Unable to set selection?");
312 g_slist_free (objects
);
316 gnm_app_clipboard_is_empty (void)
318 g_return_val_if_fail (app
!= NULL
, TRUE
);
320 return app
->clipboard_sheet_view
== NULL
;
324 gnm_app_clipboard_is_cut (void)
326 g_return_val_if_fail (app
!= NULL
, FALSE
);
328 if (app
->clipboard_sheet_view
!= NULL
)
329 return app
->clipboard_copied_contents
? FALSE
: TRUE
;
334 * gnm_app_clipboard_sheet_get:
336 * Returns: (transfer none) (nullable): the current clipboard #Sheet.
339 gnm_app_clipboard_sheet_get (void)
341 g_return_val_if_fail (app
!= NULL
, NULL
);
343 if (app
->clipboard_sheet_view
== NULL
)
345 return sv_sheet (app
->clipboard_sheet_view
);
349 * gnm_app_clipboard_sheet_view_get:
351 * Returns: (transfer none) (nullable): the current clipboard #SheetView.
354 gnm_app_clipboard_sheet_view_get (void)
356 g_return_val_if_fail (app
!= NULL
, NULL
);
357 return app
->clipboard_sheet_view
;
361 * gnm_app_clipboard_contents_get:
363 * Returns: (nullable) (transfer none): the current contents of the clipboard.
366 gnm_app_clipboard_contents_get (void)
368 g_return_val_if_fail (app
!= NULL
, NULL
);
369 return app
->clipboard_copied_contents
;
373 * gnm_app_clipboard_area_get:
375 * Returns: (nullable) (transfer none): the current range in the clipboard.
378 gnm_app_clipboard_area_get (void)
380 g_return_val_if_fail (app
!= NULL
, NULL
);
382 * Only return the range if the sheet has been set.
383 * The range will still contain data even after
384 * the clipboard has been cleared so we need to be extra
385 * safe and only return a range if there is a valid selection
387 if (app
->clipboard_sheet_view
!= NULL
)
388 return app
->clipboard_cut_range
;
393 * gnm_app_workbook_get_by_name:
394 * @name: the workbook name.
397 * Returns: (transfer none): the #Workbook or %NULL.
400 gnm_app_workbook_get_by_name (char const *name
,
404 char *filename
= NULL
;
406 if (name
== NULL
|| *name
== 0)
410 wb
= gnm_app_workbook_get_by_uri (name
);
414 filename
= g_filename_from_utf8 (name
, -1, NULL
, NULL
, NULL
);
416 /* Try as absolute filename. */
417 if (filename
&& g_path_is_absolute (filename
)) {
418 char *uri
= go_filename_to_uri (filename
);
420 wb
= gnm_app_workbook_get_by_uri (uri
);
427 if (filename
&& ref_uri
) {
428 char *rel_uri
= go_url_encode (filename
, 1);
429 char *uri
= go_url_resolve_relative (ref_uri
, rel_uri
);
432 wb
= gnm_app_workbook_get_by_uri (uri
);
444 struct wb_uri_closure
{
450 cb_workbook_uri (Workbook
* wb
, gpointer closure
)
452 struct wb_uri_closure
*dat
= closure
;
453 char const *wb_uri
= go_doc_get_uri (GO_DOC (wb
));
455 if (wb_uri
&& strcmp (wb_uri
, dat
->uri
) == 0) {
462 typedef gboolean (*GnmWbIterFunc
) (Workbook
*wb
, gpointer data
);
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 notified): the handler.
915 * @data: user data for @handler
916 * @notify: destroy notification for @data
918 * Returns: (transfer full): the newly allocated #GnmAction.
921 gnm_action_new (char const *id
, char const *label
,
922 char const *icon_name
, gboolean always_available
,
923 GnmActionHandler handler
,
924 gpointer data
, GDestroyNotify notify
)
926 GnmAction
*res
= g_new0 (GnmAction
, 1);
928 res
->id
= g_strdup (id
);
929 res
->label
= g_strdup (label
);
930 res
->icon_name
= g_strdup (icon_name
);
931 res
->always_available
= always_available
;
932 res
->handler
= handler
;
934 res
->notify
= notify
;
940 * @action: (transfer full) (nullable): #GnmAction
943 gnm_action_unref (GnmAction
*action
)
945 if (!action
|| action
->ref_count
-- > 1)
949 action
->notify (action
->data
);
952 g_free (action
->label
);
953 g_free (action
->icon_name
);
959 * @action: (transfer none) (nullable): #GnmAction
961 * Returns: (transfer full) (nullable): a new reference to @action.
964 gnm_action_ref (GnmAction
*action
)
972 gnm_action_get_type (void)
977 t
= g_boxed_type_register_static ("GnmAction",
978 (GBoxedCopyFunc
)gnm_action_ref
,
979 (GBoxedFreeFunc
)gnm_action_unref
);
985 * making GnmAppExtraUI a boxed type for introspection. copy and free don't do
986 anything which might be critical, crossing fingers.*/
988 static GnmAppExtraUI
*
989 gnm_app_extra_ui_ref (GnmAppExtraUI
*ui
)
995 static GnmAppExtraUI
*
996 gnm_app_extra_ui_unref (GnmAppExtraUI
*ui
)
1003 gnm_app_extra_ui_get_type (void)
1008 t
= g_boxed_type_register_static ("GnmAppExtraUI",
1009 (GBoxedCopyFunc
)gnm_app_extra_ui_ref
,
1010 (GBoxedFreeFunc
)gnm_app_extra_ui_unref
);
1016 * gnm_app_add_extra_ui:
1017 * @group_name: action group name.
1018 * @actions: (element-type GnmAction): list of actions.
1019 * @layout: the xml string describing the menus and toolbars.
1020 * @domain: localization domain.
1022 * Returns: (transfer full): the newly allocated #GnmAppExtraUI.
1025 gnm_app_add_extra_ui (char const *group_name
,
1030 GnmAppExtraUI
*extra_ui
= g_new0 (GnmAppExtraUI
, 1);
1031 extra_uis
= g_slist_prepend (extra_uis
, extra_ui
);
1032 extra_ui
->group_name
= g_strdup (group_name
);
1033 extra_ui
->actions
= actions
;
1034 extra_ui
->layout
= g_strdup (layout
);
1035 g_signal_emit (G_OBJECT (app
), signals
[CUSTOM_UI_ADDED
], 0, extra_ui
);
1036 if (gnm_debug_flag ("extra-ui"))
1037 g_printerr ("Adding extra ui [%s] %p\n", group_name
, extra_ui
);
1042 gnm_app_remove_extra_ui (GnmAppExtraUI
*extra_ui
)
1044 if (gnm_debug_flag ("extra-ui"))
1045 g_printerr ("Removing extra ui %p\n", extra_ui
);
1046 extra_uis
= g_slist_remove (extra_uis
, extra_ui
);
1047 g_signal_emit (G_OBJECT (app
), signals
[CUSTOM_UI_REMOVED
], 0, extra_ui
);
1048 g_free (extra_ui
->group_name
);
1049 g_free (extra_ui
->layout
);
1054 * gnm_app_foreach_extra_ui:
1055 * @func: (scope call): #GFunc
1058 * Applies @func to each #GnmAppExtraUI.
1061 gnm_app_foreach_extra_ui (GFunc func
, gpointer data
)
1063 g_slist_foreach (extra_uis
, func
, data
);
1066 /**********************************************************************/
1068 static gint windows_update_timer
= 0;
1070 cb_flag_windows_changed (void)
1073 g_signal_emit (G_OBJECT (app
), signals
[WINDOW_LIST_CHANGED
], 0);
1074 windows_update_timer
= 0;
1079 * gnm_app_flag_windows_changed_:
1081 * An internal utility routine to flag a regeneration of the window lists
1084 gnm_app_flag_windows_changed_ (void)
1086 if (windows_update_timer
== 0)
1087 windows_update_timer
= g_timeout_add (100,
1088 (GSourceFunc
)cb_flag_windows_changed
, NULL
);
1091 /**********************************************************************/
1096 * Recalculate everything dirty in all workbooks that have automatic
1100 gnm_app_recalc (void)
1104 g_return_if_fail (app
!= NULL
);
1106 gnm_app_recalc_start ();
1108 for (l
= app
->workbook_list
; l
; l
= l
->next
) {
1109 Workbook
*wb
= l
->data
;
1111 if (workbook_get_recalcmode (wb
))
1112 workbook_recalc (wb
);
1115 gnm_app_recalc_finish ();
1119 gnm_app_recalc_start (void)
1121 g_return_if_fail (app
->recalc_count
>= 0);
1122 app
->recalc_count
++;
1126 gnm_app_recalc_finish (void)
1128 g_return_if_fail (app
->recalc_count
> 0);
1129 app
->recalc_count
--;
1130 if (app
->recalc_count
== 0) {
1131 gnm_app_recalc_clear_caches ();
1132 g_signal_emit_by_name (gnm_app_get_app (), "recalc-finished");
1137 gnm_app_recalc_clear_caches (void)
1139 g_signal_emit_by_name (gnm_app_get_app (), "recalc-clear-caches");
1143 gnm_app_shutting_down (void)
1145 return app
->shutting_down
;
1149 gnm_app_initial_open_complete (void)
1151 return app
->initial_open_complete
;