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
);
268 if (wb_control_claim_selection (wbc
)) {
269 g_signal_emit (G_OBJECT (app
), signals
[CLIPBOARD_MODIFIED
], 0);
271 gnm_app_clipboard_clear (FALSE
);
272 g_warning ("Unable to set selection?");
277 * gnm_app_clipboard_cut_copy_obj:
278 * @wbc: #WorkbookControl
279 * @is_cut: %TRUE for cut, %FALSE for copy
281 * @objects: (element-type SheetObject) (transfer container): a list
284 * Different than copying/cutting a region, this can actually cut an object
287 gnm_app_clipboard_cut_copy_obj (WorkbookControl
*wbc
, gboolean is_cut
,
288 SheetView
*sv
, GSList
*objects
)
290 g_return_if_fail (GNM_IS_SHEET_VIEW (sv
));
291 g_return_if_fail (objects
!= NULL
);
292 g_return_if_fail (app
!= NULL
);
294 gnm_app_clipboard_clear (FALSE
);
295 g_free (app
->clipboard_cut_range
);
296 app
->clipboard_cut_range
= NULL
;
297 gnm_sheet_view_weak_ref (sv
, &(app
->clipboard_sheet_view
));
298 app
->clipboard_copied_contents
299 = clipboard_copy_obj (sv_sheet (sv
), objects
);
301 cmd_objects_delete (wbc
, objects
, _("Cut Object"));
304 if (wb_control_claim_selection (wbc
)) {
305 g_signal_emit (G_OBJECT (app
), signals
[CLIPBOARD_MODIFIED
], 0);
307 gnm_app_clipboard_clear (FALSE
);
308 g_warning ("Unable to set selection?");
310 g_slist_free (objects
);
314 gnm_app_clipboard_is_empty (void)
316 g_return_val_if_fail (app
!= NULL
, TRUE
);
318 return app
->clipboard_sheet_view
== NULL
;
322 gnm_app_clipboard_is_cut (void)
324 g_return_val_if_fail (app
!= NULL
, FALSE
);
326 if (app
->clipboard_sheet_view
!= NULL
)
327 return app
->clipboard_copied_contents
? FALSE
: TRUE
;
332 * gnm_app_clipboard_sheet_get:
334 * Returns: (transfer none) (nullable): the current clipboard #Sheet.
337 gnm_app_clipboard_sheet_get (void)
339 g_return_val_if_fail (app
!= NULL
, NULL
);
341 if (app
->clipboard_sheet_view
== NULL
)
343 return sv_sheet (app
->clipboard_sheet_view
);
347 * gnm_app_clipboard_sheet_view_get:
349 * Returns: (transfer none) (nullable): the current clipboard #SheetView.
352 gnm_app_clipboard_sheet_view_get (void)
354 g_return_val_if_fail (app
!= NULL
, NULL
);
355 return app
->clipboard_sheet_view
;
359 * gnm_app_clipboard_contents_get:
361 * Returns: (nullable) (transfer none): the current contents of the clipboard.
364 gnm_app_clipboard_contents_get (void)
366 g_return_val_if_fail (app
!= NULL
, NULL
);
367 return app
->clipboard_copied_contents
;
371 * gnm_app_clipboard_area_get:
373 * Returns: (nullable) (transfer none): the current range in the clipboard.
376 gnm_app_clipboard_area_get (void)
378 g_return_val_if_fail (app
!= NULL
, NULL
);
380 * Only return the range if the sheet has been set.
381 * The range will still contain data even after
382 * the clipboard has been cleared so we need to be extra
383 * safe and only return a range if there is a valid selection
385 if (app
->clipboard_sheet_view
!= NULL
)
386 return app
->clipboard_cut_range
;
391 * gnm_app_workbook_get_by_name:
392 * @name: the workbook name.
395 * Returns: (transfer none): the #Workbook or %NULL.
398 gnm_app_workbook_get_by_name (char const *name
,
402 char *filename
= NULL
;
404 if (name
== NULL
|| *name
== 0)
408 wb
= gnm_app_workbook_get_by_uri (name
);
412 filename
= g_filename_from_utf8 (name
, -1, NULL
, NULL
, NULL
);
414 /* Try as absolute filename. */
415 if (filename
&& g_path_is_absolute (filename
)) {
416 char *uri
= go_filename_to_uri (filename
);
418 wb
= gnm_app_workbook_get_by_uri (uri
);
425 if (filename
&& ref_uri
) {
426 char *rel_uri
= go_url_encode (filename
, 1);
427 char *uri
= go_url_resolve_relative (ref_uri
, rel_uri
);
430 wb
= gnm_app_workbook_get_by_uri (uri
);
442 struct wb_uri_closure
{
448 cb_workbook_uri (Workbook
* wb
, gpointer closure
)
450 struct wb_uri_closure
*dat
= closure
;
451 char const *wb_uri
= go_doc_get_uri (GO_DOC (wb
));
453 if (wb_uri
&& strcmp (wb_uri
, dat
->uri
) == 0) {
461 gnm_app_workbook_foreach (GnmWbIterFunc cback
, gpointer data
);
464 gnm_app_workbook_get_by_uri (char const *uri
)
466 struct wb_uri_closure closure
;
468 g_return_val_if_fail (uri
!= NULL
, NULL
);
472 gnm_app_workbook_foreach (&cb_workbook_uri
, &closure
);
478 gnm_app_workbook_foreach (GnmWbIterFunc cback
, gpointer data
)
482 g_return_val_if_fail (app
!= NULL
, FALSE
);
484 for (l
= app
->workbook_list
; l
; l
= l
->next
){
485 Workbook
*wb
= l
->data
;
487 if (!(*cback
)(wb
, data
))
494 * gnm_app_workbook_get_by_index:
497 * Get nth workbook. Index is zero-based.
498 * Return value: (nullable) (transfer none): the nth workbook, if any.
501 gnm_app_workbook_get_by_index (int i
)
503 return g_list_nth_data (app
->workbook_list
, i
);
507 gnm_app_display_dpi_get (gboolean horizontal
)
510 ? gnm_conf_get_core_gui_screen_horizontaldpi ()
511 : gnm_conf_get_core_gui_screen_verticaldpi ();
515 gnm_app_dpi_to_pixels (void)
517 return MIN (gnm_app_display_dpi_get (TRUE
),
518 gnm_app_display_dpi_get (FALSE
)) / 72.;
523 * gnm_app_create_opener_filter:
524 * @openers: (element-type GOFileOpener): a list of file openers.
526 * Creates a #GtkFileFilter from the list of file types supported by the
527 * openers in the list.
528 * Returns: (transfer full): the newly allocated #GtkFileFilter.
531 gnm_app_create_opener_filter (GList
*openers
)
534 static const char *const bad_suffixes
[] = {
541 GtkFileFilter
*filter
= gtk_file_filter_new ();
542 gboolean for_history
= (openers
== NULL
);
545 openers
= go_get_file_openers ();
547 for (; openers
; openers
= openers
->next
) {
548 GOFileOpener
*opener
= openers
->data
;
549 if (opener
!= NULL
) {
550 const GSList
*mimes
= go_file_opener_get_mimes (opener
);
551 const GSList
*suffixes
= go_file_opener_get_suffixes (opener
);
555 const char *mime
= mimes
->data
;
557 * See 438918. Too many things
558 * like *.xml and *.txt get added
559 * to be useful for the file history
561 gtk_file_filter_add_mime_type (filter
, mime
);
563 g_print ("%s: Adding mime %s\n", go_file_opener_get_description (opener
), mime
);
568 const char *suffix
= suffixes
->data
;
573 for (i
= 0; bad_suffixes
[i
]; i
++)
574 if (strcmp (suffix
, bad_suffixes
[i
]) == 0)
577 /* Create "*.[xX][lL][sS]" */
578 pattern
= g_string_new ("*.");
580 gunichar uc
= g_utf8_get_char (suffix
);
581 suffix
= g_utf8_next_char (suffix
);
582 if (g_unichar_islower (uc
)) {
583 g_string_append_c (pattern
, '[');
584 g_string_append_unichar (pattern
, uc
);
585 uc
= g_unichar_toupper (uc
);
586 g_string_append_unichar (pattern
, uc
);
587 g_string_append_c (pattern
, ']');
589 g_string_append_unichar (pattern
, uc
);
592 gtk_file_filter_add_pattern (filter
, pattern
->str
);
594 g_print ("%s: Adding %s\n", go_file_opener_get_description (opener
), pattern
->str
);
595 g_string_free (pattern
, TRUE
);
598 suffixes
= suffixes
->next
;
606 compare_mru (GtkRecentInfo
*a
, GtkRecentInfo
*b
)
608 time_t ta
= MAX (gtk_recent_info_get_visited (a
), gtk_recent_info_get_modified (a
));
609 time_t tb
= MAX (gtk_recent_info_get_visited (b
), gtk_recent_info_get_modified (b
));
615 * gnm_app_history_get_list:
617 * creating it if necessary.
619 * Return value: (element-type utf8) (transfer full): the list, which must be
620 * freed along with the strings in it.
623 gnm_app_history_get_list (int max_elements
)
627 GtkFileFilter
*filter
;
630 if (app
->recent
== NULL
)
633 items
= gtk_recent_manager_get_items (app
->recent
);
634 items
= g_list_sort (items
, (GCompareFunc
)compare_mru
);
636 filter
= gnm_app_create_opener_filter (NULL
);
638 for (l
= items
; l
&& n_elements
< max_elements
; l
= l
->next
) {
639 GtkRecentInfo
*ri
= l
->data
;
640 const char *uri
= gtk_recent_info_get_uri (ri
);
643 if (gtk_recent_info_has_application (ri
, g_get_application_name ())) {
646 GtkFileFilterInfo fi
;
647 char *display_name
= g_filename_display_basename (uri
);
649 memset (&fi
, 0, sizeof (fi
));
650 fi
.contains
= (GTK_FILE_FILTER_MIME_TYPE
|
651 GTK_FILE_FILTER_URI
|
652 GTK_FILE_FILTER_DISPLAY_NAME
);
654 fi
.mime_type
= gtk_recent_info_get_mime_type (ri
);
655 fi
.display_name
= display_name
;
656 want_it
= gtk_file_filter_filter (filter
, &fi
);
657 g_free (display_name
);
661 char *filename
= go_filename_from_uri (uri
);
662 if (filename
&& !g_file_test (filename
, G_FILE_TEST_EXISTS
))
668 res
= g_slist_prepend (res
, g_strdup (uri
));
673 g_list_free_full (items
, (GDestroyNotify
)gtk_recent_info_unref
);
674 g_object_ref_sink (filter
);
675 g_object_unref (filter
);
677 return g_slist_reverse (res
);
681 * application_history_update_list:
683 * @mimetype: (nullable): the mime type for @uri
685 * Adds @uri to the application's history of files.
688 gnm_app_history_add (char const *uri
, const char *mimetype
)
692 if (app
->recent
== NULL
)
695 memset (&rd
, 0, sizeof (rd
));
698 g_print ("uri: %s\nmime: %s\n\n", uri
, mimetype
? mimetype
: "-");
702 g_strdup (mimetype
? mimetype
: "application/octet-stream");
704 rd
.app_name
= g_strdup (g_get_application_name ());
705 rd
.app_exec
= g_strjoin (" ", g_get_prgname (), "%u", NULL
);
707 rd
.is_private
= FALSE
;
709 if (!gtk_recent_manager_add_full (app
->recent
, uri
, &rd
)) {
711 g_printerr ("Warning: failed to update recent document.\n");
714 g_free (rd
.mime_type
);
715 g_free (rd
.app_name
);
716 g_free (rd
.app_exec
);
718 g_object_notify (G_OBJECT (app
), "file-history-list");
722 cb_recent_changed (G_GNUC_UNUSED GtkRecentManager
*recent
, GnmApp
*app
)
724 g_object_notify (G_OBJECT (app
), "file-history-list");
728 gnm_app_finalize (GObject
*obj
)
730 GnmApp
*application
= GNM_APP (obj
);
732 g_free (application
->clipboard_cut_range
);
733 application
->clipboard_cut_range
= NULL
;
735 application
->recent
= NULL
;
737 if (app
== application
)
740 G_OBJECT_CLASS (parent_klass
)->finalize (obj
);
744 gnm_app_get_property (GObject
*obj
, guint param_id
,
745 GValue
*value
, GParamSpec
*pspec
)
748 GnmApp
*application
= GNM_APP (obj
);
752 case PROP_HISTORY_LIST
:
753 g_value_set_pointer (value
, gnm_app_history_get_list (G_MAXINT
));
755 case PROP_SHUTTING_DOWN
:
756 g_value_set_boolean (value
, gnm_app_shutting_down ());
758 case PROP_INITIAL_OPEN_COMPLETE
:
759 g_value_set_boolean (value
, gnm_app_initial_open_complete ());
761 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj
, param_id
, pspec
);
767 gnm_app_set_property (GObject
*object
, guint property_id
,
768 GValue
const *value
, GParamSpec
*pspec
)
770 GnmApp
*app
= (GnmApp
*)object
;
772 switch (property_id
) {
773 case PROP_SHUTTING_DOWN
:
774 app
->shutting_down
= g_value_get_boolean (value
);
776 case PROP_INITIAL_OPEN_COMPLETE
:
777 app
->initial_open_complete
= g_value_get_boolean (value
);
780 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
786 gnm_app_class_init (GObjectClass
*gobject_klass
)
788 parent_klass
= g_type_class_peek_parent (gobject_klass
);
790 /* Object class method overrides */
791 gobject_klass
->finalize
= gnm_app_finalize
;
792 gobject_klass
->get_property
= gnm_app_get_property
;
793 gobject_klass
->set_property
= gnm_app_set_property
;
795 g_object_class_install_property (gobject_klass
, PROP_HISTORY_LIST
,
796 g_param_spec_pointer ("file-history-list",
797 P_("File History List"),
798 P_("A list of filenames that have been read recently"),
799 GSF_PARAM_STATIC
| G_PARAM_READABLE
));
800 g_object_class_install_property (gobject_klass
, PROP_SHUTTING_DOWN
,
801 g_param_spec_boolean ("shutting-down",
803 P_("In the process of shutting down?"),
805 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
806 g_object_class_install_property (gobject_klass
, PROP_INITIAL_OPEN_COMPLETE
,
807 g_param_spec_boolean ("initial-open-complete",
808 P_("Initial Open Complete"),
809 P_("All command-line files open?"),
811 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
814 signals
[WORKBOOK_ADDED
] = g_signal_new ("workbook_added",
817 G_STRUCT_OFFSET (GnmAppClass
, workbook_added
),
819 g_cclosure_marshal_VOID__OBJECT
,
820 G_TYPE_NONE
, 1, GNM_WORKBOOK_TYPE
);
821 signals
[WORKBOOK_REMOVED
] = g_signal_new ("workbook_removed",
824 G_STRUCT_OFFSET (GnmAppClass
, workbook_removed
),
826 g_cclosure_marshal_VOID__POINTER
,
827 G_TYPE_NONE
, 1, G_TYPE_POINTER
);
828 signals
[WINDOW_LIST_CHANGED
] = g_signal_new ("window-list-changed",
831 G_STRUCT_OFFSET (GnmAppClass
, window_list_changed
),
833 g_cclosure_marshal_VOID__VOID
,
835 signals
[CUSTOM_UI_ADDED
] = g_signal_new ("custom-ui-added",
838 G_STRUCT_OFFSET (GnmAppClass
, custom_ui_added
),
840 g_cclosure_marshal_VOID__POINTER
,
841 G_TYPE_NONE
, 1, G_TYPE_POINTER
);
842 signals
[CUSTOM_UI_REMOVED
] = g_signal_new ("custom-ui-removed",
845 G_STRUCT_OFFSET (GnmAppClass
, custom_ui_removed
),
847 g_cclosure_marshal_VOID__POINTER
,
848 G_TYPE_NONE
, 1, G_TYPE_POINTER
);
849 signals
[CLIPBOARD_MODIFIED
] = g_signal_new ("clipboard_modified",
852 G_STRUCT_OFFSET (GnmAppClass
, clipboard_modified
),
854 g_cclosure_marshal_VOID__VOID
,
856 signals
[RECALC_FINISHED
] = g_signal_new ("recalc_finished",
859 G_STRUCT_OFFSET (GnmAppClass
, recalc_finished
),
861 g_cclosure_marshal_VOID__VOID
,
863 signals
[RECALC_FINISHED
] = g_signal_new ("recalc_clear_caches",
866 G_STRUCT_OFFSET (GnmAppClass
, recalc_clear_caches
),
868 g_cclosure_marshal_VOID__VOID
,
873 gnm_app_init (GObject
*obj
)
875 GnmApp
*gnm_app
= GNM_APP (obj
);
877 gnm_app
->clipboard_copied_contents
= NULL
;
878 gnm_app
->clipboard_sheet_view
= NULL
;
880 gnm_app
->workbook_list
= NULL
;
882 if (gdk_display_get_default ()) {
884 * Only allocate a GtkRecentManager if we have a gui.
885 * This is, in part, because it currently throws an error.
888 gnm_app
->recent
= gtk_recent_manager_get_default ();
889 g_signal_connect_object (G_OBJECT (gnm_app
->recent
),
891 G_CALLBACK (cb_recent_changed
),
898 GSF_CLASS (GnmApp
, gnm_app
,
899 gnm_app_class_init
, gnm_app_init
,
902 /**********************************************************************/
903 static GSList
*extra_uis
= NULL
;
910 * @always_available: whether the action should always be available.
911 * @handler: (scope async): the handler.
913 * Returns: (transfer full): the newly allocated #GnmAction.
916 gnm_action_new (char const *id
, char const *label
,
917 char const *icon_name
, gboolean always_available
,
918 GnmActionHandler handler
)
920 GnmAction
*res
= g_new0 (GnmAction
, 1);
921 res
->id
= g_strdup (id
);
922 res
->label
= g_strdup (label
);
923 res
->icon_name
= g_strdup (icon_name
);
924 res
->always_available
= always_available
;
925 res
->handler
= handler
;
930 gnm_action_free (GnmAction
*action
)
932 if (NULL
!= action
) {
934 g_free (action
->label
);
935 g_free (action
->icon_name
);
941 gnm_action_copy (GnmAction
const *action
)
943 return gnm_action_new (action
->id
, action
->label
, action
->icon_name
,
944 action
->always_available
, action
->handler
);
948 gnm_action_get_type (void)
953 t
= g_boxed_type_register_static ("GnmAction",
954 (GBoxedCopyFunc
)gnm_action_copy
,
955 (GBoxedFreeFunc
)gnm_action_free
);
961 * making GnmAppExtraUI a boxed type for introspection. copy and free don't do
962 anything which might be critical, crossing fingers.*/
964 static GnmAppExtraUI
*
965 gnm_app_extra_ui_ref (GnmAppExtraUI
*ui
)
971 static GnmAppExtraUI
*
972 gnm_app_extra_ui_unref (GnmAppExtraUI
*ui
)
979 gnm_app_extra_ui_get_type (void)
984 t
= g_boxed_type_register_static ("GnmAppExtraUI",
985 (GBoxedCopyFunc
)gnm_app_extra_ui_ref
,
986 (GBoxedFreeFunc
)gnm_app_extra_ui_unref
);
992 * gnm_app_add_extra_ui:
993 * @group_name: action group name.
994 * @actions: (element-type GnmAction): list of actions.
995 * @layout: the xml string describing the menus and toolbars.
996 * @domain: localization domain.
997 * @user_data: user data
999 * Returns: (transfer full): the newly allocated #GnmAppExtraUI.
1002 gnm_app_add_extra_ui (char const *group_name
,
1008 GnmAppExtraUI
*extra_ui
= g_new0 (GnmAppExtraUI
, 1);
1009 extra_uis
= g_slist_prepend (extra_uis
, extra_ui
);
1010 extra_ui
->group_name
= g_strdup (group_name
);
1011 extra_ui
->actions
= actions
;
1012 extra_ui
->layout
= g_strdup (layout
);
1013 extra_ui
->user_data
= user_data
;
1014 g_signal_emit (G_OBJECT (app
), signals
[CUSTOM_UI_ADDED
], 0, extra_ui
);
1015 if (gnm_debug_flag ("extra-ui"))
1016 g_printerr ("Adding extra ui [%s] %p\n", group_name
, extra_ui
);
1021 gnm_app_remove_extra_ui (GnmAppExtraUI
*extra_ui
)
1023 if (gnm_debug_flag ("extra-ui"))
1024 g_printerr ("Removing extra ui %p\n", extra_ui
);
1025 extra_uis
= g_slist_remove (extra_uis
, extra_ui
);
1026 g_signal_emit (G_OBJECT (app
), signals
[CUSTOM_UI_REMOVED
], 0, extra_ui
);
1027 g_free (extra_ui
->group_name
);
1028 g_free (extra_ui
->layout
);
1033 * gnm_app_foreach_extra_ui:
1034 * @func: (scope call): #GFunc
1037 * Applies @func to each #GnmAppExtraUI.
1040 gnm_app_foreach_extra_ui (GFunc func
, gpointer data
)
1042 g_slist_foreach (extra_uis
, func
, data
);
1045 /**********************************************************************/
1047 static gint windows_update_timer
= 0;
1049 cb_flag_windows_changed (void)
1052 g_signal_emit (G_OBJECT (app
), signals
[WINDOW_LIST_CHANGED
], 0);
1053 windows_update_timer
= 0;
1058 * _gnm_app_flag_windows_changed:
1060 * An internal utility routine to flag a regeneration of the window lists
1063 _gnm_app_flag_windows_changed (void)
1065 if (windows_update_timer
== 0)
1066 windows_update_timer
= g_timeout_add (100,
1067 (GSourceFunc
)cb_flag_windows_changed
, NULL
);
1070 /**********************************************************************/
1075 * Recalculate everything dirty in all workbooks that have automatic
1079 gnm_app_recalc (void)
1083 g_return_if_fail (app
!= NULL
);
1085 gnm_app_recalc_start ();
1087 for (l
= app
->workbook_list
; l
; l
= l
->next
) {
1088 Workbook
*wb
= l
->data
;
1090 if (workbook_get_recalcmode (wb
))
1091 workbook_recalc (wb
);
1094 gnm_app_recalc_finish ();
1098 gnm_app_recalc_start (void)
1100 g_return_if_fail (app
->recalc_count
>= 0);
1101 app
->recalc_count
++;
1105 gnm_app_recalc_finish (void)
1107 g_return_if_fail (app
->recalc_count
> 0);
1108 app
->recalc_count
--;
1109 if (app
->recalc_count
== 0) {
1110 gnm_app_recalc_clear_caches ();
1111 g_signal_emit_by_name (gnm_app_get_app (), "recalc-finished");
1116 gnm_app_recalc_clear_caches (void)
1118 g_signal_emit_by_name (gnm_app_get_app (), "recalc-clear-caches");
1122 gnm_app_shutting_down (void)
1124 return app
->shutting_down
;
1128 gnm_app_initial_open_complete (void)
1130 return app
->initial_open_complete
;