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"
24 #include "sheet-object.h"
26 #include "gui-clipboard.h"
27 #include "expr-name.h"
28 #include "workbook-priv.h"
30 #include <gnumeric-conf.h>
31 #include <goffice/goffice.h>
32 #include <gsf/gsf-impl-utils.h>
36 #define GNM_APP(o) (G_TYPE_CHECK_INSTANCE_CAST((o), GNM_APP_TYPE, GnmApp))
37 #define GNM_IS_APP(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), GNM_APP_TYPE))
43 PROP_INITIAL_OPEN_COMPLETE
58 static guint signals
[LAST_SIGNAL
] = { 0 };
64 SheetView
*clipboard_sheet_view
;
65 GnmCellRegion
*clipboard_copied_contents
;
66 GnmRange
*clipboard_cut_range
;
70 /* Recalculation manager. */
73 GtkRecentManager
*recent
;
76 gboolean shutting_down
;
77 gboolean initial_open_complete
;
83 void (*workbook_added
) (GnmApp
*gnm_app
, Workbook
*wb
);
84 void (*workbook_removed
) (GnmApp
*gnm_app
, Workbook
*wb
);
85 void (*window_list_changed
) (GnmApp
*gnm_app
);
86 void (*custom_ui_added
) (GnmApp
*gnm_app
, GnmAppExtraUI
*ui
);
87 void (*custom_ui_removed
) (GnmApp
*gnm_app
, GnmAppExtraUI
*ui
);
88 void (*clipboard_modified
) (GnmApp
*gnm_app
);
89 void (*recalc_finished
) (GnmApp
*gnm_app
);
90 void (*recalc_clear_caches
) (GnmApp
*gnm_app
);
93 static GObjectClass
*parent_klass
;
96 static Workbook
*gnm_app_workbook_get_by_uri (char const *uri
);
101 * Returns: (transfer none): the #GnmApp instance.
104 gnm_app_get_app (void)
106 return G_OBJECT (app
);
110 * gnm_app_workbook_list_add:
113 * Add @wb to the application's list of workbooks.
116 gnm_app_workbook_list_add (Workbook
*wb
)
118 g_return_if_fail (GNM_IS_WORKBOOK (wb
));
119 g_return_if_fail (app
!= NULL
);
121 app
->workbook_list
= g_list_prepend (app
->workbook_list
, wb
);
122 g_signal_connect (G_OBJECT (wb
),
124 G_CALLBACK (_gnm_app_flag_windows_changed
), NULL
);
125 _gnm_app_flag_windows_changed ();
126 g_signal_emit (G_OBJECT (app
), signals
[WORKBOOK_ADDED
], 0, wb
);
130 * gnm_app_workbook_list_remove:
133 * Remove @wb from the application's list of workbooks.
136 gnm_app_workbook_list_remove (Workbook
*wb
)
138 g_return_if_fail (wb
!= NULL
);
139 g_return_if_fail (app
!= NULL
);
141 app
->workbook_list
= g_list_remove (app
->workbook_list
, wb
);
142 g_signal_handlers_disconnect_by_func (G_OBJECT (wb
),
143 G_CALLBACK (_gnm_app_flag_windows_changed
), NULL
);
144 _gnm_app_flag_windows_changed ();
145 g_signal_emit (G_OBJECT (app
), signals
[WORKBOOK_REMOVED
], 0, wb
);
149 * gnm_app_workbook_list:
151 * Returns: (element-type Workbook) (transfer none): the workbook list.
154 gnm_app_workbook_list (void)
156 g_return_val_if_fail (app
!= NULL
, NULL
);
158 return app
->workbook_list
;
162 gnm_app_sanity_check (void)
165 gboolean err
= FALSE
;
166 for (l
= gnm_app_workbook_list (); l
; l
= l
->next
) {
167 Workbook
*wb
= l
->data
;
168 if (gnm_named_expr_collection_sanity_check (wb
->names
, "workbook"))
172 g_error ("Sanity check failed\n");
178 * gnm_app_clipboard_clear:
180 * Clear and free the contents of the clipboard if it is
184 gnm_app_clipboard_clear (gboolean drop_selection
)
186 g_return_if_fail (app
!= NULL
);
188 if (app
->clipboard_copied_contents
) {
189 cellregion_unref (app
->clipboard_copied_contents
);
190 app
->clipboard_copied_contents
= NULL
;
192 if (app
->clipboard_sheet_view
!= NULL
) {
193 gnm_sheet_view_unant (app
->clipboard_sheet_view
);
195 g_signal_emit (G_OBJECT (app
), signals
[CLIPBOARD_MODIFIED
], 0);
197 gnm_sheet_view_weak_unref (&(app
->clipboard_sheet_view
));
199 /* Release the selection */
201 gnm_x_disown_clipboard ();
206 gnm_app_clipboard_invalidate_sheet (Sheet
*sheet
)
208 /* Clear the cliboard to avoid dangling references to the deleted sheet */
209 if (sheet
== gnm_app_clipboard_sheet_get ())
210 gnm_app_clipboard_clear (TRUE
);
211 else if (app
->clipboard_copied_contents
)
212 cellregion_invalidate_sheet (app
->clipboard_copied_contents
, sheet
);
216 gnm_app_clipboard_unant (void)
218 g_return_if_fail (app
!= NULL
);
220 if (app
->clipboard_sheet_view
!= NULL
)
221 gnm_sheet_view_unant (app
->clipboard_sheet_view
);
225 * gnm_app_clipboard_cut_copy:
226 * @wbc: the workbook control that requested the operation.
227 * @is_cut: is this a cut or a copy.
228 * @sv: The source sheet for the copy.
229 * @area: A single rectangular range to be copied.
230 * @animate_range: Do we want ot add an animated cursor around things.
233 * Clear and free the contents of the clipboard and save the sheet and area
234 * to be cut. DO NOT ACTUALLY CUT! Paste will move the region if this was a
238 * Clear and free the contents of the clipboard and COPY the designated region
239 * into the clipboard.
241 * we need to pass @wbc as a control rather than a simple command-context so
242 * that the control can claim the selection.
245 gnm_app_clipboard_cut_copy (WorkbookControl
*wbc
, gboolean is_cut
,
246 SheetView
*sv
, GnmRange
const *area
,
247 gboolean animate_cursor
)
251 g_return_if_fail (GNM_IS_SHEET_VIEW (sv
));
252 g_return_if_fail (area
!= NULL
);
253 g_return_if_fail (app
!= NULL
);
255 gnm_app_clipboard_clear (FALSE
);
256 sheet
= sv_sheet (sv
);
257 g_free (app
->clipboard_cut_range
);
258 app
->clipboard_cut_range
= gnm_range_dup (area
);
259 gnm_sheet_view_weak_ref (sv
, &(app
->clipboard_sheet_view
));
262 app
->clipboard_copied_contents
=
263 clipboard_copy_range (sheet
, area
);
264 if (animate_cursor
) {
265 GList
*l
= g_list_append (NULL
, (gpointer
)area
);
266 gnm_sheet_view_ant (sv
, l
);
270 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) {
463 gnm_app_workbook_foreach (GnmWbIterFunc cback
, gpointer data
);
466 gnm_app_workbook_get_by_uri (char const *uri
)
468 struct wb_uri_closure closure
;
470 g_return_val_if_fail (uri
!= NULL
, NULL
);
474 gnm_app_workbook_foreach (&cb_workbook_uri
, &closure
);
480 gnm_app_workbook_foreach (GnmWbIterFunc cback
, gpointer data
)
484 g_return_val_if_fail (app
!= NULL
, FALSE
);
486 for (l
= app
->workbook_list
; l
; l
= l
->next
){
487 Workbook
*wb
= l
->data
;
489 if (!(*cback
)(wb
, data
))
496 * gnm_app_workbook_get_by_index:
499 * Get nth workbook. Index is zero-based.
500 * Return value: (nullable) (transfer none): the nth workbook, if any.
503 gnm_app_workbook_get_by_index (int i
)
505 return g_list_nth_data (app
->workbook_list
, i
);
509 gnm_app_display_dpi_get (gboolean horizontal
)
512 ? gnm_conf_get_core_gui_screen_horizontaldpi ()
513 : gnm_conf_get_core_gui_screen_verticaldpi ();
517 gnm_app_dpi_to_pixels (void)
519 return MIN (gnm_app_display_dpi_get (TRUE
),
520 gnm_app_display_dpi_get (FALSE
)) / 72.;
525 * gnm_app_create_opener_filter:
526 * @openers: (element-type GOFileOpener): a list of file openers.
528 * Creates a #GtkFileFilter from the list of file types supported by the
529 * openers in the list.
530 * Returns: (transfer full): the newly allocated #GtkFileFilter.
533 gnm_app_create_opener_filter (GList
*openers
)
536 static const char *const bad_suffixes
[] = {
543 GtkFileFilter
*filter
= gtk_file_filter_new ();
544 gboolean for_history
= (openers
== NULL
);
547 openers
= go_get_file_openers ();
549 for (; openers
; openers
= openers
->next
) {
550 GOFileOpener
*opener
= openers
->data
;
551 if (opener
!= NULL
) {
552 const GSList
*mimes
= go_file_opener_get_mimes (opener
);
553 const GSList
*suffixes
= go_file_opener_get_suffixes (opener
);
557 const char *mime
= mimes
->data
;
559 * See 438918. Too many things
560 * like *.xml and *.txt get added
561 * to be useful for the file history
563 gtk_file_filter_add_mime_type (filter
, mime
);
565 g_print ("%s: Adding mime %s\n", go_file_opener_get_description (opener
), mime
);
570 const char *suffix
= suffixes
->data
;
575 for (i
= 0; bad_suffixes
[i
]; i
++)
576 if (strcmp (suffix
, bad_suffixes
[i
]) == 0)
579 /* Create "*.[xX][lL][sS]" */
580 pattern
= g_string_new ("*.");
582 gunichar uc
= g_utf8_get_char (suffix
);
583 suffix
= g_utf8_next_char (suffix
);
584 if (g_unichar_islower (uc
)) {
585 g_string_append_c (pattern
, '[');
586 g_string_append_unichar (pattern
, uc
);
587 uc
= g_unichar_toupper (uc
);
588 g_string_append_unichar (pattern
, uc
);
589 g_string_append_c (pattern
, ']');
591 g_string_append_unichar (pattern
, uc
);
594 gtk_file_filter_add_pattern (filter
, pattern
->str
);
596 g_print ("%s: Adding %s\n", go_file_opener_get_description (opener
), pattern
->str
);
597 g_string_free (pattern
, TRUE
);
600 suffixes
= suffixes
->next
;
608 compare_mru (GtkRecentInfo
*a
, GtkRecentInfo
*b
)
610 time_t ta
= MAX (gtk_recent_info_get_visited (a
), gtk_recent_info_get_modified (a
));
611 time_t tb
= MAX (gtk_recent_info_get_visited (b
), gtk_recent_info_get_modified (b
));
617 * gnm_app_history_get_list:
619 * creating it if necessary.
621 * Return value: (element-type utf8) (transfer full): the list, which must be
622 * freed along with the strings in it.
625 gnm_app_history_get_list (int max_elements
)
629 GtkFileFilter
*filter
;
632 if (app
->recent
== NULL
)
635 items
= gtk_recent_manager_get_items (app
->recent
);
636 items
= g_list_sort (items
, (GCompareFunc
)compare_mru
);
638 filter
= gnm_app_create_opener_filter (NULL
);
640 for (l
= items
; l
&& n_elements
< max_elements
; l
= l
->next
) {
641 GtkRecentInfo
*ri
= l
->data
;
642 const char *uri
= gtk_recent_info_get_uri (ri
);
645 if (gtk_recent_info_has_application (ri
, g_get_application_name ())) {
648 GtkFileFilterInfo fi
;
649 char *display_name
= g_filename_display_basename (uri
);
651 memset (&fi
, 0, sizeof (fi
));
652 fi
.contains
= (GTK_FILE_FILTER_MIME_TYPE
|
653 GTK_FILE_FILTER_URI
|
654 GTK_FILE_FILTER_DISPLAY_NAME
);
656 fi
.mime_type
= gtk_recent_info_get_mime_type (ri
);
657 fi
.display_name
= display_name
;
658 want_it
= gtk_file_filter_filter (filter
, &fi
);
659 g_free (display_name
);
663 char *filename
= go_filename_from_uri (uri
);
664 if (filename
&& !g_file_test (filename
, G_FILE_TEST_EXISTS
))
670 res
= g_slist_prepend (res
, g_strdup (uri
));
675 g_list_free_full (items
, (GDestroyNotify
)gtk_recent_info_unref
);
676 g_object_ref_sink (filter
);
677 g_object_unref (filter
);
679 return g_slist_reverse (res
);
683 * application_history_update_list:
685 * @mimetype: (nullable): the mime type for @uri
687 * Adds @uri to the application's history of files.
690 gnm_app_history_add (char const *uri
, const char *mimetype
)
694 if (app
->recent
== NULL
)
697 memset (&rd
, 0, sizeof (rd
));
700 g_print ("uri: %s\nmime: %s\n\n", uri
, mimetype
? mimetype
: "-");
704 g_strdup (mimetype
? mimetype
: "application/octet-stream");
706 rd
.app_name
= g_strdup (g_get_application_name ());
707 rd
.app_exec
= g_strjoin (" ", g_get_prgname (), "%u", NULL
);
709 rd
.is_private
= FALSE
;
711 if (!gtk_recent_manager_add_full (app
->recent
, uri
, &rd
)) {
713 g_printerr ("Warning: failed to update recent document.\n");
716 g_free (rd
.mime_type
);
717 g_free (rd
.app_name
);
718 g_free (rd
.app_exec
);
720 g_object_notify (G_OBJECT (app
), "file-history-list");
724 cb_recent_changed (G_GNUC_UNUSED GtkRecentManager
*recent
, GnmApp
*app
)
726 g_object_notify (G_OBJECT (app
), "file-history-list");
730 gnm_app_finalize (GObject
*obj
)
732 GnmApp
*application
= GNM_APP (obj
);
734 g_free (application
->clipboard_cut_range
);
735 application
->clipboard_cut_range
= NULL
;
737 application
->recent
= NULL
;
739 if (app
== application
)
742 G_OBJECT_CLASS (parent_klass
)->finalize (obj
);
746 gnm_app_get_property (GObject
*obj
, guint param_id
,
747 GValue
*value
, GParamSpec
*pspec
)
750 GnmApp
*application
= GNM_APP (obj
);
754 case PROP_HISTORY_LIST
:
755 g_value_set_pointer (value
, gnm_app_history_get_list (G_MAXINT
));
757 case PROP_SHUTTING_DOWN
:
758 g_value_set_boolean (value
, gnm_app_shutting_down ());
760 case PROP_INITIAL_OPEN_COMPLETE
:
761 g_value_set_boolean (value
, gnm_app_initial_open_complete ());
763 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj
, param_id
, pspec
);
769 gnm_app_set_property (GObject
*object
, guint property_id
,
770 GValue
const *value
, GParamSpec
*pspec
)
772 GnmApp
*app
= (GnmApp
*)object
;
774 switch (property_id
) {
775 case PROP_SHUTTING_DOWN
:
776 app
->shutting_down
= g_value_get_boolean (value
);
778 case PROP_INITIAL_OPEN_COMPLETE
:
779 app
->initial_open_complete
= g_value_get_boolean (value
);
782 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
788 gnm_app_class_init (GObjectClass
*gobject_klass
)
790 parent_klass
= g_type_class_peek_parent (gobject_klass
);
792 /* Object class method overrides */
793 gobject_klass
->finalize
= gnm_app_finalize
;
794 gobject_klass
->get_property
= gnm_app_get_property
;
795 gobject_klass
->set_property
= gnm_app_set_property
;
797 g_object_class_install_property (gobject_klass
, PROP_HISTORY_LIST
,
798 g_param_spec_pointer ("file-history-list",
799 P_("File History List"),
800 P_("A list of filenames that have been read recently"),
801 GSF_PARAM_STATIC
| G_PARAM_READABLE
));
802 g_object_class_install_property (gobject_klass
, PROP_SHUTTING_DOWN
,
803 g_param_spec_boolean ("shutting-down",
805 P_("In the process of shutting down?"),
807 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
808 g_object_class_install_property (gobject_klass
, PROP_INITIAL_OPEN_COMPLETE
,
809 g_param_spec_boolean ("initial-open-complete",
810 P_("Initial Open Complete"),
811 P_("All command-line files open?"),
813 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
816 signals
[WORKBOOK_ADDED
] = g_signal_new ("workbook_added",
819 G_STRUCT_OFFSET (GnmAppClass
, workbook_added
),
821 g_cclosure_marshal_VOID__OBJECT
,
822 G_TYPE_NONE
, 1, GNM_WORKBOOK_TYPE
);
823 signals
[WORKBOOK_REMOVED
] = g_signal_new ("workbook_removed",
826 G_STRUCT_OFFSET (GnmAppClass
, workbook_removed
),
828 g_cclosure_marshal_VOID__POINTER
,
829 G_TYPE_NONE
, 1, G_TYPE_POINTER
);
830 signals
[WINDOW_LIST_CHANGED
] = g_signal_new ("window-list-changed",
833 G_STRUCT_OFFSET (GnmAppClass
, window_list_changed
),
835 g_cclosure_marshal_VOID__VOID
,
837 signals
[CUSTOM_UI_ADDED
] = g_signal_new ("custom-ui-added",
840 G_STRUCT_OFFSET (GnmAppClass
, custom_ui_added
),
842 g_cclosure_marshal_VOID__POINTER
,
843 G_TYPE_NONE
, 1, G_TYPE_POINTER
);
844 signals
[CUSTOM_UI_REMOVED
] = g_signal_new ("custom-ui-removed",
847 G_STRUCT_OFFSET (GnmAppClass
, custom_ui_removed
),
849 g_cclosure_marshal_VOID__POINTER
,
850 G_TYPE_NONE
, 1, G_TYPE_POINTER
);
851 signals
[CLIPBOARD_MODIFIED
] = g_signal_new ("clipboard_modified",
854 G_STRUCT_OFFSET (GnmAppClass
, clipboard_modified
),
856 g_cclosure_marshal_VOID__VOID
,
858 signals
[RECALC_FINISHED
] = g_signal_new ("recalc_finished",
861 G_STRUCT_OFFSET (GnmAppClass
, recalc_finished
),
863 g_cclosure_marshal_VOID__VOID
,
865 signals
[RECALC_FINISHED
] = g_signal_new ("recalc_clear_caches",
868 G_STRUCT_OFFSET (GnmAppClass
, recalc_clear_caches
),
870 g_cclosure_marshal_VOID__VOID
,
875 gnm_app_init (GObject
*obj
)
877 GnmApp
*gnm_app
= GNM_APP (obj
);
879 gnm_app
->clipboard_copied_contents
= NULL
;
880 gnm_app
->clipboard_sheet_view
= NULL
;
882 gnm_app
->workbook_list
= NULL
;
884 if (gdk_display_get_default ()) {
886 * Only allocate a GtkRecentManager if we have a gui.
887 * This is, in part, because it currently throws an error.
890 gnm_app
->recent
= gtk_recent_manager_get_default ();
891 g_signal_connect_object (G_OBJECT (gnm_app
->recent
),
893 G_CALLBACK (cb_recent_changed
),
900 GSF_CLASS (GnmApp
, gnm_app
,
901 gnm_app_class_init
, gnm_app_init
,
904 /**********************************************************************/
905 static GSList
*extra_uis
= NULL
;
912 * @always_available: whether the action should always be available.
913 * @handler: (scope async): the handler.
915 * Returns: (transfer full): the newly allocated #GnmAction.
918 gnm_action_new (char const *id
, char const *label
,
919 char const *icon_name
, gboolean always_available
,
920 GnmActionHandler handler
)
922 GnmAction
*res
= g_new0 (GnmAction
, 1);
923 res
->id
= g_strdup (id
);
924 res
->label
= g_strdup (label
);
925 res
->icon_name
= g_strdup (icon_name
);
926 res
->always_available
= always_available
;
927 res
->handler
= handler
;
932 gnm_action_free (GnmAction
*action
)
934 if (NULL
!= action
) {
936 g_free (action
->label
);
937 g_free (action
->icon_name
);
943 gnm_action_copy (GnmAction
const *action
)
945 return gnm_action_new (action
->id
, action
->label
, action
->icon_name
,
946 action
->always_available
, action
->handler
);
950 gnm_action_get_type (void)
955 t
= g_boxed_type_register_static ("GnmAction",
956 (GBoxedCopyFunc
)gnm_action_copy
,
957 (GBoxedFreeFunc
)gnm_action_free
);
963 * making GnmAppExtraUI a boxed type for introspection. copy and free don't do
964 anything which might be critical, crossing fingers.*/
966 static GnmAppExtraUI
*
967 gnm_app_extra_ui_ref (GnmAppExtraUI
*ui
)
973 static GnmAppExtraUI
*
974 gnm_app_extra_ui_unref (GnmAppExtraUI
*ui
)
981 gnm_app_extra_ui_get_type (void)
986 t
= g_boxed_type_register_static ("GnmAppExtraUI",
987 (GBoxedCopyFunc
)gnm_app_extra_ui_ref
,
988 (GBoxedFreeFunc
)gnm_app_extra_ui_unref
);
994 * gnm_app_add_extra_ui:
995 * @group_name: action group name.
996 * @actions: (element-type GnmAction): list of actions.
997 * @layout: the xml string describing the menus and toolbars.
998 * @domain: localization domain.
999 * @user_data: user data
1001 * Returns: (transfer full): the newly allocated #GnmAppExtraUI.
1004 gnm_app_add_extra_ui (char const *group_name
,
1010 GnmAppExtraUI
*extra_ui
= g_new0 (GnmAppExtraUI
, 1);
1011 extra_uis
= g_slist_prepend (extra_uis
, extra_ui
);
1012 extra_ui
->group_name
= g_strdup (group_name
);
1013 extra_ui
->actions
= actions
;
1014 extra_ui
->layout
= g_strdup (layout
);
1015 extra_ui
->user_data
= user_data
;
1016 g_signal_emit (G_OBJECT (app
), signals
[CUSTOM_UI_ADDED
], 0, extra_ui
);
1017 if (gnm_debug_flag ("extra-ui"))
1018 g_printerr ("Adding extra ui [%s] %p\n", group_name
, extra_ui
);
1023 gnm_app_remove_extra_ui (GnmAppExtraUI
*extra_ui
)
1025 if (gnm_debug_flag ("extra-ui"))
1026 g_printerr ("Removing extra ui %p\n", extra_ui
);
1027 extra_uis
= g_slist_remove (extra_uis
, extra_ui
);
1028 g_signal_emit (G_OBJECT (app
), signals
[CUSTOM_UI_REMOVED
], 0, extra_ui
);
1029 g_free (extra_ui
->group_name
);
1030 g_free (extra_ui
->layout
);
1035 * gnm_app_foreach_extra_ui:
1036 * @func: (scope call): #GFunc
1039 * Applies @func to each #GnmAppExtraUI.
1042 gnm_app_foreach_extra_ui (GFunc func
, gpointer data
)
1044 g_slist_foreach (extra_uis
, func
, data
);
1047 /**********************************************************************/
1049 static gint windows_update_timer
= 0;
1051 cb_flag_windows_changed (void)
1054 g_signal_emit (G_OBJECT (app
), signals
[WINDOW_LIST_CHANGED
], 0);
1055 windows_update_timer
= 0;
1060 * _gnm_app_flag_windows_changed:
1062 * An internal utility routine to flag a regeneration of the window lists
1065 _gnm_app_flag_windows_changed (void)
1067 if (windows_update_timer
== 0)
1068 windows_update_timer
= g_timeout_add (100,
1069 (GSourceFunc
)cb_flag_windows_changed
, NULL
);
1072 /**********************************************************************/
1077 * Recalculate everything dirty in all workbooks that have automatic
1081 gnm_app_recalc (void)
1085 g_return_if_fail (app
!= NULL
);
1087 gnm_app_recalc_start ();
1089 for (l
= app
->workbook_list
; l
; l
= l
->next
) {
1090 Workbook
*wb
= l
->data
;
1092 if (workbook_get_recalcmode (wb
))
1093 workbook_recalc (wb
);
1096 gnm_app_recalc_finish ();
1100 gnm_app_recalc_start (void)
1102 g_return_if_fail (app
->recalc_count
>= 0);
1103 app
->recalc_count
++;
1107 gnm_app_recalc_finish (void)
1109 g_return_if_fail (app
->recalc_count
> 0);
1110 app
->recalc_count
--;
1111 if (app
->recalc_count
== 0) {
1112 gnm_app_recalc_clear_caches ();
1113 g_signal_emit_by_name (gnm_app_get_app (), "recalc-finished");
1118 gnm_app_recalc_clear_caches (void)
1120 g_signal_emit_by_name (gnm_app_get_app (), "recalc-clear-caches");
1124 gnm_app_shutting_down (void)
1126 return app
->shutting_down
;
1130 gnm_app_initial_open_complete (void)
1132 return app
->initial_open_complete
;