1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
4 * wbc-gtk.c: A gtk based WorkbookControl
6 * Copyright (C) 2000-2007 Jody Goldberg (jody@gnome.org)
7 * Copyright (C) 2006-2012 Morten Welinder (terra@gnome.org)
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of the
12 * License, or (at your option) version 3.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
24 #include <gnumeric-config.h>
26 #include "wbc-gtk-impl.h"
27 #include "workbook-view.h"
28 #include "workbook-priv.h"
32 #include "sheet-control-gui-priv.h"
34 #include "sheet-private.h"
35 #include "sheet-view.h"
36 #include "sheet-style.h"
37 #include "sheet-filter.h"
39 #include "dependent.h"
40 #include "application.h"
44 #include "style-font.h"
45 #include "gnm-format.h"
47 #include "style-color.h"
48 #include "style-border.h"
49 #include "gnumeric-conf.h"
50 #include "dialogs/dialogs.h"
51 #include "gui-clipboard.h"
52 #include "libgnumeric.h"
53 #include "gnm-pane-impl.h"
55 #include "selection.h"
56 #include "file-autoft.h"
58 #include "tools/analysis-auto-expression.h"
59 #include "sheet-object-cell-comment.h"
60 #include "print-info.h"
61 #include "expr-name.h"
63 #include <goffice/goffice.h>
64 #include <gsf/gsf-impl-utils.h>
65 #include <gsf/gsf-doc-meta-data.h>
67 #include "gdk/gdkkeysyms-compat.h"
71 #define GET_GUI_ITEM(i_) (gpointer)(gtk_builder_get_object(wbcg->gui, (i_)))
73 #define SHEET_CONTROL_KEY "SheetControl"
75 #define AUTO_EXPR_SAMPLE "Sumerage = -012345678901234"
80 WBG_GTK_PROP_AUTOSAVE_PROMPT
,
81 WBG_GTK_PROP_AUTOSAVE_TIME
85 WBC_GTK_MARKUP_CHANGED
,
95 static gboolean debug_tab_order
;
96 static char const *uifilename
= NULL
;
97 static GtkActionEntry
const *extra_actions
= NULL
;
98 static int extra_actions_nb
;
99 static guint wbc_gtk_signals
[WBC_GTK_LAST_SIGNAL
];
100 static GObjectClass
*parent_class
= NULL
;
103 wbcg_ui_update_begin (WBCGtk
*wbcg
)
105 g_return_val_if_fail (GNM_IS_WBC_GTK (wbcg
), FALSE
);
106 g_return_val_if_fail (!wbcg
->updating_ui
, FALSE
);
108 return (wbcg
->updating_ui
= TRUE
);
112 wbcg_ui_update_end (WBCGtk
*wbcg
)
114 g_return_if_fail (GNM_IS_WBC_GTK (wbcg
));
115 g_return_if_fail (wbcg
->updating_ui
);
117 wbcg
->updating_ui
= FALSE
;
120 /****************************************************************************/
123 set_uifilename (char const *name
, GtkActionEntry
const *actions
, int nb
)
126 extra_actions
= actions
;
127 extra_actions_nb
= nb
;
132 * @wbcg: the workbook control gui
133 * @name: name of action
135 * Returns: (transfer none): The action with the given name
138 wbcg_find_action (WBCGtk
*wbcg
, const char *name
)
142 a
= gtk_action_group_get_action (wbcg
->actions
, name
);
144 a
= gtk_action_group_get_action (wbcg
->permanent_actions
, name
);
146 a
= gtk_action_group_get_action (wbcg
->semi_permanent_actions
, name
);
148 a
= gtk_action_group_get_action (wbcg
->data_only_actions
, name
);
150 a
= gtk_action_group_get_action (wbcg
->font_actions
, name
);
152 a
= gtk_action_group_get_action (wbcg
->toolbar
.actions
, name
);
158 wbc_gtk_set_action_sensitivity (WBCGtk
*wbcg
,
159 char const *action
, gboolean sensitive
)
161 GtkAction
*a
= wbcg_find_action (wbcg
, action
);
162 g_object_set (G_OBJECT (a
), "sensitive", sensitive
, NULL
);
165 /* NOTE : The semantics of prefix and suffix seem contrived. Why are we
166 * handling it at this end ? That stuff should be done in the undo/redo code
169 wbc_gtk_set_action_label (WBCGtk
*wbcg
,
175 GtkAction
*a
= wbcg_find_action (wbcg
, action
);
177 if (prefix
!= NULL
) {
179 gboolean is_suffix
= (suffix
!= NULL
);
181 text
= is_suffix
? g_strdup_printf ("%s: %s", prefix
, suffix
) : (char *) prefix
;
182 g_object_set (G_OBJECT (a
),
184 "sensitive", is_suffix
,
189 g_object_set (G_OBJECT (a
), "label", suffix
, NULL
);
192 g_object_set (G_OBJECT (a
), "tooltip", new_tip
, NULL
);
196 wbc_gtk_set_toggle_action_state (WBCGtk
*wbcg
,
197 char const *action
, gboolean state
)
199 GtkAction
*a
= wbcg_find_action (wbcg
, action
);
200 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (a
), state
);
203 /****************************************************************************/
205 static SheetControlGUI
*
206 wbcg_get_scg (WBCGtk
*wbcg
, Sheet
*sheet
)
208 SheetControlGUI
*scg
;
211 if (sheet
== NULL
|| wbcg
->snotebook
== NULL
)
214 npages
= wbcg_get_n_scg (wbcg
);
217 * This can happen during construction when the clipboard is
218 * being cleared. Ctrl-C Ctrl-Q.
223 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
224 g_return_val_if_fail (sheet
->index_in_wb
>= 0, NULL
);
226 scg
= wbcg_get_nth_scg (wbcg
, sheet
->index_in_wb
);
227 if (NULL
!= scg
&& scg_sheet (scg
) == sheet
)
231 * index_in_wb is probably not accurate because we are in the
232 * middle of removing or adding a sheet.
234 for (i
= 0; i
< npages
; i
++) {
235 scg
= wbcg_get_nth_scg (wbcg
, i
);
236 if (NULL
!= scg
&& scg_sheet (scg
) == sheet
)
240 g_warning ("Failed to find scg for sheet %s", sheet
->name_quoted
);
244 static SheetControlGUI
*
245 get_scg (const GtkWidget
*w
)
247 return g_object_get_data (G_OBJECT (w
), SHEET_CONTROL_KEY
);
251 get_all_scgs (WBCGtk
*wbcg
)
253 int i
, n
= gtk_notebook_get_n_pages (wbcg
->snotebook
);
256 for (i
= 0; i
< n
; i
++) {
257 GtkWidget
*w
= gtk_notebook_get_nth_page (wbcg
->snotebook
, i
);
258 SheetControlGUI
*scg
= get_scg (w
);
259 l
= g_slist_prepend (l
, scg
);
262 return g_slist_reverse (l
);
268 cb_autosave (WBCGtk
*wbcg
)
270 WorkbookView
*wb_view
;
272 g_return_val_if_fail (GNM_IS_WBC_GTK (wbcg
), FALSE
);
274 wb_view
= wb_control_view (GNM_WBC (wbcg
));
279 if (wbcg
->autosave_time
> 0 &&
280 go_doc_is_dirty (wb_view_get_doc (wb_view
))) {
281 if (wbcg
->autosave_prompt
&& !dialog_autosave_prompt (wbcg
))
283 gui_file_save (wbcg
, wb_view
);
289 * wbcg_rangesel_possible:
290 * @wbcg: the workbook control gui
292 * Returns true if the cursor keys should be used to select
293 * a cell range (if the cursor is in a spot in the expression
294 * where it makes sense to have a cell reference), false if not.
297 wbcg_rangesel_possible (WBCGtk
const *wbcg
)
299 g_return_val_if_fail (GNM_IS_WBC_GTK (wbcg
), FALSE
);
301 /* Already range selecting */
302 if (wbcg
->rangesel
!= NULL
)
305 /* Rangesel requires that we be editing somthing */
306 if (!wbcg_is_editing (wbcg
) && !wbcg_entry_has_logical (wbcg
))
309 return gnm_expr_entry_can_rangesel (wbcg_get_entry_logical (wbcg
));
313 wbcg_is_editing (WBCGtk
const *wbcg
)
315 g_return_val_if_fail (GNM_IS_WBC_GTK (wbcg
), FALSE
);
316 return wbcg
->editing
;
320 wbcg_autosave_cancel (WBCGtk
*wbcg
)
322 if (wbcg
->autosave_timer
!= 0) {
323 g_source_remove (wbcg
->autosave_timer
);
324 wbcg
->autosave_timer
= 0;
329 wbcg_autosave_activate (WBCGtk
*wbcg
)
331 wbcg_autosave_cancel (wbcg
);
333 if (wbcg
->autosave_time
> 0) {
334 int secs
= MIN (wbcg
->autosave_time
, G_MAXINT
/ 1000);
335 wbcg
->autosave_timer
=
336 g_timeout_add (secs
* 1000,
337 (GSourceFunc
) cb_autosave
,
343 wbcg_set_autosave_time (WBCGtk
*wbcg
, int secs
)
345 if (secs
== wbcg
->autosave_time
)
348 wbcg
->autosave_time
= secs
;
349 wbcg_autosave_activate (wbcg
);
352 /****************************************************************************/
355 wbcg_edit_line_set (WorkbookControl
*wbc
, char const *text
)
357 GtkEntry
*entry
= wbcg_get_entry ((WBCGtk
*)wbc
);
358 gtk_entry_set_text (entry
, text
);
362 wbcg_edit_selection_descr_set (WorkbookControl
*wbc
, char const *text
)
364 WBCGtk
*wbcg
= (WBCGtk
*)wbc
;
365 gtk_entry_set_text (GTK_ENTRY (wbcg
->selection_descriptor
), text
);
369 wbcg_update_action_sensitivity (WorkbookControl
*wbc
)
371 WBCGtk
*wbcg
= WBC_GTK (wbc
);
372 SheetControlGUI
*scg
= wbcg_cur_scg (wbcg
);
373 gboolean edit_object
= scg
!= NULL
&&
374 (scg
->selected_objects
!= NULL
|| wbcg
->new_object
!= NULL
||
375 scg_sheet (scg
)->sheet_type
== GNM_SHEET_OBJECT
);
376 gboolean enable_actions
= TRUE
;
377 gboolean enable_edit_ok_cancel
= FALSE
;
379 if (edit_object
|| wbcg
->edit_line
.guru
!= NULL
)
380 enable_actions
= FALSE
;
381 else if (wbcg_is_editing (wbcg
)) {
382 enable_actions
= FALSE
;
383 enable_edit_ok_cancel
= TRUE
;
386 /* These are only sensitive while editing */
387 gtk_widget_set_sensitive (wbcg
->ok_button
, enable_edit_ok_cancel
);
388 gtk_widget_set_sensitive (wbcg
->cancel_button
, enable_edit_ok_cancel
);
389 gtk_widget_set_sensitive (wbcg
->func_button
, enable_actions
);
391 if (wbcg
->snotebook
) {
392 gboolean tab_context_menu
=
394 scg_sheet (scg
)->sheet_type
== GNM_SHEET_OBJECT
;
395 int i
, N
= wbcg_get_n_scg (wbcg
);
396 for (i
= 0; i
< N
; i
++) {
398 gnm_notebook_get_nth_label (wbcg
->bnotebook
, i
);
399 g_object_set_data (G_OBJECT (label
), "editable",
400 GINT_TO_POINTER (tab_context_menu
));
404 g_object_set (G_OBJECT (wbcg
->actions
),
405 "sensitive", enable_actions
,
407 g_object_set (G_OBJECT (wbcg
->font_actions
),
408 "sensitive", enable_actions
|| enable_edit_ok_cancel
,
411 if (scg
&& scg_sheet (scg
)->sheet_type
== GNM_SHEET_OBJECT
) {
412 g_object_set (G_OBJECT (wbcg
->data_only_actions
),
415 g_object_set (G_OBJECT (wbcg
->semi_permanent_actions
),
418 gtk_widget_set_sensitive (GTK_WIDGET (wbcg
->edit_line
.entry
), FALSE
);
419 gtk_widget_set_sensitive (GTK_WIDGET (wbcg
->selection_descriptor
), FALSE
);
421 g_object_set (G_OBJECT (wbcg
->data_only_actions
),
424 g_object_set (G_OBJECT (wbcg
->semi_permanent_actions
),
425 "sensitive", enable_actions
,
427 gtk_widget_set_sensitive (GTK_WIDGET (wbcg
->edit_line
.entry
), TRUE
);
428 gtk_widget_set_sensitive (GTK_WIDGET (wbcg
->selection_descriptor
), TRUE
);
433 wbcg_insert_sheet (GtkWidget
*unused
, WBCGtk
*wbcg
)
435 WorkbookControl
*wbc
= GNM_WBC (wbcg
);
436 Sheet
*sheet
= wb_control_cur_sheet (wbc
);
437 Workbook
*wb
= sheet
->workbook
;
438 WorkbookSheetState
*old_state
= workbook_sheet_state_new (wb
);
439 /* Use same size as current sheet. */
440 workbook_sheet_add (wb
, sheet
->index_in_wb
,
441 gnm_sheet_get_max_cols (sheet
),
442 gnm_sheet_get_max_rows (sheet
));
443 cmd_reorganize_sheets (wbc
, old_state
, sheet
);
447 wbcg_append_sheet (GtkWidget
*unused
, WBCGtk
*wbcg
)
449 WorkbookControl
*wbc
= GNM_WBC (wbcg
);
450 Sheet
*sheet
= wb_control_cur_sheet (wbc
);
451 Workbook
*wb
= sheet
->workbook
;
452 WorkbookSheetState
*old_state
= workbook_sheet_state_new (wb
);
453 /* Use same size as current sheet. */
454 workbook_sheet_add (wb
, -1,
455 gnm_sheet_get_max_cols (sheet
),
456 gnm_sheet_get_max_rows (sheet
));
457 cmd_reorganize_sheets (wbc
, old_state
, sheet
);
461 wbcg_clone_sheet (GtkWidget
*unused
, WBCGtk
*wbcg
)
463 WorkbookControl
*wbc
= GNM_WBC (wbcg
);
464 Sheet
*sheet
= wb_control_cur_sheet (wbc
);
465 Workbook
*wb
= sheet
->workbook
;
466 WorkbookSheetState
*old_state
= workbook_sheet_state_new (wb
);
467 Sheet
*new_sheet
= sheet_dup (sheet
);
468 workbook_sheet_attach_at_pos (wb
, new_sheet
, sheet
->index_in_wb
+ 1);
469 /* See workbook_sheet_add: */
470 g_signal_emit_by_name (G_OBJECT (wb
), "sheet_added", 0);
471 cmd_reorganize_sheets (wbc
, old_state
, sheet
);
472 g_object_unref (new_sheet
);
476 cb_show_sheet (SheetControlGUI
*scg
)
478 WBCGtk
*wbcg
= scg
->wbcg
;
479 int page_number
= gtk_notebook_page_num (wbcg
->snotebook
,
480 GTK_WIDGET (scg
->grid
));
481 gnm_notebook_set_current_page (wbcg
->bnotebook
, page_number
);
486 static void cb_sheets_manage (SheetControlGUI
*scg
) { dialog_sheet_order (scg
->wbcg
); }
487 static void cb_sheets_insert (SheetControlGUI
*scg
) { wbcg_insert_sheet (NULL
, scg
->wbcg
); }
488 static void cb_sheets_add (SheetControlGUI
*scg
) { wbcg_append_sheet (NULL
, scg
->wbcg
); }
489 static void cb_sheets_clone (SheetControlGUI
*scg
) { wbcg_clone_sheet (NULL
, scg
->wbcg
); }
490 static void cb_sheets_rename (SheetControlGUI
*scg
) { dialog_sheet_rename (scg
->wbcg
, scg_sheet (scg
)); }
491 static void cb_sheets_resize (SheetControlGUI
*scg
) { dialog_sheet_resize (scg
->wbcg
); }
495 cb_by_scg_sheet_name (gconstpointer a_
, gconstpointer b_
)
497 const SheetControlGUI
*a
= a_
;
498 const SheetControlGUI
*b
= b_
;
499 Sheet
*sa
= scg_sheet (a
);
500 Sheet
*sb
= scg_sheet (b
);
502 return g_utf8_collate (sa
->name_unquoted
, sb
->name_unquoted
);
507 sheet_menu_label_run (SheetControlGUI
*scg
, GdkEvent
*event
)
509 enum { CM_MULTIPLE
= 1, CM_DATA_SHEET
= 2 };
510 struct SheetTabMenu
{
512 void (*function
) (SheetControlGUI
*scg
);
515 } const sheet_label_context_actions
[] = {
516 { N_("Manage Sheets..."), &cb_sheets_manage
, 0, 0},
517 { NULL
, NULL
, 0, 0 },
518 { N_("Insert"), &cb_sheets_insert
, 0, 0 },
519 { N_("Append"), &cb_sheets_add
, 0, 0 },
520 { N_("Duplicate"), &cb_sheets_clone
, 0, 0 },
521 { N_("Remove"), &scg_delete_sheet_if_possible
, CM_MULTIPLE
, 0 },
522 { N_("Rename"), &cb_sheets_rename
, 0, 0 },
523 { N_("Resize..."), &cb_sheets_resize
, CM_DATA_SHEET
, 0 },
524 { N_("Select"), NULL
, 0, 1 },
525 { N_("Select (sorted)"), NULL
, 0, 2 }
529 GtkWidget
*item
, *menu
= gtk_menu_new ();
530 GtkWidget
*guru
= wbc_gtk_get_guru (scg_wbcg (scg
));
531 unsigned int N_visible
, pass
;
532 GtkWidget
*submenus
[2 + 1];
533 GSList
*scgs
= get_all_scgs (scg
->wbcg
);
535 for (pass
= 1; pass
<= 2; pass
++) {
538 submenus
[pass
] = gtk_menu_new ();
540 for (l
= scgs
; l
; l
= l
->next
) {
541 SheetControlGUI
*scg1
= l
->data
;
542 Sheet
*sheet
= scg_sheet (scg1
);
543 if (!sheet_is_visible (sheet
))
548 item
= gtk_menu_item_new_with_label (sheet
->name_unquoted
);
549 g_signal_connect_swapped (G_OBJECT (item
), "activate",
550 G_CALLBACK (cb_show_sheet
), scg1
);
551 gtk_menu_shell_append (GTK_MENU_SHELL (submenus
[pass
]), item
);
552 gtk_widget_show (item
);
555 scgs
= g_slist_sort (scgs
, cb_by_scg_sheet_name
);
559 for (ui
= 0; ui
< G_N_ELEMENTS (sheet_label_context_actions
); ui
++) {
560 const struct SheetTabMenu
*it
=
561 sheet_label_context_actions
+ ui
;
563 ((it
->flags
& CM_MULTIPLE
) && N_visible
<= 1) ||
564 ((it
->flags
& CM_DATA_SHEET
) && scg_sheet (scg
)->sheet_type
!= GNM_SHEET_DATA
) ||
565 (!it
->submenu
&& guru
!= NULL
);
568 ? gtk_menu_item_new_with_label (_(it
->text
))
569 : gtk_separator_menu_item_new ();
571 g_signal_connect_swapped (G_OBJECT (item
), "activate",
572 G_CALLBACK (it
->function
), scg
);
574 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item
),
575 submenus
[it
->submenu
]);
577 gtk_widget_set_sensitive (item
, !inactive
);
578 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
579 gtk_widget_show (item
);
582 gnumeric_popup_menu (GTK_MENU (menu
), event
);
586 * cb_sheet_label_button_press:
588 * Invoked when the user has clicked on the sheet name widget.
589 * This takes care of switching to the notebook that contains the label
592 cb_sheet_label_button_press (GtkWidget
*widget
, GdkEvent
*event
,
593 SheetControlGUI
*scg
)
595 WBCGtk
*wbcg
= scg
->wbcg
;
598 if (event
->type
!= GDK_BUTTON_PRESS
)
601 page_number
= gtk_notebook_page_num (wbcg
->snotebook
,
602 GTK_WIDGET (scg
->grid
));
603 gnm_notebook_set_current_page (wbcg
->bnotebook
, page_number
);
605 if (event
->button
.button
== 1 || NULL
!= wbcg
->rangesel
)
608 if (event
->button
.button
== 3) {
609 if ((scg_wbcg (scg
))->edit_line
.guru
== NULL
)
610 scg_object_unselect (scg
, NULL
);
611 if (g_object_get_data (G_OBJECT (widget
), "editable")) {
612 sheet_menu_label_run (scg
, event
);
613 scg_take_focus (scg
);
622 cb_sheet_label_drag_data_get (GtkWidget
*widget
, GdkDragContext
*context
,
623 GtkSelectionData
*selection_data
,
624 guint info
, guint time
)
626 SheetControlGUI
*scg
= get_scg (widget
);
627 g_return_if_fail (GNM_IS_SCG (scg
));
629 scg_drag_data_get (scg
, selection_data
);
633 cb_sheet_label_drag_data_received (GtkWidget
*widget
, GdkDragContext
*context
,
634 gint x
, gint y
, GtkSelectionData
*data
, guint info
, guint time
,
638 SheetControlGUI
*scg_src
, *scg_dst
;
639 Sheet
*s_src
, *s_dst
;
641 g_return_if_fail (GNM_IS_WBC_GTK (wbcg
));
642 g_return_if_fail (GTK_IS_WIDGET (widget
));
644 w_source
= gtk_drag_get_source_widget (context
);
646 g_warning ("Not yet implemented!"); /* Different process */
650 scg_src
= get_scg (w_source
);
651 g_return_if_fail (scg_src
!= NULL
);
652 s_src
= scg_sheet (scg_src
);
654 scg_dst
= get_scg (widget
);
655 g_return_if_fail (scg_dst
!= NULL
);
656 s_dst
= scg_sheet (scg_dst
);
658 if (s_src
== s_dst
) {
660 } else if (s_src
->workbook
== s_dst
->workbook
) {
661 /* Move within workbook */
662 Workbook
*wb
= s_src
->workbook
;
663 int p_src
= s_src
->index_in_wb
;
664 int p_dst
= s_dst
->index_in_wb
;
665 WorkbookSheetState
*old_state
= workbook_sheet_state_new (wb
);
666 workbook_sheet_move (s_src
, p_dst
- p_src
);
667 cmd_reorganize_sheets (GNM_WBC (wbcg
),
671 g_return_if_fail (GNM_IS_SCG (gtk_selection_data_get_data (data
)));
673 /* Different workbook, same process */
674 g_warning ("Not yet implemented!");
679 * Not currently reachable, I believe. We use the notebook's dragging.
682 cb_sheet_label_drag_begin (GtkWidget
*widget
, GdkDragContext
*context
,
685 GtkWidget
*arrow
, *image
;
687 g_return_if_fail (GNM_IS_WBC_GTK (wbcg
));
689 /* Create the arrow. */
690 arrow
= gtk_window_new (GTK_WINDOW_POPUP
);
691 gtk_window_set_screen (GTK_WINDOW (arrow
),
692 gtk_widget_get_screen (widget
));
693 gtk_widget_realize (arrow
);
694 image
= gtk_image_new_from_resource ("/org/gnumeric/gnumeric/images/sheet_move_marker.png");
695 gtk_widget_show (image
);
696 gtk_container_add (GTK_CONTAINER (arrow
), image
);
697 g_object_ref_sink (arrow
);
698 g_object_set_data (G_OBJECT (widget
), "arrow", arrow
);
702 cb_sheet_label_drag_end (GtkWidget
*widget
, GdkDragContext
*context
,
707 g_return_if_fail (GNM_IS_WBC (wbcg
));
709 /* Destroy the arrow. */
710 arrow
= g_object_get_data (G_OBJECT (widget
), "arrow");
711 gtk_widget_destroy (arrow
);
712 g_object_unref (arrow
);
713 g_object_set_data (G_OBJECT (widget
), "arrow", NULL
);
717 cb_sheet_label_drag_leave (GtkWidget
*widget
, GdkDragContext
*context
,
718 guint time
, WBCGtk
*wbcg
)
720 GtkWidget
*w_source
, *arrow
;
722 /* Hide the arrow. */
723 w_source
= gtk_drag_get_source_widget (context
);
725 arrow
= g_object_get_data (G_OBJECT (w_source
), "arrow");
726 gtk_widget_hide (arrow
);
731 cb_sheet_label_drag_motion (GtkWidget
*widget
, GdkDragContext
*context
,
732 gint x
, gint y
, guint time
, WBCGtk
*wbcg
)
734 SheetControlGUI
*scg_src
, *scg_dst
;
735 GtkWidget
*w_source
, *arrow
, *window
;
736 gint root_x
, root_y
, pos_x
, pos_y
;
737 GtkAllocation wa
, wsa
;
739 g_return_val_if_fail (GNM_IS_WBC_GTK (wbcg
), FALSE
);
740 g_return_val_if_fail (GNM_IS_WBC_GTK (wbcg
), FALSE
);
742 /* Make sure we are really hovering over another label. */
743 w_source
= gtk_drag_get_source_widget (context
);
747 arrow
= g_object_get_data (G_OBJECT (w_source
), "arrow");
749 scg_src
= get_scg (w_source
);
750 scg_dst
= get_scg (widget
);
752 if (scg_src
== scg_dst
) {
753 gtk_widget_hide (arrow
);
757 /* Move the arrow to the correct position and show it. */
758 window
= gtk_widget_get_ancestor (widget
, GTK_TYPE_WINDOW
);
759 gtk_window_get_position (GTK_WINDOW (window
), &root_x
, &root_y
);
760 gtk_widget_get_allocation (widget
,&wa
);
761 pos_x
= root_x
+ wa
.x
;
762 pos_y
= root_y
+ wa
.y
;
763 gtk_widget_get_allocation (w_source
,&wsa
);
766 gtk_window_move (GTK_WINDOW (arrow
), pos_x
, pos_y
);
767 gtk_widget_show (arrow
);
773 set_dir (GtkWidget
*w
, GtkTextDirection
*dir
)
775 gtk_widget_set_direction (w
, *dir
);
776 if (GTK_IS_CONTAINER (w
))
777 gtk_container_foreach (GTK_CONTAINER (w
),
778 (GtkCallback
)&set_dir
,
783 wbcg_set_direction (SheetControlGUI
const *scg
)
785 GtkWidget
*w
= (GtkWidget
*)scg
->wbcg
->snotebook
;
786 gboolean text_is_rtl
= scg_sheet (scg
)->text_is_rtl
;
787 GtkTextDirection dir
= text_is_rtl
791 if (dir
!= gtk_widget_get_direction (w
))
794 g_object_set (scg
->hs
, "inverted", text_is_rtl
, NULL
);
798 cb_direction_change (G_GNUC_UNUSED Sheet
*null_sheet
,
799 G_GNUC_UNUSED GParamSpec
*null_pspec
,
800 SheetControlGUI
const *scg
)
802 if (scg
&& scg
== wbcg_cur_scg (scg
->wbcg
))
803 wbcg_set_direction (scg
);
807 wbcg_update_menu_feedback (WBCGtk
*wbcg
, Sheet
const *sheet
)
809 g_return_if_fail (IS_SHEET (sheet
));
811 if (!wbcg_ui_update_begin (wbcg
))
814 wbc_gtk_set_toggle_action_state (wbcg
,
815 "SheetDisplayFormulas", sheet
->display_formulas
);
816 wbc_gtk_set_toggle_action_state (wbcg
,
817 "SheetHideZeros", sheet
->hide_zero
);
818 wbc_gtk_set_toggle_action_state (wbcg
,
819 "SheetHideGridlines", sheet
->hide_grid
);
820 wbc_gtk_set_toggle_action_state (wbcg
,
821 "SheetHideColHeader", sheet
->hide_col_header
);
822 wbc_gtk_set_toggle_action_state (wbcg
,
823 "SheetHideRowHeader", sheet
->hide_row_header
);
824 wbc_gtk_set_toggle_action_state (wbcg
,
825 "SheetDisplayOutlines", sheet
->display_outlines
);
826 wbc_gtk_set_toggle_action_state (wbcg
,
827 "SheetOutlineBelow", sheet
->outline_symbols_below
);
828 wbc_gtk_set_toggle_action_state (wbcg
,
829 "SheetOutlineRight", sheet
->outline_symbols_right
);
830 wbc_gtk_set_toggle_action_state (wbcg
,
831 "SheetUseR1C1", sheet
->convs
->r1c1_addresses
);
832 wbcg_ui_update_end (wbcg
);
836 cb_zoom_change (Sheet
*sheet
,
837 G_GNUC_UNUSED GParamSpec
*null_pspec
,
840 if (wbcg_ui_update_begin (wbcg
)) {
841 int pct
= sheet
->last_zoom_factor_used
* 100 + .5;
842 char *label
= g_strdup_printf ("%d%%", pct
);
843 go_action_combo_text_set_entry (wbcg
->zoom_haction
, label
,
844 GO_ACTION_COMBO_SEARCH_CURRENT
);
846 wbcg_ui_update_end (wbcg
);
851 cb_notebook_switch_page (G_GNUC_UNUSED GtkNotebook
*notebook_
,
852 G_GNUC_UNUSED GtkWidget
*page_
,
853 guint page_num
, WBCGtk
*wbcg
)
856 SheetControlGUI
*new_scg
;
858 g_return_if_fail (GNM_IS_WBC_GTK (wbcg
));
860 /* Ignore events during destruction */
861 if (wbcg
->snotebook
== NULL
)
865 g_printerr ("Notebook page switch\n");
867 /* While initializing adding the sheets will trigger page changes, but
868 * we do not actually want to change the focus sheet for the view
870 if (wbcg
->updating_ui
)
873 /* If we are not at a subexpression boundary then finish editing */
874 if (NULL
!= wbcg
->rangesel
)
875 scg_rangesel_stop (wbcg
->rangesel
, TRUE
);
878 * Make snotebook follow bnotebook. This should be the only place
879 * that changes pages for snotebook.
881 gtk_notebook_set_current_page (wbcg
->snotebook
, page_num
);
883 new_scg
= wbcg_get_nth_scg (wbcg
, page_num
);
884 wbcg_set_direction (new_scg
);
886 if (wbcg_is_editing (wbcg
) && wbcg_rangesel_possible (wbcg
)) {
888 * When we are editing, sheet changes are not done fully.
889 * We revert to the original sheet later.
891 * On the other hand, when we are selecting a range for a
892 * dialog, we do change sheet fully.
894 scg_take_focus (new_scg
);
898 gnm_expr_entry_set_scg (wbcg
->edit_line
.entry
, new_scg
);
901 * Make absolutely sure the expression doesn't get 'lost',
902 * if it's invalid then prompt the user and don't switch
905 if (wbcg_is_editing (wbcg
)) {
906 guint prev
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT (wbcg
->snotebook
),
909 if (prev
== page_num
)
912 if (!wbcg_edit_finish (wbcg
, WBC_EDIT_ACCEPT
, NULL
))
913 gnm_notebook_set_current_page (wbcg
->bnotebook
,
916 /* Looks silly, but is really neccesarry */
917 gnm_notebook_set_current_page (wbcg
->bnotebook
,
923 g_object_set_data (G_OBJECT (wbcg
->snotebook
), "previous_page",
924 GINT_TO_POINTER (gtk_notebook_get_current_page (wbcg
->snotebook
)));
926 /* if we are not selecting a range for an expression update */
927 sheet
= wbcg_focus_cur_scg (wbcg
);
928 if (sheet
!= wbcg_cur_sheet (wbcg
)) {
929 wbcg_update_menu_feedback (wbcg
, sheet
);
930 sheet_flag_status_update_range (sheet
, NULL
);
931 sheet_update (sheet
);
932 wb_view_sheet_focus (wb_control_view (GNM_WBC (wbcg
)), sheet
);
933 cb_zoom_change (sheet
, NULL
, wbcg
);
938 cb_bnotebook_button_press (GtkWidget
*widget
, GdkEventButton
*event
)
940 if (event
->type
== GDK_2BUTTON_PRESS
&& event
->button
== 1) {
942 * Eat the click so cb_paned_button_press doesn't see it.
952 cb_bnotebook_page_reordered (GtkNotebook
*notebook
, GtkWidget
*child
,
953 int page_num
, WBCGtk
*wbcg
)
955 GtkNotebook
*snotebook
= GTK_NOTEBOOK (wbcg
->snotebook
);
956 int old
= gtk_notebook_get_current_page (snotebook
);
958 if (wbcg
->updating_ui
)
962 g_printerr ("Reordered %d -> %d\n", old
, page_num
);
964 if (old
!= page_num
) {
965 WorkbookControl
* wbc
= GNM_WBC (wbcg
);
966 Workbook
*wb
= wb_control_get_workbook (wbc
);
967 Sheet
*sheet
= workbook_sheet_by_index (wb
, old
);
968 WorkbookSheetState
* old_state
= workbook_sheet_state_new(wb
);
969 workbook_sheet_move (sheet
, page_num
- old
);
970 cmd_reorganize_sheets (wbc
, old_state
, sheet
);
976 wbc_gtk_create_notebook_area (WBCGtk
*wbcg
)
978 GtkWidget
*placeholder
;
980 wbcg
->bnotebook
= g_object_new (GNM_NOTEBOOK_TYPE
,
983 g_object_ref (wbcg
->bnotebook
);
985 g_signal_connect_after (G_OBJECT (wbcg
->bnotebook
),
987 G_CALLBACK (cb_notebook_switch_page
), wbcg
);
988 g_signal_connect (G_OBJECT (wbcg
->bnotebook
),
989 "button-press-event",
990 G_CALLBACK (cb_bnotebook_button_press
),
992 g_signal_connect (G_OBJECT (wbcg
->bnotebook
),
994 G_CALLBACK (cb_bnotebook_page_reordered
),
996 placeholder
= gtk_paned_get_child1 (wbcg
->tabs_paned
);
998 gtk_widget_destroy (placeholder
);
999 gtk_paned_pack1 (wbcg
->tabs_paned
, GTK_WIDGET (wbcg
->bnotebook
), FALSE
, TRUE
);
1001 gtk_widget_show_all (GTK_WIDGET (wbcg
->tabs_paned
));
1006 wbcg_menu_state_sheet_count (WBCGtk
*wbcg
)
1008 int const sheet_count
= gnm_notebook_get_n_visible (wbcg
->bnotebook
);
1009 /* Should we enable commands requiring multiple sheets */
1010 gboolean
const multi_sheet
= (sheet_count
> 1);
1012 wbc_gtk_set_action_sensitivity (wbcg
, "SheetRemove", multi_sheet
);
1016 cb_sheet_direction_change (Sheet
*sheet
,
1017 G_GNUC_UNUSED GParamSpec
*pspec
,
1021 "icon-name", (sheet
->text_is_rtl
1022 ? "format-text-direction-rtl"
1023 : "format-text-direction-ltr"),
1028 cb_sheet_tab_change (Sheet
*sheet
,
1029 G_GNUC_UNUSED GParamSpec
*pspec
,
1032 GdkRGBA cfore
, cback
;
1033 SheetControlGUI
*scg
= get_scg (widget
);
1035 g_return_if_fail (GNM_IS_SCG (scg
));
1037 /* We're lazy and just set all relevant attributes. */
1038 g_object_set (widget
,
1039 "label", sheet
->name_unquoted
,
1042 ? go_color_to_gdk_rgba (sheet
->tab_color
->go_color
,
1046 (sheet
->tab_text_color
1047 ? go_color_to_gdk_rgba (sheet
->tab_text_color
->go_color
,
1054 cb_toggle_menu_item_changed (Sheet
*sheet
,
1055 G_GNUC_UNUSED GParamSpec
*pspec
,
1058 /* We're lazy and just update all. */
1059 wbcg_update_menu_feedback (wbcg
, sheet
);
1063 cb_sheet_visibility_change (Sheet
*sheet
,
1064 G_GNUC_UNUSED GParamSpec
*pspec
,
1065 SheetControlGUI
*scg
)
1069 g_return_if_fail (GNM_IS_SCG (scg
));
1071 viz
= sheet_is_visible (sheet
);
1072 gtk_widget_set_visible (GTK_WIDGET (scg
->grid
), viz
);
1073 gtk_widget_set_visible (GTK_WIDGET (scg
->label
), viz
);
1075 wbcg_menu_state_sheet_count (scg
->wbcg
);
1079 disconnect_sheet_focus_signals (WBCGtk
*wbcg
)
1081 SheetControlGUI
*scg
= wbcg
->active_scg
;
1087 sheet
= scg_sheet (scg
);
1090 g_printerr ("Disconnecting focus for %s with scg=%p\n", sheet
->name_unquoted
, scg
);
1093 g_signal_handlers_disconnect_by_func (sheet
, cb_toggle_menu_item_changed
, wbcg
);
1094 g_signal_handlers_disconnect_by_func (sheet
, cb_direction_change
, scg
);
1095 g_signal_handlers_disconnect_by_func (sheet
, cb_zoom_change
, wbcg
);
1097 wbcg
->active_scg
= NULL
;
1101 disconnect_sheet_signals (SheetControlGUI
*scg
)
1103 WBCGtk
*wbcg
= scg
->wbcg
;
1104 Sheet
*sheet
= scg_sheet (scg
);
1106 if (scg
== wbcg
->active_scg
)
1107 disconnect_sheet_focus_signals (wbcg
);
1110 g_printerr ("Disconnecting all for %s with scg=%p\n", sheet
->name_unquoted
, scg
);
1113 g_signal_handlers_disconnect_by_func (sheet
, cb_sheet_direction_change
,
1114 wbcg_find_action (wbcg
, "SheetDirection"));
1115 g_signal_handlers_disconnect_by_func (sheet
, cb_sheet_tab_change
, scg
->label
);
1116 g_signal_handlers_disconnect_by_func (sheet
, cb_sheet_visibility_change
, scg
);
1120 wbcg_sheet_add (WorkbookControl
*wbc
, SheetView
*sv
)
1122 static GtkTargetEntry
const drag_types
[] = {
1123 { (char *)"GNUMERIC_SHEET", GTK_TARGET_SAME_APP
, TARGET_SHEET
},
1124 { (char *)"UTF8_STRING", 0, 0 },
1125 { (char *)"image/svg+xml", 0, 0 },
1126 { (char *)"image/x-wmf", 0, 0 },
1127 { (char *)"image/x-emf", 0, 0 },
1128 { (char *)"image/png", 0, 0 },
1129 { (char *)"image/jpeg", 0, 0 },
1130 { (char *)"image/bmp", 0, 0 }
1133 WBCGtk
*wbcg
= (WBCGtk
*)wbc
;
1134 SheetControlGUI
*scg
;
1135 Sheet
*sheet
= sv_sheet (sv
);
1136 gboolean visible
= sheet_is_visible (sheet
);
1138 g_return_if_fail (wbcg
!= NULL
);
1140 scg
= sheet_control_gui_new (sv
, wbcg
);
1142 g_object_set_data (G_OBJECT (scg
->grid
), SHEET_CONTROL_KEY
, scg
);
1144 g_object_set_data (G_OBJECT (scg
->label
), SHEET_CONTROL_KEY
, scg
);
1146 /* do not preempt the editable label handler */
1147 g_signal_connect_after (G_OBJECT (scg
->label
),
1148 "button_press_event",
1149 G_CALLBACK (cb_sheet_label_button_press
), scg
);
1152 gtk_drag_source_set (scg
->label
, GDK_BUTTON1_MASK
| GDK_BUTTON3_MASK
,
1153 drag_types
, G_N_ELEMENTS (drag_types
),
1155 gtk_drag_dest_set (scg
->label
, GTK_DEST_DEFAULT_ALL
,
1156 drag_types
, G_N_ELEMENTS (drag_types
),
1158 g_object_connect (G_OBJECT (scg
->label
),
1159 "signal::drag_begin", G_CALLBACK (cb_sheet_label_drag_begin
), wbcg
,
1160 "signal::drag_end", G_CALLBACK (cb_sheet_label_drag_end
), wbcg
,
1161 "signal::drag_leave", G_CALLBACK (cb_sheet_label_drag_leave
), wbcg
,
1162 "signal::drag_data_get", G_CALLBACK (cb_sheet_label_drag_data_get
), NULL
,
1163 "signal::drag_data_received", G_CALLBACK (cb_sheet_label_drag_data_received
), wbcg
,
1164 "signal::drag_motion", G_CALLBACK (cb_sheet_label_drag_motion
), wbcg
,
1167 gtk_widget_show (scg
->label
);
1168 gtk_widget_show_all (GTK_WIDGET (scg
->grid
));
1170 gtk_widget_hide (GTK_WIDGET (scg
->grid
));
1171 gtk_widget_hide (GTK_WIDGET (scg
->label
));
1173 g_object_connect (G_OBJECT (sheet
),
1174 "signal::notify::visibility", cb_sheet_visibility_change
, scg
,
1175 "signal::notify::name", cb_sheet_tab_change
, scg
->label
,
1176 "signal::notify::tab-foreground", cb_sheet_tab_change
, scg
->label
,
1177 "signal::notify::tab-background", cb_sheet_tab_change
, scg
->label
,
1178 "signal::notify::text-is-rtl", cb_sheet_direction_change
, wbcg_find_action (wbcg
, "SheetDirection"),
1181 if (wbcg_ui_update_begin (wbcg
)) {
1183 * Just let wbcg_sheet_order_changed deal with where to put
1187 gtk_notebook_insert_page (wbcg
->snotebook
,
1188 GTK_WIDGET (scg
->grid
), NULL
,
1190 gnm_notebook_insert_tab (wbcg
->bnotebook
,
1191 GTK_WIDGET (scg
->label
),
1193 wbcg_menu_state_sheet_count (wbcg
);
1194 wbcg_ui_update_end (wbcg
);
1197 scg_adjust_preferences (scg
);
1198 if (sheet
== wb_control_cur_sheet (wbc
)) {
1199 scg_take_focus (scg
);
1200 wbcg_set_direction (scg
);
1201 cb_zoom_change (sheet
, NULL
, wbcg
);
1202 cb_toggle_menu_item_changed (sheet
, NULL
, wbcg
);
1207 wbcg_sheet_remove (WorkbookControl
*wbc
, Sheet
*sheet
)
1209 WBCGtk
*wbcg
= (WBCGtk
*)wbc
;
1210 SheetControlGUI
*scg
= wbcg_get_scg (wbcg
, sheet
);
1212 /* During destruction we may have already removed the notebook */
1216 disconnect_sheet_signals (scg
);
1218 gtk_widget_destroy (GTK_WIDGET (scg
->label
));
1219 gtk_widget_destroy (GTK_WIDGET (scg
->grid
));
1221 wbcg_menu_state_sheet_count (wbcg
);
1225 wbcg_sheet_focus (WorkbookControl
*wbc
, Sheet
*sheet
)
1227 WBCGtk
*wbcg
= (WBCGtk
*)wbc
;
1228 SheetControlGUI
*scg
= wbcg_get_scg (wbcg
, sheet
);
1231 int n
= gtk_notebook_page_num (wbcg
->snotebook
,
1232 GTK_WIDGET (scg
->grid
));
1233 gnm_notebook_set_current_page (wbcg
->bnotebook
, n
);
1235 if (wbcg
->rangesel
== NULL
)
1236 gnm_expr_entry_set_scg (wbcg
->edit_line
.entry
, scg
);
1239 disconnect_sheet_focus_signals (wbcg
);
1242 wbcg_update_menu_feedback (wbcg
, sheet
);
1245 wbcg_set_direction (scg
);
1248 g_printerr ("Connecting for %s with scg=%p\n", sheet
->name_unquoted
, scg
);
1253 "signal::notify::display-formulas", cb_toggle_menu_item_changed
, wbcg
,
1254 "signal::notify::display-zeros", cb_toggle_menu_item_changed
, wbcg
,
1255 "signal::notify::display-grid", cb_toggle_menu_item_changed
, wbcg
,
1256 "signal::notify::display-column-header", cb_toggle_menu_item_changed
, wbcg
,
1257 "signal::notify::display-row-header", cb_toggle_menu_item_changed
, wbcg
,
1258 "signal::notify::display-outlines", cb_toggle_menu_item_changed
, wbcg
,
1259 "signal::notify::display-outlines-below", cb_toggle_menu_item_changed
, wbcg
,
1260 "signal::notify::display-outlines-right", cb_toggle_menu_item_changed
, wbcg
,
1261 "signal::notify::text-is-rtl", cb_direction_change
, scg
,
1262 "signal::notify::zoom-factor", cb_zoom_change
, wbcg
,
1265 wbcg
->active_scg
= scg
;
1270 by_sheet_index (gconstpointer a
, gconstpointer b
)
1272 SheetControlGUI
*scga
= (SheetControlGUI
*)a
;
1273 SheetControlGUI
*scgb
= (SheetControlGUI
*)b
;
1274 return scg_sheet (scga
)->index_in_wb
- scg_sheet (scgb
)->index_in_wb
;
1278 wbcg_sheet_order_changed (WBCGtk
*wbcg
)
1280 if (wbcg_ui_update_begin (wbcg
)) {
1284 /* Reorder all tabs so they end up in index_in_wb order. */
1285 scgs
= g_slist_sort (get_all_scgs (wbcg
), by_sheet_index
);
1287 for (i
= 0, l
= scgs
; l
; l
= l
->next
, i
++) {
1288 SheetControlGUI
*scg
= l
->data
;
1289 gtk_notebook_reorder_child (wbcg
->snotebook
,
1290 GTK_WIDGET (scg
->grid
),
1292 gnm_notebook_move_tab (wbcg
->bnotebook
,
1293 GTK_WIDGET (scg
->label
),
1296 g_slist_free (scgs
);
1298 wbcg_ui_update_end (wbcg
);
1303 wbcg_update_title (WBCGtk
*wbcg
)
1305 GODoc
*doc
= wb_control_get_doc (GNM_WBC (wbcg
));
1306 char *basename
= doc
->uri
? go_basename_from_uri (doc
->uri
) : NULL
;
1307 char *title
= g_strconcat
1308 (go_doc_is_dirty (doc
) ? "*" : "",
1309 basename
? basename
: doc
->uri
,
1312 gtk_window_set_title (wbcg_toplevel (wbcg
), title
);
1318 wbcg_sheet_remove_all (WorkbookControl
*wbc
)
1320 WBCGtk
*wbcg
= (WBCGtk
*)wbc
;
1322 if (wbcg
->snotebook
!= NULL
) {
1323 GtkNotebook
*tmp
= wbcg
->snotebook
;
1324 GSList
*l
, *all
= get_all_scgs (wbcg
);
1325 SheetControlGUI
*current
= wbcg_cur_scg (wbcg
);
1327 /* Clear notebook to disable updates as focus changes for pages
1328 * during destruction */
1329 wbcg
->snotebook
= NULL
;
1331 /* Be sure we are no longer editing */
1332 wbcg_edit_finish (wbcg
, WBC_EDIT_REJECT
, NULL
);
1334 for (l
= all
; l
; l
= l
->next
) {
1335 SheetControlGUI
*scg
= l
->data
;
1336 disconnect_sheet_signals (scg
);
1337 if (scg
!= current
) {
1338 gtk_widget_destroy (GTK_WIDGET (scg
->label
));
1339 gtk_widget_destroy (GTK_WIDGET (scg
->grid
));
1345 /* Do current scg last. */
1347 gtk_widget_destroy (GTK_WIDGET (current
->label
));
1348 gtk_widget_destroy (GTK_WIDGET (current
->grid
));
1351 wbcg
->snotebook
= tmp
;
1356 color_diff (const GdkRGBA
*a
, const GdkRGBA
*b
)
1358 /* Ignoring alpha. */
1359 return ((a
->red
- b
->red
) * (a
->red
- b
->red
) +
1360 (a
->green
- b
->green
) * (a
->green
- b
->green
) +
1361 (a
->blue
- b
->blue
) * (a
->blue
- b
->blue
));
1366 cb_adjust_foreground_attributes (PangoAttribute
*attribute
,
1369 const GdkRGBA
*back
= data
;
1371 if (attribute
->klass
->type
== PANGO_ATTR_FOREGROUND
) {
1372 PangoColor
*pfore
= &((PangoAttrColor
*)attribute
)->color
;
1374 const double threshold
= 0.01;
1376 fore
.red
= pfore
->red
/ 65535.0;
1377 fore
.green
= pfore
->green
/ 65535.0;
1378 fore
.blue
= pfore
->blue
/ 65535.0;
1380 if (color_diff (&fore
, back
) < threshold
) {
1381 static const GdkRGBA black
= { 0, 0, 0, 1 };
1382 static const GdkRGBA white
= { 1, 1, 1, 1 };
1383 double back_norm
= color_diff (back
, &black
);
1385 const GdkRGBA
*ref
=
1386 back_norm
> 0.75 ? &black
: &white
;
1388 #define DO_CHANNEL(channel) \
1390 double val = fore.channel * (1 - f) + ref->channel * f; \
1391 pfore->channel = CLAMP (val, 0, 1) * 65535; \
1403 adjust_foreground_attributes (PangoAttrList
*attrs
, GtkWidget
*w
)
1406 GtkStyleContext
*ctxt
= gtk_widget_get_style_context (w
);
1408 gtk_style_context_get_background_color (ctxt
, GTK_STATE_FLAG_NORMAL
,
1412 g_printerr ("back=%s\n", gdk_rgba_to_string (&c
));
1414 pango_attr_list_unref
1415 (pango_attr_list_filter
1417 cb_adjust_foreground_attributes
,
1423 wbcg_auto_expr_value_changed (WorkbookView
*wbv
,
1424 G_GNUC_UNUSED GParamSpec
*pspec
,
1427 GtkLabel
*lbl
= GTK_LABEL (wbcg
->auto_expr_label
);
1428 GnmValue
const *v
= wbv
->auto_expr
.value
;
1431 GOFormat
const *format
= VALUE_FMT (v
);
1432 GString
*str
= g_string_new (wbv
->auto_expr
.descr
);
1433 PangoAttrList
*attrs
= NULL
;
1435 g_string_append (str
, " = ");
1438 PangoLayout
*layout
= gtk_widget_create_pango_layout (GTK_WIDGET (wbcg
->toplevel
), NULL
);
1439 gsize old_len
= str
->len
;
1440 GODateConventions
const *date_conv
= workbook_date_conv (wb_view_get_workbook (wbv
));
1441 int max_width
= go_format_is_general (format
) && VALUE_IS_NUMBER (v
)
1443 : strlen (AUTO_EXPR_SAMPLE
) - g_utf8_strlen (str
->str
, -1);
1444 GOFormatNumberError err
=
1445 format_value_layout (layout
, format
, v
,
1446 max_width
, date_conv
);
1448 case GO_FORMAT_NUMBER_OK
:
1449 case GO_FORMAT_NUMBER_DATE_ERROR
: {
1452 go_pango_translate_layout (layout
); /* translating custom attributes */
1453 g_string_append (str
, pango_layout_get_text (layout
));
1454 /* We need to shift the attribute list */
1455 atl
= pango_attr_list_ref (pango_layout_get_attributes (layout
));
1457 attrs
= pango_attr_list_new ();
1458 pango_attr_list_splice
1459 (attrs
, atl
, old_len
,
1460 str
->len
- old_len
);
1461 pango_attr_list_unref (atl
);
1462 /* Adjust colours to make text visible. */
1463 adjust_foreground_attributes
1465 gtk_widget_get_parent (GTK_WIDGET (lbl
)));
1470 case GO_FORMAT_NUMBER_INVALID_FORMAT
:
1471 g_string_append (str
, _("Invalid format"));
1474 g_object_unref (layout
);
1476 g_string_append (str
, value_peek_string (v
));
1478 gtk_label_set_text (lbl
, str
->str
);
1479 gtk_label_set_attributes (lbl
, attrs
);
1481 pango_attr_list_unref (attrs
);
1482 g_string_free (str
, TRUE
);
1484 gtk_label_set_text (lbl
, "");
1485 gtk_label_set_attributes (lbl
, NULL
);
1490 wbcg_scrollbar_visibility (WorkbookView
*wbv
,
1491 G_GNUC_UNUSED GParamSpec
*pspec
,
1494 SheetControlGUI
*scg
= wbcg_cur_scg (wbcg
);
1495 scg_adjust_preferences (scg
);
1499 wbcg_notebook_tabs_visibility (WorkbookView
*wbv
,
1500 G_GNUC_UNUSED GParamSpec
*pspec
,
1503 gtk_widget_set_visible (GTK_WIDGET (wbcg
->bnotebook
),
1504 wbv
->show_notebook_tabs
);
1509 wbcg_menu_state_update (WorkbookControl
*wbc
, int flags
)
1511 WBCGtk
*wbcg
= (WBCGtk
*)wbc
;
1512 SheetControlGUI
*scg
= wbcg_cur_scg (wbcg
);
1513 SheetView
const *sv
= wb_control_cur_sheet_view (wbc
);
1514 Sheet
const *sheet
= wb_control_cur_sheet (wbc
);
1515 gboolean
const has_guru
= wbc_gtk_get_guru (wbcg
) != NULL
;
1516 gboolean edit_object
= scg
!= NULL
&&
1517 (scg
->selected_objects
!= NULL
|| wbcg
->new_object
!= NULL
);
1518 gboolean has_print_area
;
1520 if (MS_INSERT_COLS
& flags
)
1521 wbc_gtk_set_action_sensitivity (wbcg
, "InsertColumns",
1522 sv
->enable_insert_cols
);
1523 if (MS_INSERT_ROWS
& flags
)
1524 wbc_gtk_set_action_sensitivity (wbcg
, "InsertRows",
1525 sv
->enable_insert_rows
);
1526 if (MS_INSERT_CELLS
& flags
)
1527 wbc_gtk_set_action_sensitivity (wbcg
, "InsertCells",
1528 sv
->enable_insert_cells
);
1529 if (MS_SHOWHIDE_DETAIL
& flags
) {
1530 wbc_gtk_set_action_sensitivity (wbcg
, "DataOutlineShowDetail",
1531 sheet
->priv
->enable_showhide_detail
);
1532 wbc_gtk_set_action_sensitivity (wbcg
, "DataOutlineHideDetail",
1533 sheet
->priv
->enable_showhide_detail
);
1535 if (MS_PASTE_SPECIAL
& flags
)
1536 wbc_gtk_set_action_sensitivity (wbcg
, "EditPasteSpecial",
1537 // Inter-process paste special is now allowed
1538 !gnm_app_clipboard_is_cut () &&
1540 if (MS_PRINT_SETUP
& flags
)
1541 wbc_gtk_set_action_sensitivity (wbcg
, "FilePageSetup", !has_guru
);
1542 if (MS_SEARCH_REPLACE
& flags
)
1543 wbc_gtk_set_action_sensitivity (wbcg
, "EditReplace", !has_guru
);
1544 if (MS_DEFINE_NAME
& flags
) {
1545 wbc_gtk_set_action_sensitivity (wbcg
, "EditNames", !has_guru
);
1546 wbc_gtk_set_action_sensitivity (wbcg
, "InsertNames", !has_guru
);
1548 if (MS_CONSOLIDATE
& flags
)
1549 wbc_gtk_set_action_sensitivity (wbcg
, "DataConsolidate", !has_guru
);
1550 if (MS_FILTER_STATE_CHANGED
& flags
)
1551 wbc_gtk_set_action_sensitivity (wbcg
, "DataFilterShowAll", sheet
->has_filtered_rows
);
1552 if (MS_SHOW_PRINTAREA
& flags
) {
1553 GnmRange
*print_area
= sheet_get_nominal_printarea (sheet
);
1554 has_print_area
= (print_area
!= NULL
);
1555 g_free (print_area
);
1556 wbc_gtk_set_action_sensitivity (wbcg
, "FilePrintAreaClear", has_print_area
);
1557 wbc_gtk_set_action_sensitivity (wbcg
, "FilePrintAreaShow", has_print_area
);
1559 if (MS_PAGE_BREAKS
& flags
) {
1560 gint col
= sv
->edit_pos
.col
;
1561 gint row
= sv
->edit_pos
.row
;
1562 GnmPrintInformation
*pi
= sheet
->print_info
;
1563 char const* new_label
= NULL
;
1564 char const *new_tip
= NULL
;
1566 if (pi
->page_breaks
.v
!= NULL
&&
1567 gnm_page_breaks_get_break (pi
->page_breaks
.v
, col
) == GNM_PAGE_BREAK_MANUAL
) {
1568 new_label
= _("Remove Column Page Break");
1569 new_tip
= _("Remove the page break to the left of the current column");
1571 new_label
= _("Add Column Page Break");
1572 new_tip
= _("Add a page break to the left of the current column");
1574 wbc_gtk_set_action_label (wbcg
, "FilePrintAreaToggleColPageBreak",
1575 NULL
, new_label
, new_tip
);
1576 if (pi
->page_breaks
.h
!= NULL
&&
1577 gnm_page_breaks_get_break (pi
->page_breaks
.h
, col
) == GNM_PAGE_BREAK_MANUAL
) {
1578 new_label
= _("Remove Row Page Break");
1579 new_tip
= _("Remove the page break above the current row");
1581 new_label
= _("Add Row Page Break");
1582 new_tip
= _("Add a page break above current row");
1584 wbc_gtk_set_action_label (wbcg
, "FilePrintAreaToggleRowPageBreak",
1585 NULL
, new_label
, new_tip
);
1586 wbc_gtk_set_action_sensitivity (wbcg
, "FilePrintAreaToggleRowPageBreak",
1588 wbc_gtk_set_action_sensitivity (wbcg
, "FilePrintAreaToggleColPageBreak",
1590 wbc_gtk_set_action_sensitivity (wbcg
, "FilePrintAreaClearAllPageBreak",
1591 print_info_has_manual_breaks (sheet
->print_info
));
1593 if (MS_SELECT_OBJECT
& flags
) {
1594 wbc_gtk_set_action_sensitivity (wbcg
, "EditSelectObject",
1595 sheet
->sheet_objects
!= NULL
);
1598 if (MS_FREEZE_VS_THAW
& flags
) {
1599 /* Cheat and use the same accelerator for both states because
1600 * we don't reset it when the label changes */
1601 char const* label
= gnm_sheet_view_is_frozen (sv
)
1602 ? _("Un_freeze Panes")
1603 : _("_Freeze Panes");
1604 char const *new_tip
= gnm_sheet_view_is_frozen (sv
)
1605 ? _("Unfreeze the top left of the sheet")
1606 : _("Freeze the top left of the sheet");
1607 wbc_gtk_set_action_label (wbcg
, "ViewFreezeThawPanes", NULL
, label
, new_tip
);
1610 if (MS_ADD_VS_REMOVE_FILTER
& flags
) {
1611 gboolean
const has_filter
= (NULL
!= gnm_sheet_view_editpos_in_filter (sv
));
1612 GnmFilter
*f
= gnm_sheet_view_selection_intersects_filter_rows (sv
);
1614 char const *new_tip
;
1615 gboolean active
= TRUE
;
1618 if ((!has_filter
) && (NULL
!= f
)) {
1619 gchar
*nlabel
= NULL
;
1620 if (NULL
!= (r
= gnm_sheet_view_selection_extends_filter (sv
, f
))) {
1622 nlabel
= g_strdup_printf
1623 (_("Extend _Auto Filter to %s"),
1624 range_as_string (r
));
1625 new_tip
= _("Extend the existing filter.");
1626 wbc_gtk_set_action_label
1627 (wbcg
, "DataAutoFilter", NULL
,
1632 nlabel
= g_strdup_printf
1633 (_("Auto Filter blocked by %s"),
1634 range_as_string (&f
->r
));
1635 new_tip
= _("The selection intersects an "
1636 "existing auto filter.");
1637 wbc_gtk_set_action_label
1638 (wbcg
, "DataAutoFilter", NULL
,
1644 ? _("Remove _Auto Filter")
1645 : _("Add _Auto Filter");
1646 new_tip
= has_filter
1647 ? _("Remove a filter")
1648 : _("Add a filter");
1649 wbc_gtk_set_action_label (wbcg
, "DataAutoFilter", NULL
, label
, new_tip
);
1652 wbc_gtk_set_action_sensitivity (wbcg
, "DataAutoFilter", active
);
1654 if (MS_COMMENT_LINKS
& flags
) {
1655 gboolean has_comment
1656 = (sheet_get_comment (sheet
, &sv
->edit_pos
) != NULL
);
1659 range_init_cellpos (&rge
, &sv
->edit_pos
);
1661 sheet_style_region_contains_link (sheet
, &rge
));
1662 wbc_gtk_set_action_sensitivity
1663 (wbcg
, "EditComment", has_comment
);
1664 wbc_gtk_set_action_sensitivity
1665 (wbcg
, "EditHyperlink", has_link
);
1668 if (MS_COMMENT_LINKS_RANGE
& flags
) {
1671 gboolean has_links
= FALSE
, has_comments
= FALSE
;
1672 gboolean sel_is_vector
= FALSE
;
1673 SheetView
*sv
= scg_view (scg
);
1674 for (l
= sv
->selections
;
1675 l
!= NULL
; l
= l
->next
) {
1676 GnmRange
const *r
= l
->data
;
1678 GnmStyleList
*styles
;
1680 styles
= sheet_style_collect_hlinks
1682 has_links
= (styles
!= NULL
);
1683 style_list_free (styles
);
1685 if (!has_comments
) {
1686 objs
= sheet_objects_get
1687 (sheet
, r
, GNM_CELL_COMMENT_TYPE
);
1688 has_comments
= (objs
!= NULL
);
1689 g_slist_free (objs
);
1691 if((count
++ > 1) && has_comments
&& has_links
)
1694 wbc_gtk_set_action_sensitivity
1695 (wbcg
, "EditClearHyperlinks", has_links
);
1696 wbc_gtk_set_action_sensitivity
1697 (wbcg
, "EditClearComments", has_comments
);
1699 GnmRange
const *r
= sv
->selections
->data
;
1700 sel_is_vector
= (range_width (r
) == 1 ||
1701 range_height (r
) == 1) &&
1702 !range_is_singleton (r
);
1704 wbc_gtk_set_action_sensitivity
1705 (wbcg
, "InsertSortDecreasing", sel_is_vector
);
1706 wbc_gtk_set_action_sensitivity
1707 (wbcg
, "InsertSortIncreasing", sel_is_vector
);
1709 if (MS_FILE_EXPORT_IMPORT
& flags
) {
1710 Workbook
*wb
= wb_control_get_workbook (wbc
);
1711 gboolean has_export_info
= workbook_get_file_exporter (wb
) &&
1712 workbook_get_last_export_uri (wb
);
1713 wbc_gtk_set_action_sensitivity (wbcg
, "DataExportRepeat", has_export_info
);
1714 if (has_export_info
) {
1715 gchar
*base
= go_basename_from_uri (workbook_get_last_export_uri (wb
));
1716 gchar
*new_label
= g_strdup_printf (_("Repeat Export to %s"),
1719 wbc_gtk_set_action_label (wbcg
, "DataExportRepeat", NULL
,
1720 new_label
, N_("Repeat the last data export"));
1723 wbc_gtk_set_action_label (wbcg
, "DataExportRepeat", NULL
,
1724 N_("Repeat Export"), N_("Repeat the last data export"));
1727 gboolean
const has_slicer
= (NULL
!= gnm_sheet_view_editpos_in_slicer (sv
));
1728 char const* label
= has_slicer
1729 ? _("Remove _Data Slicer")
1730 : _("Create _Data Slicer");
1731 char const *new_tip
= has_slicer
1732 ? _("Remove a Data Slicer")
1733 : _("Create a Data Slicer");
1734 wbc_gtk_set_action_label (wbcg
, "DataSlicer", NULL
, label
, new_tip
);
1735 wbc_gtk_set_action_sensitivity (wbcg
, "DataSlicerRefresh", has_slicer
);
1736 wbc_gtk_set_action_sensitivity (wbcg
, "DataSlicerEdit", has_slicer
);
1741 wbcg_undo_redo_labels (WorkbookControl
*wbc
, char const *undo
, char const *redo
)
1743 WBCGtk
*wbcg
= (WBCGtk
*)wbc
;
1744 g_return_if_fail (wbcg
!= NULL
);
1746 wbc_gtk_set_action_label (wbcg
, "Redo", _("_Redo"), redo
, NULL
);
1747 wbc_gtk_set_action_label (wbcg
, "Undo", _("_Undo"), undo
, NULL
);
1748 wbc_gtk_set_action_sensitivity (wbcg
, "Repeat", undo
!= NULL
);
1752 wbcg_paste_from_selection (WorkbookControl
*wbc
, GnmPasteTarget
const *pt
)
1754 gnm_x_request_clipboard ((WBCGtk
*)wbc
, pt
);
1758 wbcg_claim_selection (WorkbookControl
*wbc
)
1760 WBCGtk
*wbcg
= (WBCGtk
*)wbc
;
1761 GdkDisplay
*display
= gtk_widget_get_display (GTK_WIDGET (wbcg_toplevel (wbcg
)));
1762 return gnm_x_claim_clipboard (display
);
1766 wbcg_show_save_dialog (WBCGtk
*wbcg
, Workbook
*wb
)
1770 char const *wb_uri
= go_doc_get_uri (GO_DOC (wb
));
1774 char *base
= go_basename_from_uri (wb_uri
);
1775 char *display
= g_markup_escape_text (base
, -1);
1776 msg
= g_strdup_printf (
1777 _("Save changes to workbook '%s' before closing?"),
1782 msg
= g_strdup (_("Save changes to workbook before closing?"));
1785 d
= gnm_message_dialog_create (wbcg_toplevel (wbcg
),
1786 GTK_DIALOG_DESTROY_WITH_PARENT
,
1787 GTK_MESSAGE_WARNING
,
1789 _("If you close without saving, changes will be discarded."));
1790 atk_object_set_role (gtk_widget_get_accessible (d
), ATK_ROLE_ALERT
);
1792 go_gtk_dialog_add_button (GTK_DIALOG(d
), _("Discard"),
1793 GTK_STOCK_DELETE
, GTK_RESPONSE_NO
);
1794 go_gtk_dialog_add_button (GTK_DIALOG(d
), _("Don't close"),
1795 GNM_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
);
1797 gtk_dialog_add_button (GTK_DIALOG(d
), GNM_STOCK_SAVE
, GTK_RESPONSE_YES
);
1798 gtk_dialog_set_default_response (GTK_DIALOG (d
), GTK_RESPONSE_YES
);
1799 ret
= go_gtk_dialog_run (GTK_DIALOG (d
), wbcg_toplevel (wbcg
));
1806 * wbcg_close_if_user_permits : If the workbook is dirty the user is
1807 * prompted to see if they should exit.
1813 * 3) save any future dirty
1814 * 4) do not save any future dirty
1817 wbcg_close_if_user_permits (WBCGtk
*wbcg
, WorkbookView
*wb_view
)
1819 gboolean can_close
= TRUE
;
1820 gboolean done
= FALSE
;
1823 Workbook
*wb
= wb_view_get_workbook (wb_view
);
1824 static int in_can_close
;
1826 g_return_val_if_fail (GNM_IS_WORKBOOK (wb
), 0);
1830 in_can_close
= TRUE
;
1832 while (go_doc_is_dirty (GO_DOC (wb
)) && !done
) {
1834 button
= wbcg_show_save_dialog(wbcg
, wb
);
1837 case GTK_RESPONSE_YES
:
1838 done
= gui_file_save (wbcg
, wb_view
);
1841 case GNM_RESPONSE_SAVE_ALL
:
1842 done
= gui_file_save (wbcg
, wb_view
);
1845 case GTK_RESPONSE_NO
:
1847 go_doc_set_dirty (GO_DOC (wb
), FALSE
);
1850 case GNM_RESPONSE_DISCARD_ALL
:
1852 go_doc_set_dirty (GO_DOC (wb
), FALSE
);
1855 default: /* CANCEL */
1862 in_can_close
= FALSE
;
1865 gnm_x_store_clipboard_if_needed (wb
);
1866 g_object_unref (wb
);
1868 case GNM_RESPONSE_SAVE_ALL
:
1870 case GNM_RESPONSE_DISCARD_ALL
:
1883 * Returns TRUE if the control should NOT be closed.
1886 wbc_gtk_close (WBCGtk
*wbcg
)
1888 WorkbookView
*wb_view
= wb_control_view (GNM_WBC (wbcg
));
1890 g_return_val_if_fail (GNM_IS_WORKBOOK_VIEW (wb_view
), TRUE
);
1891 g_return_val_if_fail (wb_view
->wb_controls
!= NULL
, TRUE
);
1893 /* If we were editing when the quit request came make sure we don't
1894 * lose any entered text
1896 if (!wbcg_edit_finish (wbcg
, WBC_EDIT_ACCEPT
, NULL
))
1899 /* If something is still using the control
1900 * eg progress meter for a new book */
1901 if (G_OBJECT (wbcg
)->ref_count
> 1)
1904 /* This is the last control */
1905 if (wb_view
->wb_controls
->len
<= 1) {
1906 Workbook
*wb
= wb_view_get_workbook (wb_view
);
1908 g_return_val_if_fail (GNM_IS_WORKBOOK (wb
), TRUE
);
1909 g_return_val_if_fail (wb
->wb_views
!= NULL
, TRUE
);
1911 /* This is the last view */
1912 if (wb
->wb_views
->len
<= 1) {
1913 if (wbcg_close_if_user_permits (wbcg
, wb_view
) == 0)
1918 g_object_unref (wb_view
);
1920 g_object_unref (wbcg
);
1922 _gnm_app_flag_windows_changed ();
1928 cb_cancel_input (WBCGtk
*wbcg
)
1930 wbcg_edit_finish (wbcg
, WBC_EDIT_REJECT
, NULL
);
1934 cb_accept_input (WBCGtk
*wbcg
)
1936 wbcg_edit_finish (wbcg
, WBC_EDIT_ACCEPT
, NULL
);
1940 cb_accept_input_wo_ac (WBCGtk
*wbcg
)
1942 wbcg_edit_finish (wbcg
, WBC_EDIT_ACCEPT_WO_AC
, NULL
);
1946 cb_accept_input_array (WBCGtk
*wbcg
)
1948 wbcg_edit_finish (wbcg
, WBC_EDIT_ACCEPT_ARRAY
, NULL
);
1952 cb_accept_input_selected_cells (WBCGtk
*wbcg
)
1954 wbcg_edit_finish (wbcg
, WBC_EDIT_ACCEPT_RANGE
, NULL
);
1958 cb_accept_input_selected_merged (WBCGtk
*wbcg
)
1960 Sheet
*sheet
= wbcg
->editing_sheet
;
1962 #warning FIXME: this creates 2 undo items!
1963 if (wbcg_is_editing (wbcg
) &&
1964 wbcg_edit_finish (wbcg
, WBC_EDIT_ACCEPT
, NULL
)) {
1965 WorkbookControl
*wbc
= GNM_WBC (wbcg
);
1966 WorkbookView
*wbv
= wb_control_view (wbc
);
1967 SheetView
*sv
= sheet_get_view (sheet
, wbv
);
1968 GnmRange sel
= *(selection_first_range (sv
, NULL
, NULL
));
1969 GSList
*selection
= g_slist_prepend (NULL
, &sel
);
1971 cmd_merge_cells (wbc
, sheet
, selection
, FALSE
);
1972 g_slist_free (selection
);
1977 /* cb_accept_input_sheets_collector (Sheet *sheet, GSList **n) */
1979 /* if (sheet->visibility == GNM_SHEET_VISIBILITY_VISIBLE) */
1980 /* (*n) = g_slist_prepend (*n, sheet); */
1984 /* cb_accept_input_sheets (WBCGtk *wbcg) */
1986 /* GSList *sheets = workbook_sheets */
1987 /* (wb_control_get_workbook (GNM_WBC (wbcg))); */
1988 /* GSList *vis_sheets = NULL; */
1990 /* g_slist_foreach (sheets, */
1991 /* (GFunc) cb_accept_input_sheets_collector, */
1994 /* wbcg_edit_multisheet_finish (wbcg, WBC_EDIT_ACCEPT, NULL, vis_sheets); */
1996 /* g_slist_free (sheets); */
1997 /* g_slist_free (vis_sheets); */
2001 /* cb_accept_input_menu_sensitive_sheets_counter (Sheet *sheet, gint *n) */
2003 /* if (sheet->visibility == GNM_SHEET_VISIBILITY_VISIBLE) */
2007 /* static gboolean */
2008 /* cb_accept_input_menu_sensitive_sheets (WBCGtk *wbcg) */
2010 /* GSList *sheets = workbook_sheets */
2011 /* (wb_control_get_workbook (GNM_WBC (wbcg))); */
2014 /* g_slist_foreach (sheets, */
2015 /* (GFunc) cb_accept_input_menu_sensitive_sheets_counter, */
2017 /* g_slist_free (sheets); */
2018 /* return (n > 1); */
2021 /* static gboolean */
2022 /* cb_accept_input_menu_sensitive_selected_sheets (WBCGtk *wbcg) */
2024 /* GSList *sheets = workbook_sheets */
2025 /* (wb_control_get_workbook (GNM_WBC (wbcg))); */
2028 /* g_slist_foreach (sheets, */
2029 /* (GFunc) cb_accept_input_menu_sensitive_sheets_counter, */
2031 /* g_slist_free (sheets); */
2032 /* return (n > 2); */
2036 cb_accept_input_menu_sensitive_selected_cells (WBCGtk
*wbcg
)
2038 WorkbookControl
*wbc
= GNM_WBC (wbcg
);
2039 WorkbookView
*wbv
= wb_control_view (wbc
);
2040 SheetView
*sv
= sheet_get_view (wbcg
->editing_sheet
, wbv
);
2041 gboolean result
= TRUE
;
2042 GSList
*selection
= selection_get_ranges (sv
, FALSE
), *l
;
2044 for (l
= selection
; l
!= NULL
; l
= l
->next
) {
2045 GnmRange
const *sel
= l
->data
;
2046 if (sheet_range_splits_array
2047 (wbcg
->editing_sheet
, sel
, NULL
, NULL
, NULL
)) {
2052 range_fragment_free (selection
);
2057 cb_accept_input_menu_sensitive_selected_merged (WBCGtk
*wbcg
)
2059 WorkbookControl
*wbc
= GNM_WBC (wbcg
);
2060 WorkbookView
*wbv
= wb_control_view (wbc
);
2061 SheetView
*sv
= sheet_get_view (wbcg
->editing_sheet
, wbv
);
2062 GnmRange
const *sel
= selection_first_range (sv
, NULL
, NULL
);
2064 return (sel
&& !range_is_singleton (sel
) &&
2065 sv
->edit_pos
.col
== sel
->start
.col
&&
2066 sv
->edit_pos
.row
== sel
->start
.row
&&
2067 !sheet_range_splits_array
2068 (wbcg
->editing_sheet
, sel
, NULL
, NULL
, NULL
));
2072 cb_accept_input_menu (GtkMenuToolButton
*button
, WBCGtk
*wbcg
)
2074 GtkWidget
*menu
= gtk_menu_tool_button_get_menu (button
);
2075 GList
*l
, *children
= gtk_container_get_children (GTK_CONTAINER (menu
));
2077 struct AcceptInputMenu
{
2079 void (*function
) (WBCGtk
*wbcg
);
2080 gboolean (*sensitive
) (WBCGtk
*wbcg
);
2081 } const accept_input_actions
[] = {
2082 { N_("Enter in current cell"), cb_accept_input
,
2084 { N_("Enter in current cell without autocorrection"), cb_accept_input_wo_ac
,
2086 /* { N_("Enter on all non-hidden sheets"), cb_accept_input_sheets, */
2087 /* cb_accept_input_menu_sensitive_sheets}, */
2088 /* { N_("Enter on multiple sheets..."), cb_accept_input_selected_sheets, */
2089 /* cb_accept_input_menu_sensitive_selected_sheets }, */
2090 { NULL
, NULL
, NULL
},
2091 { N_("Enter in current range merged"), cb_accept_input_selected_merged
,
2092 cb_accept_input_menu_sensitive_selected_merged
},
2093 { NULL
, NULL
, NULL
},
2094 { N_("Enter in selected ranges"), cb_accept_input_selected_cells
,
2095 cb_accept_input_menu_sensitive_selected_cells
},
2096 { N_("Enter in selected ranges as array"), cb_accept_input_array
,
2097 cb_accept_input_menu_sensitive_selected_cells
},
2101 const struct AcceptInputMenu
*it
;
2103 if (children
== NULL
)
2104 for (ui
= 0; ui
< G_N_ELEMENTS (accept_input_actions
); ui
++) {
2105 it
= accept_input_actions
+ ui
;
2108 item
= gtk_image_menu_item_new_with_label
2111 g_signal_connect_swapped
2112 (G_OBJECT (item
), "activate",
2113 G_CALLBACK (it
->function
),
2115 if (wbcg
->editing_sheet
) {
2117 gtk_widget_set_sensitive
2118 (item
, (it
->sensitive
) (wbcg
));
2120 gtk_widget_set_sensitive (item
, TRUE
);
2122 gtk_widget_set_sensitive (item
, FALSE
);
2124 item
= gtk_separator_menu_item_new ();
2125 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
2126 gtk_widget_show (item
);
2129 for (ui
= 0, l
= children
;
2130 ui
< G_N_ELEMENTS (accept_input_actions
) && l
!= NULL
;
2131 ui
++, l
= l
->next
) {
2132 it
= accept_input_actions
+ ui
;
2133 if (wbcg
->editing_sheet
) {
2135 gtk_widget_set_sensitive
2136 (GTK_WIDGET (l
->data
),
2137 (it
->sensitive
) (wbcg
));
2139 gtk_widget_set_sensitive
2140 (GTK_WIDGET (l
->data
), TRUE
);
2142 gtk_widget_set_sensitive (l
->data
, FALSE
);
2146 g_list_free (children
);
2150 cb_editline_focus_in (GtkWidget
*w
, GdkEventFocus
*event
,
2153 if (!wbcg_is_editing (wbcg
))
2154 if (!wbcg_edit_start (wbcg
, FALSE
, TRUE
)) {
2156 GtkEntry
*entry
= GTK_ENTRY (w
);
2158 wbcg_focus_cur_scg (wbcg
);
2159 #warning GTK3: what can we do there for gtk3?
2161 entry
->in_drag
= FALSE
;
2163 * ->button is private, ugh. Since the text area
2164 * never gets a release event, there seems to be
2165 * no official way of returning the widget to its
2177 cb_statusbox_activate (GtkEntry
*entry
, WBCGtk
*wbcg
)
2179 WorkbookControl
*wbc
= GNM_WBC (wbcg
);
2180 wb_control_parse_and_jump (wbc
, gtk_entry_get_text (entry
));
2181 wbcg_focus_cur_scg (wbcg
);
2182 wb_view_selection_desc (wb_control_view (wbc
), TRUE
, wbc
);
2186 cb_statusbox_focus (GtkEntry
*entry
, GdkEventFocus
*event
,
2189 gtk_editable_select_region (GTK_EDITABLE (entry
), 0, 0);
2193 /******************************************************************************/
2196 dump_size_tree (GtkWidget
*w
, gpointer indent_
)
2198 int indent
= GPOINTER_TO_INT (indent_
);
2202 g_printerr ("%*s", indent
, "");
2203 if (gtk_widget_get_name (w
))
2204 g_printerr ("\"%s\" ", gtk_widget_get_name (w
));
2206 gtk_widget_get_preferred_height (w
, &h1
, &h2
);
2207 gtk_widget_get_allocation (w
, &a
);
2209 g_printerr ("%s %p viz=%d act=%dx%d minheight=%d natheight=%d\n",
2210 g_type_name_from_instance ((GTypeInstance
*)w
), w
,
2211 gtk_widget_get_visible (w
),
2215 if (GTK_IS_CONTAINER (w
)) {
2216 gtk_container_foreach (GTK_CONTAINER (w
),
2218 GINT_TO_POINTER (indent
+ 2));
2224 cb_workbook_debug_info (WBCGtk
*wbcg
)
2226 Workbook
*wb
= wb_control_get_workbook (GNM_WBC (wbcg
));
2228 if (gnm_debug_flag ("notebook-size"))
2229 dump_size_tree (GTK_WIDGET (wbcg_toplevel (wbcg
)), GINT_TO_POINTER (0));
2231 if (gnm_debug_flag ("deps")) {
2232 dependents_dump (wb
);
2235 if (gnm_debug_flag ("expr-sharer")) {
2236 GnmExprSharer
*es
= workbook_share_expressions (wb
, FALSE
);
2237 gnm_expr_sharer_report (es
);
2238 gnm_expr_sharer_destroy (es
);
2241 if (gnm_debug_flag ("style-optimize")) {
2242 workbook_optimize_style (wb
);
2245 if (gnm_debug_flag ("name-collections")) {
2246 gnm_named_expr_collection_dump (wb
->names
, "workbook");
2247 WORKBOOK_FOREACH_SHEET(wb
, sheet
, {
2248 gnm_named_expr_collection_dump (sheet
->names
,
2249 sheet
->name_unquoted
);
2255 cb_autofunction (WBCGtk
*wbcg
)
2260 if (wbcg_is_editing (wbcg
))
2263 entry
= wbcg_get_entry (wbcg
);
2264 txt
= gtk_entry_get_text (entry
);
2265 if (strncmp (txt
, "=", 1)) {
2266 if (!wbcg_edit_start (wbcg
, TRUE
, TRUE
))
2267 return; /* attempt to edit failed */
2268 gtk_entry_set_text (entry
, "=");
2269 gtk_editable_set_position (GTK_EDITABLE (entry
), 1);
2271 if (!wbcg_edit_start (wbcg
, FALSE
, TRUE
))
2272 return; /* attempt to edit failed */
2274 /* FIXME : This is crap!
2275 * When the function druid is more complete use that.
2277 gtk_editable_set_position (GTK_EDITABLE (entry
),
2278 gtk_entry_get_text_length (entry
)-1);
2283 * We must not crash on focus=NULL. We're called like that as a result of
2284 * gtk_window_set_focus (toplevel, NULL) if the first sheet view is destroyed
2285 * just after being created. This happens e.g when we cancel a file import or
2289 cb_set_focus (GtkWindow
*window
, GtkWidget
*focus
, WBCGtk
*wbcg
)
2291 if (focus
&& !gtk_window_get_focus (window
))
2292 wbcg_focus_cur_scg (wbcg
);
2295 /***************************************************************************/
2298 cb_scroll_wheel (GtkWidget
*w
, GdkEventScroll
*event
,
2301 SheetControlGUI
*scg
= wbcg_get_scg (wbcg
, wbcg_focus_cur_scg (wbcg
));
2302 Sheet
*sheet
= scg_sheet (scg
);
2303 /* scroll always operates on pane 0 */
2304 GnmPane
*pane
= scg_pane (scg
, 0);
2305 gboolean go_horiz
= (event
->direction
== GDK_SCROLL_LEFT
||
2306 event
->direction
== GDK_SCROLL_RIGHT
);
2307 gboolean go_back
= (event
->direction
== GDK_SCROLL_UP
||
2308 event
->direction
== GDK_SCROLL_LEFT
);
2311 !gtk_widget_get_realized (w
) ||
2312 event
->direction
== GDK_SCROLL_SMOOTH
)
2315 if ((event
->state
& GDK_SHIFT_MASK
))
2316 go_horiz
= !go_horiz
;
2318 if ((event
->state
& GDK_CONTROL_MASK
)) { /* zoom */
2319 int zoom
= (int)(sheet
->last_zoom_factor_used
* 100. + .5) - 10;
2321 if ((zoom
% 15) != 0) {
2322 zoom
= 15 * (int)(zoom
/15);
2332 if (0 <= zoom
&& zoom
<= 390)
2333 cmd_zoom (GNM_WBC (wbcg
), g_slist_append (NULL
, sheet
),
2334 (double) (zoom
+ 10) / 100);
2335 } else if (go_horiz
) {
2336 int col
= (pane
->last_full
.col
- pane
->first
.col
) / 4;
2340 col
= pane
->first
.col
- col
;
2342 col
= pane
->first
.col
+ col
;
2343 scg_set_left_col (pane
->simple
.scg
, col
);
2345 int row
= (pane
->last_full
.row
- pane
->first
.row
) / 4;
2349 row
= pane
->first
.row
- row
;
2351 row
= pane
->first
.row
+ row
;
2352 scg_set_top_row (pane
->simple
.scg
, row
);
2358 * Make current control size the default. Toplevel would resize
2359 * spontaneously. This makes it stay the same size until user resizes.
2362 cb_realize (GtkWindow
*toplevel
, WBCGtk
*wbcg
)
2366 g_return_if_fail (GTK_IS_WINDOW (toplevel
));
2368 gtk_widget_get_allocation (GTK_WIDGET (toplevel
), &ta
);
2369 gtk_window_set_default_size (toplevel
, ta
.width
, ta
.height
);
2371 /* if we are already initialized set the focus. Without this loading a
2372 * multpage book sometimes leaves focus on the last book rather than
2373 * the current book. Which leads to a slew of errors for keystrokes
2374 * until focus is corrected.
2376 if (wbcg
->snotebook
) {
2377 wbcg_focus_cur_scg (wbcg
);
2378 wbcg_update_menu_feedback (wbcg
, wbcg_cur_sheet (wbcg
));
2383 cb_css_parse_error (GtkCssProvider
*css
, GtkCssSection
*section
, GError
*err
)
2385 if (g_error_matches (err
, GTK_CSS_PROVIDER_ERROR
,
2386 GTK_CSS_PROVIDER_ERROR_DEPRECATED
) &&
2387 !gnm_debug_flag ("css"))
2390 g_warning ("Theme parsing error: %s", err
->message
);
2393 struct css_provider_data
{
2394 GtkCssProvider
*css
;
2399 cb_unload_providers (gpointer data_
)
2401 struct css_provider_data
*data
= data_
;
2404 for (l
= data
->screens
; l
; l
= l
->next
) {
2405 GdkScreen
*screen
= l
->data
;
2406 gtk_style_context_remove_provider_for_screen
2407 (screen
, GTK_STYLE_PROVIDER (data
->css
));
2409 g_slist_free (data
->screens
);
2410 g_object_unref (data
->css
);
2415 cb_screen_changed (GtkWidget
*widget
)
2417 GdkScreen
*screen
= gtk_widget_get_screen (widget
);
2418 GObject
*app
= gnm_app_get_app ();
2419 const char *app_key
= "css-provider";
2420 struct css_provider_data
*data
;
2422 data
= g_object_get_data (app
, app_key
);
2424 const char *resource
= "/org/gnumeric/gnumeric/ui/gnumeric.css";
2425 GBytes
*cssbytes
= g_resources_lookup_data (resource
, 0, NULL
);
2426 const char *csstext
= g_bytes_get_data (cssbytes
, NULL
);
2427 gboolean debug
= gnm_debug_flag ("css");
2429 data
= g_new (struct css_provider_data
, 1);
2430 data
->css
= gtk_css_provider_new ();
2431 data
->screens
= NULL
;
2434 g_printerr ("Loading style from %s\n", resource
);
2436 g_signal_connect (data
->css
, "parsing-error",
2437 G_CALLBACK (cb_css_parse_error
),
2440 gtk_css_provider_load_from_data (data
->css
, csstext
, -1, NULL
);
2441 g_object_set_data_full (app
, app_key
, data
, cb_unload_providers
);
2442 g_bytes_unref (cssbytes
);
2445 if (screen
&& !g_slist_find (data
->screens
, screen
)) {
2446 gtk_style_context_add_provider_for_screen
2448 GTK_STYLE_PROVIDER (data
->css
),
2449 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
);
2450 data
->screens
= g_slist_prepend (data
->screens
, screen
);
2455 wbcg_set_status_text (WBCGtk
*wbcg
, char const *text
)
2457 g_return_if_fail (GNM_IS_WBC_GTK (wbcg
));
2458 gtk_statusbar_pop (GTK_STATUSBAR (wbcg
->status_text
), 0);
2459 gtk_statusbar_push (GTK_STATUSBAR (wbcg
->status_text
), 0, text
);
2463 set_visibility (WBCGtk
*wbcg
,
2464 char const *action_name
,
2467 GtkWidget
*w
= g_hash_table_lookup (wbcg
->visibility_widgets
, action_name
);
2469 gtk_widget_set_visible (w
, visible
);
2470 wbc_gtk_set_toggle_action_state (wbcg
, action_name
, visible
);
2475 wbcg_toggle_visibility (WBCGtk
*wbcg
, GtkToggleAction
*action
)
2477 if (!wbcg
->updating_ui
&& wbcg_ui_update_begin (wbcg
)) {
2478 char const *name
= gtk_action_get_name (GTK_ACTION (action
));
2479 set_visibility (wbcg
, name
,
2480 gtk_toggle_action_get_active (action
));
2481 wbcg_ui_update_end (wbcg
);
2486 cb_visibility (char const *action
, GtkWidget
*orig_widget
, WBCGtk
*new_wbcg
)
2488 set_visibility (new_wbcg
, action
, gtk_widget_get_visible (orig_widget
));
2492 wbcg_copy_toolbar_visibility (WBCGtk
*new_wbcg
,
2495 g_hash_table_foreach (wbcg
->visibility_widgets
,
2496 (GHFunc
)cb_visibility
, new_wbcg
);
2501 wbcg_set_end_mode (WBCGtk
*wbcg
, gboolean flag
)
2503 g_return_if_fail (GNM_IS_WBC_GTK (wbcg
));
2505 if (!wbcg
->last_key_was_end
!= !flag
) {
2506 const char *txt
= flag
? _("END") : "";
2507 wbcg_set_status_text (wbcg
, txt
);
2508 wbcg
->last_key_was_end
= flag
;
2512 static PangoFontDescription
*
2513 settings_get_font_desc (GtkSettings
*settings
)
2515 PangoFontDescription
*font_desc
;
2518 g_object_get (settings
, "gtk-font-name", &font_str
, NULL
);
2519 font_desc
= pango_font_description_from_string (
2520 font_str
? font_str
: "sans 10");
2527 cb_update_item_bar_font (GtkWidget
*w
)
2529 SheetControlGUI
*scg
= get_scg (w
);
2530 sc_resize ((SheetControl
*)scg
, TRUE
);
2534 cb_desktop_font_changed (GtkSettings
*settings
, GParamSpec
*pspec
,
2537 if (wbcg
->font_desc
)
2538 pango_font_description_free (wbcg
->font_desc
);
2539 wbcg
->font_desc
= settings_get_font_desc (settings
);
2540 gtk_container_foreach (GTK_CONTAINER (wbcg
->snotebook
),
2541 (GtkCallback
)cb_update_item_bar_font
, NULL
);
2545 wbcg_get_screen (WBCGtk
*wbcg
)
2547 return gtk_widget_get_screen (wbcg
->notebook_area
);
2550 static GtkSettings
*
2551 wbcg_get_gtk_settings (WBCGtk
*wbcg
)
2553 return gtk_settings_get_for_screen (wbcg_get_screen (wbcg
));
2556 /* ------------------------------------------------------------------------- */
2559 show_gui (WBCGtk
*wbcg
)
2561 SheetControlGUI
*scg
;
2562 WorkbookView
*wbv
= wb_control_view (GNM_WBC (wbcg
));
2566 GdkScreen
*screen
= wbcg_get_screen (wbcg
);
2568 /* In a Xinerama setup, we want the geometry of the actual display
2569 * unit, if available. See bug 59902. */
2570 gdk_screen_get_monitor_geometry (screen
, 0, &rect
);
2571 sx
= MAX (rect
.width
, 600);
2572 sy
= MAX (rect
.height
, 200);
2574 fx
= gnm_conf_get_core_gui_window_x ();
2575 fy
= gnm_conf_get_core_gui_window_y ();
2577 /* Successfully parsed geometry string and urged WM to comply */
2578 if (NULL
!= wbcg
->preferred_geometry
&& NULL
!= wbcg
->toplevel
&&
2579 gtk_window_parse_geometry (GTK_WINDOW (wbcg
->toplevel
),
2580 wbcg
->preferred_geometry
)) {
2581 g_free (wbcg
->preferred_geometry
);
2582 wbcg
->preferred_geometry
= NULL
;
2583 } else if (wbcg
->snotebook
!= NULL
&&
2585 (wbv
->preferred_width
> 0 || wbv
->preferred_height
> 0)) {
2586 /* Set grid size to preferred width */
2587 int pwidth
= MIN(wbv
->preferred_width
, gdk_screen_get_width (screen
));
2588 int pheight
= MIN(wbv
->preferred_height
, gdk_screen_get_height (screen
));
2589 GtkRequisition requisition
;
2591 pwidth
= pwidth
> 0 ? pwidth
: -1;
2592 pheight
= pheight
> 0 ? pheight
: -1;
2593 gtk_widget_set_size_request (GTK_WIDGET (wbcg
->notebook_area
),
2595 gtk_widget_get_preferred_size (GTK_WIDGET (wbcg
->toplevel
),
2596 &requisition
, NULL
);
2597 /* We want to test if toplevel is bigger than screen.
2598 * gtk_widget_size_request tells us the space
2599 * allocated to the toplevel proper, but not how much is
2600 * need for WM decorations or a possible panel.
2602 * The test below should very rarely maximize when there is
2603 * actually room on the screen.
2605 * We maximize instead of resizing for two reasons:
2606 * - The preferred width / height is restored with one click on
2608 * - We don't have to guess what size we should resize to.
2610 if (requisition
.height
+ 20 > rect
.height
||
2611 requisition
.width
> rect
.width
) {
2612 gtk_window_maximize (GTK_WINDOW (wbcg
->toplevel
));
2614 gtk_window_set_default_size
2615 (wbcg_toplevel (wbcg
),
2616 requisition
.width
, requisition
.height
);
2620 gtk_window_set_default_size (wbcg_toplevel (wbcg
), sx
* fx
, sy
* fy
);
2623 scg
= wbcg_cur_scg (wbcg
);
2625 wbcg_set_direction (scg
);
2627 gtk_widget_show (GTK_WIDGET (wbcg_toplevel (wbcg
)));
2629 /* rehide headers if necessary */
2630 if (NULL
!= scg
&& wbcg_cur_sheet (wbcg
))
2631 scg_adjust_preferences (scg
);
2633 gtk_widget_set_size_request (GTK_WIDGET (wbcg
->notebook_area
),
2639 wbcg_get_label_for_position (WBCGtk
*wbcg
, GtkWidget
*source
,
2643 GtkWidget
*last_visible
= NULL
;
2645 g_return_val_if_fail (GNM_IS_WBC_GTK (wbcg
), NULL
);
2647 n
= wbcg_get_n_scg (wbcg
);
2648 for (i
= 0; i
< n
; i
++) {
2649 GtkWidget
*label
= gnm_notebook_get_nth_label (wbcg
->bnotebook
, i
);
2653 if (!gtk_widget_get_visible (label
))
2656 gtk_widget_get_allocation (label
, &la
);
2662 * We are left of this label's right edge. Use it
2663 * even if we are far left of the label.
2668 last_visible
= label
;
2671 return last_visible
;
2675 wbcg_is_local_drag (WBCGtk
*wbcg
, GtkWidget
*source_widget
)
2677 GtkWidget
*top
= (GtkWidget
*)wbcg_toplevel (wbcg
);
2678 return GNM_IS_PANE (source_widget
) &&
2679 gtk_widget_get_toplevel (source_widget
) == top
;
2682 cb_wbcg_drag_motion (GtkWidget
*widget
, GdkDragContext
*context
,
2683 gint x
, gint y
, guint time
, WBCGtk
*wbcg
)
2685 GtkWidget
*source_widget
= gtk_drag_get_source_widget (context
);
2687 if (GNM_IS_NOTEBOOK (gtk_widget_get_parent (source_widget
))) {
2688 /* The user wants to reorder sheets. We simulate a
2689 * drag motion over a label.
2691 GtkWidget
*label
= wbcg_get_label_for_position (wbcg
, source_widget
, x
);
2692 return cb_sheet_label_drag_motion (label
, context
, x
, y
,
2694 } else if (wbcg_is_local_drag (wbcg
, source_widget
))
2695 gnm_pane_object_autoscroll (GNM_PANE (source_widget
),
2696 context
, x
, y
, time
);
2702 cb_wbcg_drag_leave (GtkWidget
*widget
, GdkDragContext
*context
,
2703 guint time
, WBCGtk
*wbcg
)
2705 GtkWidget
*source_widget
= gtk_drag_get_source_widget (context
);
2707 g_return_if_fail (GNM_IS_WBC_GTK (wbcg
));
2709 if (GNM_IS_NOTEBOOK (gtk_widget_get_parent (source_widget
)))
2711 g_object_get_data (G_OBJECT (source_widget
), "arrow"));
2712 else if (wbcg_is_local_drag (wbcg
, source_widget
))
2713 gnm_pane_slide_stop (GNM_PANE (source_widget
));
2717 cb_wbcg_drag_data_received (GtkWidget
*widget
, GdkDragContext
*context
,
2718 gint x
, gint y
, GtkSelectionData
*selection_data
,
2719 guint info
, guint time
, WBCGtk
*wbcg
)
2721 gchar
*target_type
= gdk_atom_name (gtk_selection_data_get_target (selection_data
));
2723 if (!strcmp (target_type
, "text/uri-list")) { /* filenames from nautilus */
2724 scg_drag_data_received (wbcg_cur_scg (wbcg
),
2725 gtk_drag_get_source_widget (context
), 0, 0,
2727 } else if (!strcmp (target_type
, "GNUMERIC_SHEET")) {
2728 /* The user wants to reorder the sheets but hasn't dropped
2729 * the sheet onto a label. Never mind. We figure out
2730 * where the arrow is currently located and simulate a drop
2732 GtkWidget
*label
= wbcg_get_label_for_position (wbcg
,
2733 gtk_drag_get_source_widget (context
), x
);
2734 cb_sheet_label_drag_data_received (label
, context
, x
, y
,
2735 selection_data
, info
, time
, wbcg
);
2737 GtkWidget
*source_widget
= gtk_drag_get_source_widget (context
);
2738 if (wbcg_is_local_drag (wbcg
, source_widget
))
2739 g_printerr ("autoscroll complete - stop it\n");
2741 scg_drag_data_received (wbcg_cur_scg (wbcg
),
2742 source_widget
, 0, 0, selection_data
);
2744 g_free (target_type
);
2747 static void cb_cs_go_up (WBCGtk
*wbcg
)
2748 { wb_control_navigate_to_cell (GNM_WBC (wbcg
), navigator_top
); }
2749 static void cb_cs_go_down (WBCGtk
*wbcg
)
2750 { wb_control_navigate_to_cell (GNM_WBC (wbcg
), navigator_bottom
); }
2751 static void cb_cs_go_left (WBCGtk
*wbcg
)
2752 { wb_control_navigate_to_cell (GNM_WBC (wbcg
), navigator_first
); }
2753 static void cb_cs_go_right (WBCGtk
*wbcg
)
2754 { wb_control_navigate_to_cell (GNM_WBC (wbcg
), navigator_last
); }
2755 static void cb_cs_go_to_cell (WBCGtk
*wbcg
) { dialog_goto_cell (wbcg
); }
2758 wbc_gtk_cell_selector_popup (G_GNUC_UNUSED GtkEntry
*entry
,
2759 G_GNUC_UNUSED GtkEntryIconPosition icon_pos
,
2760 G_GNUC_UNUSED GdkEvent
*event
,
2763 if (event
->type
== GDK_BUTTON_PRESS
) {
2764 WBCGtk
*wbcg
= data
;
2766 struct CellSelectorMenu
{
2768 void (*function
) (WBCGtk
*wbcg
);
2769 } const cell_selector_actions
[] = {
2770 { N_("Go to Top"), &cb_cs_go_up
},
2771 { N_("Go to Bottom"), &cb_cs_go_down
},
2772 { N_("Go to First"), &cb_cs_go_left
},
2773 { N_("Go to Last"), &cb_cs_go_right
},
2775 { N_("Go to Cell..."), &cb_cs_go_to_cell
}
2778 GtkWidget
*item
, *menu
= gtk_menu_new ();
2779 gboolean active
= (!wbcg_is_editing (wbcg
) &&
2780 NULL
== wbc_gtk_get_guru (wbcg
));
2782 for (ui
= 0; ui
< G_N_ELEMENTS (cell_selector_actions
); ui
++) {
2783 const struct CellSelectorMenu
*it
=
2784 cell_selector_actions
+ ui
;
2786 item
= gtk_image_menu_item_new_with_label
2789 item
= gtk_separator_menu_item_new ();
2792 g_signal_connect_swapped
2793 (G_OBJECT (item
), "activate",
2794 G_CALLBACK (it
->function
), wbcg
);
2795 gtk_widget_set_sensitive (item
, active
);
2796 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
2797 gtk_widget_show (item
);
2800 gnumeric_popup_menu (GTK_MENU (menu
), event
);
2805 wbc_gtk_create_edit_area (WBCGtk
*wbcg
)
2810 GtkWidget
*debug_button
;
2812 wbc_gtk_init_editline (wbcg
);
2813 entry
= wbcg_get_entry (wbcg
);
2815 /* Set a reasonable width for the selection box. */
2816 len
= gnm_widget_measure_string
2817 (GTK_WIDGET (wbcg_toplevel (wbcg
)),
2818 cell_coord_name (GNM_MAX_COLS
- 1, GNM_MAX_ROWS
- 1));
2820 * Add a little extra since font might be proportional and since
2821 * we also put user defined names there.
2824 gtk_widget_set_size_request (wbcg
->selection_descriptor
, len
, -1);
2826 g_signal_connect_swapped (wbcg
->cancel_button
,
2827 "clicked", G_CALLBACK (cb_cancel_input
),
2830 g_signal_connect_swapped (wbcg
->ok_button
,
2831 "clicked", G_CALLBACK (cb_accept_input
),
2833 gtk_menu_tool_button_set_menu (GTK_MENU_TOOL_BUTTON (wbcg
->ok_button
),
2835 gtk_menu_tool_button_set_arrow_tooltip_text
2836 (GTK_MENU_TOOL_BUTTON (wbcg
->ok_button
),
2837 _("Accept change in multiple cells"));
2838 g_signal_connect (wbcg
->ok_button
,
2839 "show-menu", G_CALLBACK (cb_accept_input_menu
),
2842 g_signal_connect_swapped (wbcg
->func_button
,
2843 "clicked", G_CALLBACK (cb_autofunction
),
2846 /* Dependency debugger */
2847 debug_button
= GET_GUI_ITEM ("debug_button");
2848 if (gnm_debug_flag ("notebook-size") ||
2849 gnm_debug_flag ("deps") ||
2850 gnm_debug_flag ("expr-sharer") ||
2851 gnm_debug_flag ("style-optimize") ||
2852 gnm_debug_flag ("name-collections")) {
2853 g_signal_connect_swapped (debug_button
,
2854 "clicked", G_CALLBACK (cb_workbook_debug_info
),
2857 gtk_widget_destroy (debug_button
);
2860 item
= GET_GUI_ITEM ("edit_line_entry_item");
2861 gtk_container_add (GTK_CONTAINER (item
),
2862 GTK_WIDGET (wbcg
->edit_line
.entry
));
2863 gtk_widget_show_all (GTK_WIDGET (item
));
2865 /* Do signal setup for the editing input line */
2866 g_signal_connect (G_OBJECT (entry
),
2868 G_CALLBACK (cb_editline_focus_in
), wbcg
);
2871 g_signal_connect (G_OBJECT (wbcg
->selection_descriptor
),
2873 G_CALLBACK (cb_statusbox_activate
), wbcg
);
2874 g_signal_connect (G_OBJECT (wbcg
->selection_descriptor
),
2876 G_CALLBACK (cb_statusbox_focus
), wbcg
);
2878 gtk_entry_set_icon_from_icon_name
2879 (GTK_ENTRY (wbcg
->selection_descriptor
),
2880 GTK_ENTRY_ICON_SECONDARY
, "go-jump");
2881 gtk_entry_set_icon_sensitive
2882 (GTK_ENTRY (wbcg
->selection_descriptor
),
2883 GTK_ENTRY_ICON_SECONDARY
, TRUE
);
2884 gtk_entry_set_icon_activatable
2885 (GTK_ENTRY (wbcg
->selection_descriptor
),
2886 GTK_ENTRY_ICON_SECONDARY
, TRUE
);
2888 g_signal_connect (G_OBJECT (wbcg
->selection_descriptor
),
2891 (wbc_gtk_cell_selector_popup
),
2896 wbcg_validation_msg (WorkbookControl
*wbc
, ValidationStyle v
,
2897 char const *title
, char const *msg
)
2899 WBCGtk
*wbcg
= (WBCGtk
*)wbc
;
2900 ValidationStatus res0
, res1
= GNM_VALIDATION_STATUS_VALID
; /* supress warning */
2901 char const *btn0
, *btn1
;
2902 GtkMessageType type
;
2907 case GNM_VALIDATION_STYLE_STOP
:
2908 res0
= GNM_VALIDATION_STATUS_INVALID_EDIT
;
2909 btn0
= _("_Re-Edit");
2910 res1
= GNM_VALIDATION_STATUS_INVALID_DISCARD
;
2911 btn1
= _("_Discard");
2912 type
= GTK_MESSAGE_ERROR
;
2914 case GNM_VALIDATION_STYLE_WARNING
:
2915 res0
= GNM_VALIDATION_STATUS_VALID
;
2916 btn0
= _("_Accept");
2917 res1
= GNM_VALIDATION_STATUS_INVALID_DISCARD
;
2918 btn1
= _("_Discard");
2919 type
= GTK_MESSAGE_WARNING
;
2921 case GNM_VALIDATION_STYLE_INFO
:
2922 res0
= GNM_VALIDATION_STATUS_VALID
;
2923 btn0
= GNM_STOCK_OK
;
2925 type
= GTK_MESSAGE_INFO
;
2927 case GNM_VALIDATION_STYLE_PARSE_ERROR
:
2928 res0
= GNM_VALIDATION_STATUS_INVALID_EDIT
;
2929 btn0
= _("_Re-Edit");
2930 res1
= GNM_VALIDATION_STATUS_VALID
;
2931 btn1
= _("_Accept");
2932 type
= GTK_MESSAGE_ERROR
;
2936 g_assert_not_reached ();
2939 dialog
= gtk_message_dialog_new (wbcg_toplevel (wbcg
),
2940 GTK_DIALOG_DESTROY_WITH_PARENT
,
2941 type
, GTK_BUTTONS_NONE
, "%s", msg
);
2942 gtk_dialog_add_buttons (GTK_DIALOG (dialog
),
2943 btn0
, GTK_RESPONSE_YES
,
2944 btn1
, GTK_RESPONSE_NO
,
2946 /* TODO : what to use if nothing is specified ? */
2947 /* TODO : do we want the document name here too ? */
2949 gtk_window_set_title (GTK_WINDOW (dialog
), title
);
2950 gtk_dialog_set_default_response (GTK_DIALOG (dialog
), GTK_RESPONSE_NO
);
2951 response
= go_gtk_dialog_run (GTK_DIALOG (dialog
),
2952 wbcg_toplevel (wbcg
));
2953 return ((response
== GTK_RESPONSE_NO
|| response
== GTK_RESPONSE_CANCEL
) ? res1
: res0
);
2956 #define DISCONNECT(obj,field) \
2957 if (wbcg->field) { \
2959 g_signal_handler_disconnect (obj, wbcg->field); \
2964 wbcg_view_changed (WBCGtk
*wbcg
,
2965 G_GNUC_UNUSED GParamSpec
*pspec
,
2968 WorkbookControl
*wbc
= GNM_WBC (wbcg
);
2969 Workbook
*wb
= wb_control_get_workbook (wbc
);
2970 WorkbookView
*wbv
= wb_control_view (wbc
);
2972 /* Reconnect self because we need to change data. */
2973 DISCONNECT (wbc
, sig_view_changed
);
2974 wbcg
->sig_view_changed
=
2975 g_signal_connect_object
2978 G_CALLBACK (wbcg_view_changed
),
2982 DISCONNECT (wbcg
->sig_wbv
, sig_auto_expr_text
);
2983 DISCONNECT (wbcg
->sig_wbv
, sig_auto_expr_attrs
);
2984 DISCONNECT (wbcg
->sig_wbv
, sig_show_horizontal_scrollbar
);
2985 DISCONNECT (wbcg
->sig_wbv
, sig_show_vertical_scrollbar
);
2986 DISCONNECT (wbcg
->sig_wbv
, sig_show_notebook_tabs
);
2988 g_object_remove_weak_pointer (wbcg
->sig_wbv
,
2990 wbcg
->sig_wbv
= wbv
;
2992 g_object_add_weak_pointer (wbcg
->sig_wbv
,
2994 wbcg
->sig_auto_expr_text
=
2995 g_signal_connect_object
2997 "notify::auto-expr-value",
2998 G_CALLBACK (wbcg_auto_expr_value_changed
),
3001 wbcg_auto_expr_value_changed (wbv
, NULL
, wbcg
);
3003 wbcg
->sig_show_horizontal_scrollbar
=
3004 g_signal_connect_object
3006 "notify::show-horizontal-scrollbar",
3007 G_CALLBACK (wbcg_scrollbar_visibility
),
3010 wbcg
->sig_show_vertical_scrollbar
=
3011 g_signal_connect_object
3013 "notify::show-vertical-scrollbar",
3014 G_CALLBACK (wbcg_scrollbar_visibility
),
3017 wbcg
->sig_show_notebook_tabs
=
3018 g_signal_connect_object
3020 "notify::show-notebook-tabs",
3021 G_CALLBACK (wbcg_notebook_tabs_visibility
),
3024 wbcg_notebook_tabs_visibility (wbv
, NULL
, wbcg
);
3027 DISCONNECT (old_wb
, sig_sheet_order
);
3028 DISCONNECT (old_wb
, sig_notify_uri
);
3029 DISCONNECT (old_wb
, sig_notify_dirty
);
3032 wbcg
->sig_sheet_order
=
3033 g_signal_connect_object
3035 "sheet-order-changed",
3036 G_CALLBACK (wbcg_sheet_order_changed
),
3037 wbcg
, G_CONNECT_SWAPPED
);
3039 wbcg
->sig_notify_uri
=
3040 g_signal_connect_object
3043 G_CALLBACK (wbcg_update_title
),
3044 wbcg
, G_CONNECT_SWAPPED
);
3046 wbcg
->sig_notify_dirty
=
3047 g_signal_connect_object
3050 G_CALLBACK (wbcg_update_title
),
3051 wbcg
, G_CONNECT_SWAPPED
);
3053 wbcg_update_title (wbcg
);
3059 /***************************************************************************/
3061 static GOActionComboStack
*
3062 ur_stack (WorkbookControl
*wbc
, gboolean is_undo
)
3064 WBCGtk
*wbcg
= (WBCGtk
*)wbc
;
3065 return is_undo
? wbcg
->undo_haction
: wbcg
->redo_haction
;
3069 wbc_gtk_undo_redo_truncate (WorkbookControl
*wbc
, int n
, gboolean is_undo
)
3071 go_action_combo_stack_truncate (ur_stack (wbc
, is_undo
), n
);
3075 wbc_gtk_undo_redo_pop (WorkbookControl
*wbc
, gboolean is_undo
)
3077 go_action_combo_stack_pop (ur_stack (wbc
, is_undo
), 1);
3081 wbc_gtk_undo_redo_push (WorkbookControl
*wbc
, gboolean is_undo
,
3082 char const *text
, gpointer key
)
3084 go_action_combo_stack_push (ur_stack (wbc
, is_undo
), text
, key
);
3087 /****************************************************************************/
3090 set_font_name_feedback (GtkAction
*act
, const char *family
)
3092 PangoFontDescription
*desc
= pango_font_description_new ();
3093 pango_font_description_set_family (desc
, family
);
3094 wbcg_font_action_set_font_desc (act
, desc
);
3095 pango_font_description_free (desc
);
3099 set_font_size_feedback (GtkAction
*act
, double size
)
3101 PangoFontDescription
*desc
= pango_font_description_new ();
3102 pango_font_description_set_size (desc
, size
* PANGO_SCALE
);
3103 wbcg_font_action_set_font_desc (act
, desc
);
3104 pango_font_description_free (desc
);
3107 /****************************************************************************/
3109 static WorkbookControl
*
3110 wbc_gtk_control_new (G_GNUC_UNUSED WorkbookControl
*wbc
,
3115 return (WorkbookControl
*)wbc_gtk_new (wbv
, wb
,
3116 extra
? GDK_SCREEN (extra
) : NULL
, NULL
);
3120 wbc_gtk_init_state (WorkbookControl
*wbc
)
3122 WorkbookView
*wbv
= wb_control_view (wbc
);
3123 WBCGtk
*wbcg
= WBC_GTK (wbc
);
3125 /* Share a colour history for all a view's controls */
3126 go_action_combo_color_set_group (wbcg
->back_color
, wbv
);
3127 go_action_combo_color_set_group (wbcg
->fore_color
, wbv
);
3131 wbc_gtk_style_feedback_real (WorkbookControl
*wbc
, GnmStyle
const *changes
)
3133 WorkbookView
*wb_view
= wb_control_view (wbc
);
3134 WBCGtk
*wbcg
= (WBCGtk
*)wbc
;
3136 g_return_if_fail (wb_view
!= NULL
);
3138 if (!wbcg_ui_update_begin (WBC_GTK (wbc
)))
3141 if (changes
== NULL
)
3142 changes
= wb_view
->current_style
;
3144 if (gnm_style_is_element_set (changes
, MSTYLE_FONT_BOLD
))
3145 gtk_toggle_action_set_active (wbcg
->font
.bold
,
3146 gnm_style_get_font_bold (changes
));
3147 if (gnm_style_is_element_set (changes
, MSTYLE_FONT_ITALIC
))
3148 gtk_toggle_action_set_active (wbcg
->font
.italic
,
3149 gnm_style_get_font_italic (changes
));
3150 if (gnm_style_is_element_set (changes
, MSTYLE_FONT_UNDERLINE
)) {
3151 gtk_toggle_action_set_active (wbcg
->font
.underline
,
3152 gnm_style_get_font_uline (changes
) == UNDERLINE_SINGLE
);
3153 gtk_toggle_action_set_active (wbcg
->font
.d_underline
,
3154 gnm_style_get_font_uline (changes
) == UNDERLINE_DOUBLE
);
3155 gtk_toggle_action_set_active (wbcg
->font
.sl_underline
,
3156 gnm_style_get_font_uline (changes
) == UNDERLINE_SINGLE_LOW
);
3157 gtk_toggle_action_set_active (wbcg
->font
.dl_underline
,
3158 gnm_style_get_font_uline (changes
) == UNDERLINE_DOUBLE_LOW
);
3160 if (gnm_style_is_element_set (changes
, MSTYLE_FONT_STRIKETHROUGH
))
3161 gtk_toggle_action_set_active (wbcg
->font
.strikethrough
,
3162 gnm_style_get_font_strike (changes
));
3164 if (gnm_style_is_element_set (changes
, MSTYLE_FONT_SCRIPT
)) {
3165 gtk_toggle_action_set_active (wbcg
->font
.superscript
,
3166 gnm_style_get_font_script (changes
) == GO_FONT_SCRIPT_SUPER
);
3167 gtk_toggle_action_set_active (wbcg
->font
.subscript
,
3168 gnm_style_get_font_script (changes
) == GO_FONT_SCRIPT_SUB
);
3170 gtk_toggle_action_set_active (wbcg
->font
.superscript
, FALSE
);
3171 gtk_toggle_action_set_active (wbcg
->font
.subscript
, FALSE
);
3174 if (gnm_style_is_element_set (changes
, MSTYLE_ALIGN_H
)) {
3175 GnmHAlign align
= gnm_style_get_align_h (changes
);
3176 gtk_toggle_action_set_active (wbcg
->h_align
.left
,
3177 align
== GNM_HALIGN_LEFT
);
3178 gtk_toggle_action_set_active (wbcg
->h_align
.center
,
3179 align
== GNM_HALIGN_CENTER
);
3180 gtk_toggle_action_set_active (wbcg
->h_align
.right
,
3181 align
== GNM_HALIGN_RIGHT
);
3182 gtk_toggle_action_set_active (wbcg
->h_align
.center_across_selection
,
3183 align
== GNM_HALIGN_CENTER_ACROSS_SELECTION
);
3184 go_action_combo_pixmaps_select_id (wbcg
->halignment
, align
);
3186 if (gnm_style_is_element_set (changes
, MSTYLE_ALIGN_V
)) {
3187 GnmVAlign align
= gnm_style_get_align_v (changes
);
3188 gtk_toggle_action_set_active (wbcg
->v_align
.top
,
3189 align
== GNM_VALIGN_TOP
);
3190 gtk_toggle_action_set_active (wbcg
->v_align
.bottom
,
3191 align
== GNM_VALIGN_BOTTOM
);
3192 gtk_toggle_action_set_active (wbcg
->v_align
.center
,
3193 align
== GNM_VALIGN_CENTER
);
3194 go_action_combo_pixmaps_select_id (wbcg
->valignment
, align
);
3197 if (gnm_style_is_element_set (changes
, MSTYLE_FONT_SIZE
)) {
3198 set_font_size_feedback (wbcg
->font_name_haction
,
3199 gnm_style_get_font_size (changes
));
3200 set_font_size_feedback (wbcg
->font_name_vaction
,
3201 gnm_style_get_font_size (changes
));
3204 if (gnm_style_is_element_set (changes
, MSTYLE_FONT_NAME
)) {
3205 set_font_name_feedback (wbcg
->font_name_haction
,
3206 gnm_style_get_font_name (changes
));
3207 set_font_name_feedback (wbcg
->font_name_vaction
,
3208 gnm_style_get_font_name (changes
));
3211 wbcg_ui_update_end (WBC_GTK (wbc
));
3215 cb_wbc_gtk_style_feedback (WBCGtk
*gtk
)
3217 wbc_gtk_style_feedback_real ((WorkbookControl
*)gtk
, NULL
);
3218 gtk
->idle_update_style_feedback
= 0;
3222 wbc_gtk_style_feedback (WorkbookControl
*wbc
, GnmStyle
const *changes
)
3224 WBCGtk
*wbcg
= (WBCGtk
*)wbc
;
3227 wbc_gtk_style_feedback_real (wbc
, changes
);
3228 else if (0 == wbcg
->idle_update_style_feedback
)
3229 wbcg
->idle_update_style_feedback
= g_timeout_add (200,
3230 (GSourceFunc
) cb_wbc_gtk_style_feedback
, wbc
);
3234 cb_handlebox_dock_status (GtkHandleBox
*hb
,
3235 GtkToolbar
*toolbar
, gpointer pattached
)
3237 gboolean attached
= GPOINTER_TO_INT (pattached
);
3238 gtk_toolbar_set_show_arrow (toolbar
, attached
);
3242 get_accel_label (GtkMenuItem
*item
, guint
*key
)
3244 GList
*children
= gtk_container_get_children (GTK_CONTAINER (item
));
3246 char const *res
= NULL
;
3248 *key
= GDK_KEY_VoidSymbol
;
3249 for (l
= children
; l
; l
= l
->next
) {
3250 GtkWidget
*w
= l
->data
;
3252 if (GTK_IS_ACCEL_LABEL (w
)) {
3253 *key
= gtk_label_get_mnemonic_keyval (GTK_LABEL (w
));
3254 res
= gtk_label_get_label (GTK_LABEL (w
));
3259 g_list_free (children
);
3264 check_underlines (GtkWidget
*w
, char const *path
)
3266 GList
*children
= gtk_container_get_children (GTK_CONTAINER (w
));
3267 GHashTable
*used
= g_hash_table_new_full (NULL
, NULL
, NULL
, (GDestroyNotify
)g_free
);
3270 for (l
= children
; l
; l
= l
->next
) {
3271 GtkMenuItem
*item
= GTK_MENU_ITEM (l
->data
);
3272 GtkWidget
*sub
= gtk_menu_item_get_submenu (item
);
3274 char const *label
= get_accel_label (item
, &key
);
3277 char *newpath
= g_strconcat (path
, *path
? "->" : "", label
, NULL
);
3278 check_underlines (sub
, newpath
);
3282 if (key
!= GDK_KEY_VoidSymbol
) {
3283 char const *prev
= g_hash_table_lookup (used
, GUINT_TO_POINTER (key
));
3285 /* xgettext: Translators: if this warning shows up when
3286 * running Gnumeric in your locale, the underlines need
3287 * to be moved in strings representing menu entries.
3288 * One slightly tricky point here is that in certain cases,
3289 * the same menu entry shows up in more than one menu.
3291 g_warning (_("In the `%s' menu, the key `%s' is used for both `%s' and `%s'."),
3292 path
, gdk_keyval_name (key
), prev
, label
);
3294 g_hash_table_insert (used
, GUINT_TO_POINTER (key
), g_strdup (label
));
3298 g_list_free (children
);
3299 g_hash_table_destroy (used
);
3302 /****************************************************************************/
3303 /* window list menu */
3306 cb_window_menu_activate (GObject
*action
, WBCGtk
*wbcg
)
3308 gtk_window_present (wbcg_toplevel (wbcg
));
3312 regenerate_window_menu (WBCGtk
*gtk
, Workbook
*wb
, unsigned i
)
3315 char *basename
= GO_DOC (wb
)->uri
3316 ? go_basename_from_uri (GO_DOC (wb
)->uri
)
3319 /* How many controls are there? */
3321 WORKBOOK_FOREACH_CONTROL (wb
, wbv
, wbc
, {
3322 if (GNM_IS_WBC_GTK (wbc
))
3327 WORKBOOK_FOREACH_CONTROL (wb
, wbv
, wbc
, {
3330 if (GNM_IS_WBC_GTK (wbc
) && basename
) {
3331 GString
*label
= g_string_new (NULL
);
3334 GtkActionEntry entry
;
3336 if (i
< 10) g_string_append_c (label
, '_');
3337 g_string_append_printf (label
, "%d ", i
);
3339 for (s
= basename
; *s
; s
++) {
3341 g_string_append_c (label
, '_');
3342 g_string_append_c (label
, *s
);
3346 g_string_append_printf (label
, " #%d", k
++);
3348 entry
.name
= name
= g_strdup_printf ("WindowListEntry%d", i
);
3349 entry
.stock_id
= NULL
;
3350 entry
.label
= label
->str
;
3351 entry
.accelerator
= NULL
;
3352 entry
.tooltip
= NULL
;
3353 entry
.callback
= G_CALLBACK (cb_window_menu_activate
);
3355 gtk_action_group_add_actions (gtk
->windows
.actions
,
3358 g_string_free (label
, TRUE
);
3367 cb_regenerate_window_menu (WBCGtk
*gtk
)
3369 Workbook
*wb
= wb_control_get_workbook (GNM_WBC (gtk
));
3373 /* This can happen during exit. */
3377 if (gtk
->windows
.merge_id
!= 0)
3378 gtk_ui_manager_remove_ui (gtk
->ui
, gtk
->windows
.merge_id
);
3379 gtk
->windows
.merge_id
= gtk_ui_manager_new_merge_id (gtk
->ui
);
3381 if (gtk
->windows
.actions
!= NULL
) {
3382 gtk_ui_manager_remove_action_group (gtk
->ui
,
3383 gtk
->windows
.actions
);
3384 g_object_unref (gtk
->windows
.actions
);
3386 gtk
->windows
.actions
= gtk_action_group_new ("WindowList");
3388 gtk_ui_manager_insert_action_group (gtk
->ui
, gtk
->windows
.actions
, 0);
3390 /* create the actions */
3391 i
= regenerate_window_menu (gtk
, wb
, 1); /* current wb first */
3392 for (ptr
= gnm_app_workbook_list (); ptr
!= NULL
; ptr
= ptr
->next
)
3393 if (ptr
->data
!= wb
)
3394 i
= regenerate_window_menu (gtk
, ptr
->data
, i
);
3398 char *name
= g_strdup_printf ("WindowListEntry%d", i
);
3399 gtk_ui_manager_add_ui (gtk
->ui
, gtk
->windows
.merge_id
,
3400 "/menubar/View/Windows", name
, name
,
3401 GTK_UI_MANAGER_AUTO
, TRUE
);
3407 GtkActionGroup
*actions
;
3412 cb_custom_ui_handler (GObject
*gtk_action
, WorkbookControl
*wbc
)
3414 GnmAction
*action
= g_object_get_data (gtk_action
, "GnmAction");
3415 GnmAppExtraUI
*extra_ui
= g_object_get_data (gtk_action
, "ExtraUI");
3417 g_return_if_fail (action
!= NULL
);
3418 g_return_if_fail (action
->handler
!= NULL
);
3419 g_return_if_fail (extra_ui
!= NULL
);
3421 action
->handler (action
, wbc
, extra_ui
->user_data
);
3425 cb_add_custom_ui (G_GNUC_UNUSED GnmApp
*app
,
3426 GnmAppExtraUI
*extra_ui
, WBCGtk
*gtk
)
3428 CustomUIHandle
*details
;
3430 GError
*error
= NULL
;
3431 const char *ui_substr
;
3433 details
= g_new0 (CustomUIHandle
, 1);
3434 details
->actions
= gtk_action_group_new (extra_ui
->group_name
);
3436 for (ptr
= extra_ui
->actions
; ptr
!= NULL
; ptr
= ptr
->next
) {
3437 GnmAction
*action
= ptr
->data
;
3439 GtkActionEntry entry
;
3441 entry
.name
= action
->id
;
3442 entry
.stock_id
= action
->icon_name
;
3443 entry
.label
= action
->label
;
3444 entry
.accelerator
= NULL
;
3445 entry
.tooltip
= NULL
;
3446 entry
.callback
= G_CALLBACK (cb_custom_ui_handler
);
3447 gtk_action_group_add_actions (details
->actions
, &entry
, 1, gtk
);
3448 res
= gtk_action_group_get_action (details
->actions
, action
->id
);
3449 g_object_set_data (G_OBJECT (res
), "GnmAction", action
);
3450 g_object_set_data (G_OBJECT (res
), "ExtraUI", extra_ui
);
3452 gtk_ui_manager_insert_action_group (gtk
->ui
, details
->actions
, 0);
3454 ui_substr
= strstr (extra_ui
->layout
, "<ui>");
3455 if (ui_substr
== extra_ui
->layout
)
3458 details
->merge_id
= gtk_ui_manager_add_ui_from_string
3459 (gtk
->ui
, extra_ui
->layout
, -1, ui_substr
? NULL
: &error
);
3460 if (details
->merge_id
== 0 && ui_substr
) {
3461 /* Work around bug 569724. */
3462 details
->merge_id
= gtk_ui_manager_add_ui_from_string
3463 (gtk
->ui
, ui_substr
, -1, &error
);
3467 g_message ("building menus failed: %s", error
->message
);
3468 g_error_free (error
);
3469 gtk_ui_manager_remove_action_group (gtk
->ui
, details
->actions
);
3470 g_object_unref (details
->actions
);
3473 g_hash_table_insert (gtk
->custom_uis
, extra_ui
, details
);
3477 cb_remove_custom_ui (G_GNUC_UNUSED GnmApp
*app
,
3478 GnmAppExtraUI
*extra_ui
, WBCGtk
*gtk
)
3480 CustomUIHandle
*details
= g_hash_table_lookup (gtk
->custom_uis
, extra_ui
);
3481 if (NULL
!= details
) {
3482 gtk_ui_manager_remove_ui (gtk
->ui
, details
->merge_id
);
3483 gtk_ui_manager_remove_action_group (gtk
->ui
, details
->actions
);
3484 g_object_unref (details
->actions
);
3485 g_hash_table_remove (gtk
->custom_uis
, extra_ui
);
3490 cb_init_extra_ui (GnmAppExtraUI
*extra_ui
, WBCGtk
*gtk
)
3492 cb_add_custom_ui (NULL
, extra_ui
, gtk
);
3495 /****************************************************************************/
3499 set_toolbar_style_for_position (GtkToolbar
*tb
, GtkPositionType pos
)
3501 GtkWidget
*box
= gtk_widget_get_parent (GTK_WIDGET (tb
));
3503 static const GtkOrientation orientations
[] = {
3504 GTK_ORIENTATION_VERTICAL
, GTK_ORIENTATION_VERTICAL
,
3505 GTK_ORIENTATION_HORIZONTAL
, GTK_ORIENTATION_HORIZONTAL
3508 gtk_orientable_set_orientation (GTK_ORIENTABLE (tb
),
3511 if (GTK_IS_HANDLE_BOX (box
)) {
3512 static const GtkPositionType hdlpos
[] = {
3513 GTK_POS_TOP
, GTK_POS_TOP
,
3514 GTK_POS_LEFT
, GTK_POS_LEFT
3517 gtk_handle_box_set_handle_position (GTK_HANDLE_BOX (box
),
3520 if (pos
== GTK_POS_TOP
|| pos
== GTK_POS_BOTTOM
)
3521 g_object_set (G_OBJECT (tb
), "hexpand", TRUE
, "vexpand", FALSE
, NULL
);
3523 g_object_set (G_OBJECT (tb
), "vexpand", TRUE
, "hexpand", FALSE
, NULL
);
3527 set_toolbar_position (GtkToolbar
*tb
, GtkPositionType pos
, WBCGtk
*gtk
)
3529 GtkWidget
*box
= gtk_widget_get_parent (GTK_WIDGET (tb
));
3530 GtkContainer
*zone
= GTK_CONTAINER (gtk_widget_get_parent (GTK_WIDGET (box
)));
3531 GtkContainer
*new_zone
= GTK_CONTAINER (gtk
->toolbar_zones
[pos
]);
3532 char const *name
= g_object_get_data (G_OBJECT (box
), "name");
3533 const char *key
= "toolbar-order";
3534 int n
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT (box
), key
));
3535 GList
*children
, *l
;
3538 if (zone
== new_zone
)
3543 gtk_container_remove (zone
, box
);
3544 set_toolbar_style_for_position (tb
, pos
);
3546 children
= gtk_container_get_children (new_zone
);
3547 for (l
= children
; l
; l
= l
->next
) {
3548 GObject
*child
= l
->data
;
3549 int nc
= GPOINTER_TO_INT (g_object_get_data (child
, key
));
3552 g_list_free (children
);
3554 gtk_container_add (new_zone
, box
);
3555 gtk_container_child_set (new_zone
, box
, "position", cpos
, NULL
);
3557 g_object_unref (box
);
3560 gnm_conf_set_toolbar_position (name
, pos
);
3564 cb_set_toolbar_position (GtkMenuItem
*item
, WBCGtk
*gtk
)
3566 GtkToolbar
*tb
= g_object_get_data (G_OBJECT (item
), "toolbar");
3567 GtkPositionType side
= GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item
), "side"));
3569 if (gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (item
)))
3570 set_toolbar_position (tb
, side
, gtk
);
3574 cb_tcm_hide (GtkWidget
*widget
, GtkWidget
*box
)
3576 gtk_widget_hide (box
);
3580 toolbar_context_menu (GtkToolbar
*tb
, WBCGtk
*gtk
, GdkEvent
*event
)
3582 GtkWidget
*box
= gtk_widget_get_parent (GTK_WIDGET (tb
));
3583 GtkWidget
*zone
= gtk_widget_get_parent (GTK_WIDGET (box
));
3584 GtkWidget
*menu
= gtk_menu_new ();
3586 GSList
*group
= NULL
;
3589 static const struct {
3591 GtkPositionType pos
;
3592 } const pos_items
[] = {
3593 { N_("Display toolbar above sheets"), GTK_POS_TOP
},
3594 { N_("Display toolbar to the left of sheets"), GTK_POS_LEFT
},
3595 { N_("Display toolbar to the right of sheets"), GTK_POS_RIGHT
}
3598 if (gnm_debug_flag ("toolbar-size"))
3599 dump_size_tree (GTK_WIDGET (tb
), GINT_TO_POINTER (0));
3601 for (ui
= 0; ui
< G_N_ELEMENTS (pos_items
); ui
++) {
3602 char const *text
= _(pos_items
[ui
].text
);
3603 GtkPositionType pos
= pos_items
[ui
].pos
;
3605 item
= gtk_radio_menu_item_new_with_label (group
, text
);
3606 group
= gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (item
));
3608 gtk_check_menu_item_set_active
3609 (GTK_CHECK_MENU_ITEM (item
),
3610 (zone
== gtk
->toolbar_zones
[pos
]));
3612 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
3613 g_object_set_data (G_OBJECT (item
), "toolbar", tb
);
3614 g_object_set_data (G_OBJECT (item
), "side", GINT_TO_POINTER (pos
));
3615 g_signal_connect (G_OBJECT (item
), "activate",
3616 G_CALLBACK (cb_set_toolbar_position
),
3620 item
= gtk_separator_menu_item_new ();
3621 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
3623 item
= gtk_menu_item_new_with_label (_("Hide"));
3624 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
3625 g_signal_connect (G_OBJECT (item
), "activate",
3626 G_CALLBACK (cb_tcm_hide
),
3629 gtk_widget_show_all (menu
);
3630 gnumeric_popup_menu (GTK_MENU (menu
), event
);
3634 cb_toolbar_button_press (GtkToolbar
*tb
, GdkEvent
*event
, WBCGtk
*gtk
)
3636 if (event
->type
== GDK_BUTTON_PRESS
&&
3637 event
->button
.button
== 3) {
3638 toolbar_context_menu (tb
, gtk
, event
);
3646 cb_handlebox_button_press (GtkHandleBox
*hdlbox
, GdkEvent
*event
, WBCGtk
*gtk
)
3648 if (event
->type
== GDK_BUTTON_PRESS
&&
3649 event
->button
.button
== 3) {
3650 GtkToolbar
*tb
= GTK_TOOLBAR (gtk_bin_get_child (GTK_BIN (hdlbox
)));
3651 toolbar_context_menu (tb
, gtk
, event
);
3660 cb_toolbar_activate (GtkToggleAction
*action
, WBCGtk
*wbcg
)
3662 wbcg_toggle_visibility (wbcg
, action
);
3666 cb_toolbar_box_visible (GtkWidget
*box
, G_GNUC_UNUSED GParamSpec
*pspec
,
3669 GtkToggleAction
*toggle_action
= g_object_get_data (
3670 G_OBJECT (box
), "toggle_action");
3671 char const *name
= g_object_get_data (G_OBJECT (box
), "name");
3672 gboolean visible
= gtk_widget_get_visible (box
);
3674 gtk_toggle_action_set_active (toggle_action
, visible
);
3675 if (!wbcg
->is_fullscreen
) {
3677 * We do not persist changes made going-to/while-in/leaving
3680 gnm_conf_set_toolbar_visible (name
, visible
);
3684 static struct ToolbarInfo
{
3686 const char *menu_text
;
3688 } toolbar_info
[] = {
3689 { "StandardToolbar", N_("Standard Toolbar"), "<control>7" },
3690 { "FormatToolbar", N_("Format Toolbar"), NULL
},
3691 { "ObjectToolbar", N_("Object Toolbar"), NULL
},
3692 { NULL
, NULL
, NULL
}
3697 cb_add_menus_toolbars (G_GNUC_UNUSED GtkUIManager
*ui
,
3698 GtkWidget
*w
, WBCGtk
*gtk
)
3700 if (GTK_IS_TOOLBAR (w
)) {
3701 WBCGtk
*wbcg
= (WBCGtk
*)gtk
;
3702 char const *name
= gtk_widget_get_name (w
);
3703 GtkToggleActionEntry entry
;
3704 char *toggle_name
= g_strconcat ("ViewMenuToolbar", name
, NULL
);
3705 char *tooltip
= g_strdup_printf (_("Show/Hide toolbar %s"), _(name
));
3706 gboolean visible
= gnm_conf_get_toolbar_visible (name
);
3707 int n
= g_hash_table_size (wbcg
->visibility_widgets
);
3709 const struct ToolbarInfo
*ti
;
3711 GtkPositionType pos
= gnm_conf_get_toolbar_position (name
);
3713 // See bug 761142. This isn't supposed to be necessary.
3714 gtk_style_context_invalidate (gtk_widget_get_style_context (w
));
3716 if (gnm_conf_get_detachable_toolbars ()) {
3717 box
= gtk_handle_box_new ();
3718 g_object_connect (box
,
3719 "signal::child_attached", G_CALLBACK (cb_handlebox_dock_status
), GINT_TO_POINTER (TRUE
),
3720 "signal::child_detached", G_CALLBACK (cb_handlebox_dock_status
), GINT_TO_POINTER (FALSE
),
3723 box
= gtk_box_new (GTK_ORIENTATION_HORIZONTAL
, 0);
3724 g_signal_connect (G_OBJECT (w
),
3725 "button_press_event",
3726 G_CALLBACK (cb_toolbar_button_press
),
3728 g_signal_connect (G_OBJECT (box
),
3729 "button_press_event",
3730 G_CALLBACK (cb_handlebox_button_press
),
3733 gtk_container_add (GTK_CONTAINER (box
), w
);
3734 gtk_widget_show_all (box
);
3736 gtk_widget_hide (box
);
3737 g_object_set_data (G_OBJECT (box
), "toolbar-order",
3738 GINT_TO_POINTER (n
));
3739 set_toolbar_position (GTK_TOOLBAR (w
), pos
, gtk
);
3741 g_signal_connect (box
,
3743 G_CALLBACK (cb_toolbar_box_visible
),
3745 g_object_set_data_full (G_OBJECT (box
), "name",
3747 (GDestroyNotify
)g_free
);
3750 g_hash_table_insert (wbcg
->visibility_widgets
,
3751 g_strdup (toggle_name
),
3754 gtk_toolbar_set_show_arrow (GTK_TOOLBAR (w
), TRUE
);
3755 gtk_toolbar_set_style (GTK_TOOLBAR (w
), GTK_TOOLBAR_ICONS
);
3756 gtk_toolbar_set_icon_size (GTK_TOOLBAR (w
), GTK_ICON_SIZE_SMALL_TOOLBAR
);
3758 entry
.name
= toggle_name
;
3759 entry
.stock_id
= NULL
;
3761 entry
.accelerator
= NULL
;
3762 entry
.tooltip
= tooltip
;
3763 entry
.callback
= G_CALLBACK (cb_toolbar_activate
);
3764 entry
.is_active
= visible
;
3766 for (ti
= toolbar_info
; ti
->name
; ti
++) {
3767 if (strcmp (name
, ti
->name
) == 0) {
3768 entry
.label
= _(ti
->menu_text
);
3769 entry
.accelerator
= ti
->accel
;
3774 gtk_action_group_add_toggle_actions (gtk
->toolbar
.actions
,
3776 g_object_set_data (G_OBJECT (box
), "toggle_action",
3777 gtk_action_group_get_action (gtk
->toolbar
.actions
, toggle_name
));
3778 gtk_ui_manager_add_ui (gtk
->ui
, gtk
->toolbar
.merge_id
,
3779 "/menubar/View/Toolbars", toggle_name
, toggle_name
,
3780 GTK_UI_MANAGER_AUTO
, FALSE
);
3781 wbcg
->hide_for_fullscreen
=
3782 g_slist_prepend (wbcg
->hide_for_fullscreen
,
3783 gtk_action_group_get_action (gtk
->toolbar
.actions
,
3787 g_free (toggle_name
);
3789 gtk_box_pack_start (GTK_BOX (gtk
->menu_zone
), w
, FALSE
, TRUE
, 0);
3790 gtk_widget_show_all (w
);
3795 cb_clear_menu_tip (GOCmdContext
*cc
)
3797 go_cmd_context_progress_message_set (cc
, " ");
3801 cb_show_menu_tip (GtkWidget
*proxy
, GOCmdContext
*cc
)
3803 GtkAction
*action
= g_object_get_data (G_OBJECT (proxy
), "GtkAction");
3805 g_object_get (action
, "tooltip", &tip
, NULL
);
3807 go_cmd_context_progress_message_set (cc
, _(tip
));
3810 cb_clear_menu_tip (cc
);
3814 cb_connect_proxy (G_GNUC_UNUSED GtkUIManager
*ui
,
3819 /* connect whether there is a tip or not it may change later */
3820 if (GTK_IS_MENU_ITEM (proxy
)) {
3821 g_object_set_data (G_OBJECT (proxy
), "GtkAction", action
);
3822 g_object_connect (proxy
,
3823 "signal::select", G_CALLBACK (cb_show_menu_tip
), cc
,
3824 "swapped_signal::deselect", G_CALLBACK (cb_clear_menu_tip
), cc
,
3830 cb_disconnect_proxy (G_GNUC_UNUSED GtkUIManager
*ui
,
3831 G_GNUC_UNUSED GtkAction
*action
,
3835 if (GTK_IS_MENU_ITEM (proxy
)) {
3836 g_object_set_data (G_OBJECT (proxy
), "GtkAction", NULL
);
3837 g_object_disconnect (proxy
,
3838 "any_signal::select", G_CALLBACK (cb_show_menu_tip
), cc
,
3839 "any_signal::deselect", G_CALLBACK (cb_clear_menu_tip
), cc
,
3845 cb_post_activate (G_GNUC_UNUSED GtkUIManager
*manager
, GtkAction
*action
, WBCGtk
*wbcg
)
3847 if (!wbcg_is_editing (wbcg
) && strcmp(gtk_action_get_name (action
), "EditGotoCellIndicator") != 0)
3848 wbcg_focus_cur_scg (wbcg
);
3852 cb_wbcg_window_state_event (GtkWidget
*widget
,
3853 GdkEventWindowState
*event
,
3856 gboolean new_val
= (event
->new_window_state
& GDK_WINDOW_STATE_FULLSCREEN
) != 0;
3857 if (!(event
->changed_mask
& GDK_WINDOW_STATE_FULLSCREEN
) ||
3858 new_val
== wbcg
->is_fullscreen
||
3862 wbc_gtk_set_toggle_action_state (wbcg
, "ViewFullScreen", new_val
);
3867 wbcg
->is_fullscreen
= TRUE
;
3868 for (l
= wbcg
->hide_for_fullscreen
; l
; l
= l
->next
) {
3869 GtkToggleAction
*ta
= l
->data
;
3871 gboolean active
= gtk_toggle_action_get_active (ta
);
3872 u
= go_undo_binary_new
3873 (ta
, GUINT_TO_POINTER (active
),
3874 (GOUndoBinaryFunc
)gtk_toggle_action_set_active
,
3876 wbcg
->undo_for_fullscreen
=
3877 go_undo_combine (wbcg
->undo_for_fullscreen
, u
);
3878 gtk_toggle_action_set_active (ta
, FALSE
);
3881 if (wbcg
->undo_for_fullscreen
) {
3882 go_undo_undo (wbcg
->undo_for_fullscreen
);
3883 g_object_unref (wbcg
->undo_for_fullscreen
);
3884 wbcg
->undo_for_fullscreen
= NULL
;
3886 wbcg
->is_fullscreen
= FALSE
;
3890 /****************************************************************************/
3893 cb_auto_expr_cell_changed (GtkWidget
*item
, WBCGtk
*wbcg
)
3895 WorkbookView
*wbv
= wb_control_view (GNM_WBC (wbcg
));
3896 const GnmEvalPos
*ep
;
3897 GnmExprTop
const *texpr
;
3900 if (wbcg
->updating_ui
)
3903 ep
= g_object_get_data (G_OBJECT (item
), "evalpos");
3906 "auto-expr-func", NULL
,
3907 "auto-expr-descr", NULL
,
3908 "auto-expr-eval-pos", ep
,
3911 /* Now we have the expression set. */
3912 texpr
= wbv
->auto_expr
.dep
.texpr
;
3913 v
= gnm_expr_top_get_constant (texpr
);
3916 "auto-expr-descr", value_peek_string (v
),
3921 cb_auto_expr_changed (GtkWidget
*item
, WBCGtk
*wbcg
)
3923 const GnmFunc
*func
;
3925 WorkbookView
*wbv
= wb_control_view (GNM_WBC (wbcg
));
3927 if (wbcg
->updating_ui
)
3930 func
= g_object_get_data (G_OBJECT (item
), "func");
3931 descr
= g_object_get_data (G_OBJECT (item
), "descr");
3934 "auto-expr-func", func
,
3935 "auto-expr-descr", descr
,
3936 "auto-expr-eval-pos", NULL
,
3941 cb_auto_expr_precision_toggled (GtkWidget
*item
, WBCGtk
*wbcg
)
3943 WorkbookView
*wbv
= wb_control_view (GNM_WBC (wbcg
));
3944 if (wbcg
->updating_ui
)
3947 go_object_toggle (wbv
, "auto-expr-max-precision");
3951 cb_auto_expr_insert_formula (WBCGtk
*wbcg
, gboolean below
)
3953 SheetControlGUI
*scg
= wbcg_cur_scg (wbcg
);
3954 GnmRange
const *selection
= selection_first_range (scg_view (scg
), NULL
, NULL
);
3957 gboolean multiple
, use_last_cr
;
3958 data_analysis_output_t
*dao
;
3959 analysis_tools_data_auto_expression_t
*specs
;
3961 g_return_if_fail (selection
!= NULL
);
3964 multiple
= (range_width (selection
) > 1);
3965 output
= *selection
;
3966 range_normalize (&output
);
3967 output
.start
.row
= output
.end
.row
;
3968 use_last_cr
= (range_height (selection
) > 1) && sheet_is_region_empty (scg_sheet (scg
), &output
);
3970 if (range_translate (&output
, scg_sheet (scg
), 0, 1))
3972 if (multiple
&& gnm_sheet_get_last_col (scg_sheet (scg
)) > output
.end
.col
)
3975 input
= gnm_range_dup (selection
);
3976 range_normalize (input
);
3980 multiple
= (range_height (selection
) > 1);
3981 output
= *selection
;
3982 range_normalize (&output
);
3983 output
.start
.col
= output
.end
.col
;
3984 use_last_cr
= (range_width (selection
) > 1) && sheet_is_region_empty (scg_sheet (scg
), &output
);
3986 if (range_translate (&output
, scg_sheet (scg
), 1, 0))
3988 if (multiple
&& gnm_sheet_get_last_row (scg_sheet (scg
)) > output
.end
.row
)
3991 input
= gnm_range_dup (selection
);
3992 range_normalize (input
);
3998 dao
= dao_init (NULL
, RangeOutput
);
3999 dao
->start_col
= output
.start
.col
;
4000 dao
->start_row
= output
.start
.row
;
4001 dao
->cols
= range_width (&output
);
4002 dao
->rows
= range_height (&output
);
4003 dao
->sheet
= scg_sheet (scg
);
4004 dao
->autofit_flag
= FALSE
;
4005 dao
->put_formulas
= TRUE
;
4007 specs
= g_new0 (analysis_tools_data_auto_expression_t
, 1);
4008 specs
->base
.wbc
= GNM_WBC (wbcg
);
4009 specs
->base
.input
= g_slist_prepend (NULL
, value_new_cellrange_r (scg_sheet (scg
), input
));
4011 specs
->base
.group_by
= below
? GROUPED_BY_COL
: GROUPED_BY_ROW
;
4012 specs
->base
.labels
= FALSE
;
4013 specs
->multiple
= multiple
;
4014 specs
->below
= below
;
4016 g_object_get (G_OBJECT (wb_control_view (GNM_WBC (wbcg
))),
4017 "auto-expr-func", &(specs
->func
), NULL
);
4018 if (specs
->func
== NULL
) {
4019 specs
->func
= gnm_func_lookup_or_add_placeholder ("sum");
4020 gnm_func_ref (specs
->func
);
4023 cmd_analysis_tool (GNM_WBC (wbcg
), scg_sheet (scg
),
4024 dao
, specs
, analysis_tool_auto_expression_engine
,
4029 cb_auto_expr_insert_formula_below (G_GNUC_UNUSED GtkWidget
*item
, WBCGtk
*wbcg
)
4031 cb_auto_expr_insert_formula (wbcg
, TRUE
);
4035 cb_auto_expr_insert_formula_to_side (G_GNUC_UNUSED GtkWidget
*item
, WBCGtk
*wbcg
)
4037 cb_auto_expr_insert_formula (wbcg
, FALSE
);
4042 cb_select_auto_expr (GtkWidget
*widget
, GdkEvent
*event
, WBCGtk
*wbcg
)
4045 * WARNING * WARNING * WARNING
4047 * Keep the functions in lower case.
4048 * We currently register the functions in lower case and some locales
4049 * (notably tr_TR) do not have the same encoding for tolower that
4052 * eg tolower ('I') != 'i'
4053 * Which would break function lookup when looking up for function 'selectIon'
4054 * when it was registered as 'selection'
4056 * WARNING * WARNING * WARNING
4059 char const * const displayed_name
;
4060 char const * const function
;
4061 } const quick_compute_routines
[] = {
4062 { N_("Sum"), "sum" },
4063 { N_("Min"), "min" },
4064 { N_("Max"), "max" },
4065 { N_("Average"), "average" },
4066 { N_("Count"), "count" },
4070 WorkbookView
*wbv
= wb_control_view (GNM_WBC (wbcg
));
4071 Sheet
*sheet
= wb_view_cur_sheet (wbv
);
4072 GtkWidget
*item
, *menu
;
4075 GnmCellPos
const *pos
;
4078 if (event
->button
.button
!= 3)
4081 menu
= gtk_menu_new ();
4083 for (i
= 0; quick_compute_routines
[i
].displayed_name
; i
++) {
4085 char const *fname
= quick_compute_routines
[i
].function
;
4086 char const *dispname
=
4087 _(quick_compute_routines
[i
].displayed_name
);
4088 GnmExprTop
const *new_auto_expr
;
4092 /* Test the expression... */
4093 parse_pos_init (&pp
, sheet
->workbook
, sheet
, 0, 0);
4094 expr_txt
= g_strconcat (fname
, "(",
4095 parsepos_as_string (&pp
),
4097 new_auto_expr
= gnm_expr_parse_str
4098 (expr_txt
, &pp
, GNM_EXPR_PARSE_DEFAULT
,
4099 sheet_get_conventions (sheet
), NULL
);
4103 gnm_expr_top_unref (new_auto_expr
);
4105 item
= gtk_menu_item_new_with_label (dispname
);
4106 g_object_set_data (G_OBJECT (item
),
4107 "func", gnm_func_lookup (fname
, NULL
));
4108 g_object_set_data (G_OBJECT (item
),
4109 "descr", (gpointer
)dispname
);
4110 g_signal_connect (G_OBJECT (item
),
4112 G_CALLBACK (cb_auto_expr_changed
), wbcg
);
4113 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
4114 gtk_widget_show (item
);
4117 item
= gtk_separator_menu_item_new ();
4118 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
4119 gtk_widget_show (item
);
4121 pos
= &(scg_view (wbcg_cur_scg (wbcg
)))->edit_pos
;
4122 eval_pos_init_pos (&ep
, sheet
, pos
);
4123 cell_item
= g_strdup_printf (_("Content of %s"), cellpos_as_string (pos
));
4124 item
= gtk_menu_item_new_with_label (cell_item
);
4126 g_object_set_data_full (G_OBJECT (item
),
4127 "evalpos", g_memdup (&ep
, sizeof (ep
)),
4128 (GDestroyNotify
)g_free
);
4129 g_signal_connect (G_OBJECT (item
), "activate",
4130 G_CALLBACK (cb_auto_expr_cell_changed
), wbcg
);
4131 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
4132 gtk_widget_show (item
);
4134 item
= gtk_separator_menu_item_new ();
4135 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
4136 gtk_widget_show (item
);
4138 item
= gtk_check_menu_item_new_with_label (_("Use Maximum Precision"));
4139 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item
),
4140 wbv
->auto_expr
.use_max_precision
);
4141 g_signal_connect (G_OBJECT (item
), "activate",
4142 G_CALLBACK (cb_auto_expr_precision_toggled
), wbcg
);
4143 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
4144 gtk_widget_show (item
);
4146 item
= gtk_separator_menu_item_new ();
4147 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
4148 gtk_widget_show (item
);
4150 item
= gtk_menu_item_new_with_label (_("Insert Formula Below"));
4151 g_signal_connect (G_OBJECT (item
), "activate",
4152 G_CALLBACK (cb_auto_expr_insert_formula_below
), wbcg
);
4153 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
4154 gtk_widget_show (item
);
4156 item
= gtk_menu_item_new_with_label (_("Insert Formula to Side"));
4157 g_signal_connect (G_OBJECT (item
), "activate",
4158 G_CALLBACK (cb_auto_expr_insert_formula_to_side
), wbcg
);
4159 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
4160 gtk_widget_show (item
);
4162 gnumeric_popup_menu (GTK_MENU (menu
), event
);
4167 wbc_gtk_create_status_area (WBCGtk
*wbcg
)
4171 g_object_ref (wbcg
->auto_expr_label
);
4172 gtk_label_set_max_width_chars (GTK_LABEL (wbcg
->auto_expr_label
),
4173 strlen (AUTO_EXPR_SAMPLE
));
4174 gtk_widget_set_size_request
4175 (wbcg
->auto_expr_label
,
4176 gnm_widget_measure_string (GTK_WIDGET (wbcg
->toplevel
),
4180 gtk_widget_set_size_request
4182 gnm_widget_measure_string (GTK_WIDGET (wbcg
->toplevel
),
4185 ebox
= GET_GUI_ITEM ("auto_expr_event_box");
4186 gtk_style_context_add_class (gtk_widget_get_style_context (ebox
),
4188 g_signal_connect (G_OBJECT (ebox
),
4189 "button_press_event",
4190 G_CALLBACK (cb_select_auto_expr
), wbcg
);
4192 g_hash_table_insert (wbcg
->visibility_widgets
,
4193 g_strdup ("ViewStatusbar"),
4194 g_object_ref (wbcg
->status_area
));
4196 /* disable statusbar by default going to fullscreen */
4197 wbcg
->hide_for_fullscreen
=
4198 g_slist_prepend (wbcg
->hide_for_fullscreen
,
4199 wbcg_find_action (wbcg
, "ViewStatusbar"));
4200 g_assert (wbcg
->hide_for_fullscreen
->data
);
4203 /****************************************************************************/
4206 cb_file_history_activate (GObject
*action
, WBCGtk
*wbcg
)
4208 gui_file_read (wbcg
, g_object_get_data (action
, "uri"), NULL
, NULL
);
4212 wbc_gtk_reload_recent_file_menu (WBCGtk
*wbcg
)
4214 WBCGtk
*gtk
= (WBCGtk
*)wbcg
;
4215 GSList
*history
, *ptr
;
4217 gboolean any_history
;
4218 GtkAction
*full_history
;
4220 if (gtk
->file_history
.merge_id
!= 0)
4221 gtk_ui_manager_remove_ui (gtk
->ui
, gtk
->file_history
.merge_id
);
4222 gtk
->file_history
.merge_id
= gtk_ui_manager_new_merge_id (gtk
->ui
);
4224 if (gtk
->file_history
.actions
!= NULL
) {
4225 gtk_ui_manager_remove_action_group (gtk
->ui
,
4226 gtk
->file_history
.actions
);
4227 g_object_unref (gtk
->file_history
.actions
);
4229 gtk
->file_history
.actions
= gtk_action_group_new ("FileHistory");
4231 /* create the actions */
4232 history
= gnm_app_history_get_list (3);
4233 any_history
= (history
!= NULL
);
4234 for (i
= 1, ptr
= history
; ptr
!= NULL
; ptr
= ptr
->next
, i
++) {
4235 GtkActionEntry entry
;
4237 char const *uri
= ptr
->data
;
4238 char *name
= g_strdup_printf ("FileHistoryEntry%d", i
);
4239 char *label
= gnm_history_item_label (uri
, i
);
4240 char *filename
= go_filename_from_uri (uri
);
4241 char *filename_utf8
= filename
? g_filename_to_utf8 (filename
, -1, NULL
, NULL
, NULL
) : NULL
;
4242 char *tooltip
= g_strdup_printf (_("Open %s"), filename_utf8
? filename_utf8
: uri
);
4245 entry
.stock_id
= NULL
;
4246 entry
.label
= label
;
4247 entry
.accelerator
= NULL
;
4248 entry
.tooltip
= tooltip
;
4249 entry
.callback
= G_CALLBACK (cb_file_history_activate
);
4250 gtk_action_group_add_actions (gtk
->file_history
.actions
,
4251 &entry
, 1, (WBCGtk
*)wbcg
);
4252 action
= gtk_action_group_get_action (gtk
->file_history
.actions
,
4254 g_object_set_data_full (G_OBJECT (action
), "uri",
4255 g_strdup (uri
), (GDestroyNotify
)g_free
);
4260 g_free (filename_utf8
);
4263 g_slist_free_full (history
, (GDestroyNotify
)g_free
);
4265 gtk_ui_manager_insert_action_group (gtk
->ui
, gtk
->file_history
.actions
, 0);
4269 char *name
= g_strdup_printf ("FileHistoryEntry%d", i
);
4270 gtk_ui_manager_add_ui (gtk
->ui
, gtk
->file_history
.merge_id
,
4271 "/menubar/File/FileHistory", name
, name
,
4272 GTK_UI_MANAGER_AUTO
, TRUE
);
4276 full_history
= wbcg_find_action (wbcg
, "FileHistoryFull");
4277 g_object_set (G_OBJECT (full_history
), "sensitive", any_history
, NULL
);
4281 cb_new_from_template (GObject
*action
, WBCGtk
*wbcg
)
4283 const char *uri
= g_object_get_data (action
, "uri");
4284 gnm_gui_file_template (wbcg
, uri
);
4288 add_template_dir (const char *path
, GHashTable
*h
)
4293 dir
= g_dir_open (path
, 0, NULL
);
4297 while ((name
= g_dir_read_name (dir
))) {
4298 char *fullname
= g_build_filename (path
, name
, NULL
);
4301 * Unconditionally remove, so we can link to /dev/null
4302 * and cause a system file to be hidden.
4304 g_hash_table_remove (h
, name
);
4306 if (g_file_test (fullname
, G_FILE_TEST_IS_REGULAR
)) {
4307 char *uri
= go_filename_to_uri (fullname
);
4308 g_hash_table_insert (h
, g_strdup (name
), uri
);
4316 wbc_gtk_reload_templates (WBCGtk
*gtk
)
4323 if (gtk
->templates
.merge_id
!= 0)
4324 gtk_ui_manager_remove_ui (gtk
->ui
, gtk
->templates
.merge_id
);
4325 gtk
->templates
.merge_id
= gtk_ui_manager_new_merge_id (gtk
->ui
);
4327 if (gtk
->templates
.actions
!= NULL
) {
4328 gtk_ui_manager_remove_action_group (gtk
->ui
,
4329 gtk
->templates
.actions
);
4330 g_object_unref (gtk
->templates
.actions
);
4332 gtk
->templates
.actions
= gtk_action_group_new ("TemplateList");
4334 gtk_ui_manager_insert_action_group (gtk
->ui
, gtk
->templates
.actions
, 0);
4336 h
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, g_free
);
4338 path
= g_build_filename (gnm_sys_data_dir (), "templates", NULL
);
4339 add_template_dir (path
, h
);
4342 /* Possibly override the above with user templates without version. */
4343 path
= g_build_filename (gnm_usr_dir (FALSE
), "templates", NULL
);
4344 add_template_dir (path
, h
);
4347 /* Possibly override the above with user templates with version. */
4348 path
= g_build_filename (gnm_usr_dir (TRUE
), "templates", NULL
);
4349 add_template_dir (path
, h
);
4352 names
= g_slist_sort (go_hash_keys (h
), (GCompareFunc
)g_utf8_collate
);
4354 for (i
= 1, l
= names
; l
; l
= l
->next
) {
4355 const char *uri
= g_hash_table_lookup (h
, l
->data
);
4356 GString
*label
= g_string_new (NULL
);
4357 GtkActionEntry entry
;
4360 char *basename
= go_basename_from_uri (uri
);
4364 if (i
< 10) g_string_append_c (label
, '_');
4365 g_string_append_printf (label
, "%d ", i
);
4367 for (s
= basename
; *s
; s
++) {
4368 if (*s
== '_') g_string_append_c (label
, '_');
4369 g_string_append_c (label
, *s
);
4372 entry
.name
= gname
= g_strdup_printf ("Template%d", i
);
4373 entry
.stock_id
= NULL
;
4374 entry
.label
= label
->str
;
4375 entry
.accelerator
= NULL
;
4376 entry
.tooltip
= NULL
;
4377 entry
.callback
= G_CALLBACK (cb_new_from_template
);
4379 gtk_action_group_add_actions (gtk
->templates
.actions
,
4382 action
= gtk_action_group_get_action (gtk
->templates
.actions
,
4385 g_object_set_data_full (G_OBJECT (action
), "uri",
4386 g_strdup (uri
), (GDestroyNotify
)g_free
);
4389 gpath
= "/menubar/File/Templates";
4390 gtk_ui_manager_add_ui (gtk
->ui
, gtk
->templates
.merge_id
,
4391 gpath
, gname
, gname
,
4392 GTK_UI_MANAGER_AUTO
, FALSE
);
4394 g_string_free (label
, TRUE
);
4400 g_slist_free (names
);
4401 g_hash_table_destroy (h
);
4405 wbc_gtk_load_templates (WBCGtk
*wbcg
)
4407 if (wbcg
->templates
.merge_id
== 0) {
4408 wbc_gtk_reload_templates (wbcg
);
4411 wbcg
->template_loader_handler
= 0;
4416 wbcg_set_toplevel (WBCGtk
*wbcg
, GtkWidget
*w
)
4418 static GtkTargetEntry
const drag_types
[] = {
4419 { (char *) "text/uri-list", 0, TARGET_URI_LIST
},
4420 { (char *) "GNUMERIC_SHEET", 0, TARGET_SHEET
},
4421 { (char *) "GNUMERIC_SAME_PROC", GTK_TARGET_SAME_APP
, 0 }
4424 g_return_if_fail (wbcg
->toplevel
== NULL
);
4427 w
= GTK_WIDGET (wbcg_toplevel (wbcg
));
4428 g_return_if_fail (GTK_IS_WINDOW (w
));
4430 g_object_set (G_OBJECT (w
),
4434 g_signal_connect_data (w
, "delete_event",
4435 G_CALLBACK (wbc_gtk_close
), wbcg
, NULL
,
4436 G_CONNECT_AFTER
| G_CONNECT_SWAPPED
);
4437 g_signal_connect_after (w
, "set_focus",
4438 G_CALLBACK (cb_set_focus
), wbcg
);
4439 g_signal_connect (w
, "scroll-event",
4440 G_CALLBACK (cb_scroll_wheel
), wbcg
);
4441 g_signal_connect (w
, "realize",
4442 G_CALLBACK (cb_realize
), wbcg
);
4443 g_signal_connect (w
, "screen-changed",
4444 G_CALLBACK (cb_screen_changed
), NULL
);
4445 cb_screen_changed (w
);
4447 /* Setup a test of Drag and Drop */
4448 gtk_drag_dest_set (GTK_WIDGET (w
),
4449 GTK_DEST_DEFAULT_ALL
, drag_types
, G_N_ELEMENTS (drag_types
),
4450 GDK_ACTION_COPY
| GDK_ACTION_MOVE
);
4451 gtk_drag_dest_add_image_targets (GTK_WIDGET (w
));
4452 gtk_drag_dest_add_text_targets (GTK_WIDGET (w
));
4453 g_object_connect (G_OBJECT (w
),
4454 "signal::drag-leave", G_CALLBACK (cb_wbcg_drag_leave
), wbcg
,
4455 "signal::drag-data-received", G_CALLBACK (cb_wbcg_drag_data_received
), wbcg
,
4456 "signal::drag-motion", G_CALLBACK (cb_wbcg_drag_motion
), wbcg
,
4458 "signal::drag-data-get", G_CALLBACK (wbcg_drag_data_get
), wbc
,
4463 /***************************************************************************/
4466 wbc_gtk_get_property (GObject
*object
, guint property_id
,
4467 GValue
*value
, GParamSpec
*pspec
)
4469 WBCGtk
*wbcg
= (WBCGtk
*)object
;
4471 switch (property_id
) {
4472 case WBG_GTK_PROP_AUTOSAVE_PROMPT
:
4473 g_value_set_boolean (value
, wbcg
->autosave_prompt
);
4475 case WBG_GTK_PROP_AUTOSAVE_TIME
:
4476 g_value_set_int (value
, wbcg
->autosave_time
);
4479 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
4485 wbc_gtk_set_property (GObject
*object
, guint property_id
,
4486 const GValue
*value
, GParamSpec
*pspec
)
4488 WBCGtk
*wbcg
= (WBCGtk
*)object
;
4490 switch (property_id
) {
4491 case WBG_GTK_PROP_AUTOSAVE_PROMPT
:
4492 wbcg
->autosave_prompt
= g_value_get_boolean (value
);
4494 case WBG_GTK_PROP_AUTOSAVE_TIME
:
4495 wbcg_set_autosave_time (wbcg
, g_value_get_int (value
));
4498 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
4504 wbc_gtk_finalize (GObject
*obj
)
4506 WBCGtk
*wbcg
= WBC_GTK (obj
);
4508 if (wbcg
->idle_update_style_feedback
!= 0)
4509 g_source_remove (wbcg
->idle_update_style_feedback
);
4511 if (wbcg
->template_loader_handler
!= 0) {
4512 g_source_remove (wbcg
->template_loader_handler
);
4513 wbcg
->template_loader_handler
= 0;
4516 if (wbcg
->file_history
.merge_id
!= 0)
4517 gtk_ui_manager_remove_ui (wbcg
->ui
, wbcg
->file_history
.merge_id
);
4518 g_clear_object (&wbcg
->file_history
.actions
);
4520 if (wbcg
->toolbar
.merge_id
!= 0)
4521 gtk_ui_manager_remove_ui (wbcg
->ui
, wbcg
->toolbar
.merge_id
);
4522 g_clear_object (&wbcg
->toolbar
.actions
);
4524 if (wbcg
->windows
.merge_id
!= 0)
4525 gtk_ui_manager_remove_ui (wbcg
->ui
, wbcg
->windows
.merge_id
);
4526 g_clear_object (&wbcg
->windows
.actions
);
4528 if (wbcg
->templates
.merge_id
!= 0)
4529 gtk_ui_manager_remove_ui (wbcg
->ui
, wbcg
->templates
.merge_id
);
4530 g_clear_object (&wbcg
->templates
.actions
);
4533 GSList
*l
, *uis
= go_hash_keys (wbcg
->custom_uis
);
4534 for (l
= uis
; l
; l
= l
->next
) {
4535 GnmAppExtraUI
*extra_ui
= l
->data
;
4536 cb_remove_custom_ui (NULL
, extra_ui
, wbcg
);
4541 g_hash_table_destroy (wbcg
->custom_uis
);
4542 wbcg
->custom_uis
= NULL
;
4544 g_clear_object (&wbcg
->zoom_vaction
);
4545 g_clear_object (&wbcg
->zoom_haction
);
4546 g_clear_object (&wbcg
->borders
);
4547 g_clear_object (&wbcg
->fore_color
);
4548 g_clear_object (&wbcg
->back_color
);
4549 g_clear_object (&wbcg
->font_name_haction
);
4550 g_clear_object (&wbcg
->font_name_vaction
);
4551 g_clear_object (&wbcg
->redo_haction
);
4552 g_clear_object (&wbcg
->redo_vaction
);
4553 g_clear_object (&wbcg
->undo_haction
);
4554 g_clear_object (&wbcg
->undo_vaction
);
4555 g_clear_object (&wbcg
->halignment
);
4556 g_clear_object (&wbcg
->valignment
);
4557 g_clear_object (&wbcg
->actions
);
4558 g_clear_object (&wbcg
->permanent_actions
);
4559 g_clear_object (&wbcg
->font_actions
);
4560 g_clear_object (&wbcg
->data_only_actions
);
4561 g_clear_object (&wbcg
->semi_permanent_actions
);
4562 g_clear_object (&wbcg
->ui
);
4564 /* Disconnect signals that would attempt to change things during
4568 wbcg_autosave_cancel (wbcg
);
4570 if (wbcg
->bnotebook
!= NULL
)
4571 g_signal_handlers_disconnect_by_func (
4572 G_OBJECT (wbcg
->bnotebook
),
4573 G_CALLBACK (cb_notebook_switch_page
), wbcg
);
4574 g_clear_object (&wbcg
->bnotebook
);
4576 g_signal_handlers_disconnect_by_func (
4577 G_OBJECT (wbcg
->toplevel
),
4578 G_CALLBACK (cb_set_focus
), wbcg
);
4580 wbcg_auto_complete_destroy (wbcg
);
4582 gtk_window_set_focus (wbcg_toplevel (wbcg
), NULL
);
4584 if (wbcg
->toplevel
!= NULL
) {
4585 gtk_widget_destroy (wbcg
->toplevel
);
4586 wbcg
->toplevel
= NULL
;
4589 if (wbcg
->font_desc
) {
4590 pango_font_description_free (wbcg
->font_desc
);
4591 wbcg
->font_desc
= NULL
;
4594 g_clear_object (&wbcg
->auto_expr_label
);
4596 g_hash_table_destroy (wbcg
->visibility_widgets
);
4597 g_clear_object (&wbcg
->undo_for_fullscreen
);
4599 g_slist_free (wbcg
->hide_for_fullscreen
);
4600 wbcg
->hide_for_fullscreen
= NULL
;
4603 g_free (wbcg
->preferred_geometry
);
4604 wbcg
->preferred_geometry
= NULL
;
4606 g_clear_object (&wbcg
->gui
);
4608 parent_class
->finalize (obj
);
4611 /***************************************************************************/
4614 GnmExprEntry
*entry
;
4615 GogDataset
*dataset
;
4617 gboolean suppress_update
;
4618 GogDataType data_type
;
4621 gulong dataset_changed_handler
;
4622 gulong entry_update_handler
;
4627 cb_graph_dim_editor_update (GnmExprEntry
*gee
,
4628 G_GNUC_UNUSED gboolean user_requested
,
4629 GraphDimEditor
*editor
)
4631 GOData
*data
= NULL
;
4633 SheetControlGUI
*scg
;
4634 editor
->changed
= FALSE
;
4636 /* Ignore changes while we are insensitive. useful for displaying
4637 * values, without storing them as Data. Also ignore updates if the
4638 * dataset has been cleared via the weakref handler */
4639 if (!gtk_widget_is_sensitive (GTK_WIDGET (gee
)) ||
4640 editor
->dataset
== NULL
)
4643 scg
= gnm_expr_entry_get_scg (gee
);
4644 sheet
= scg_sheet (scg
);
4646 /* If we are setting something */
4647 if (!gnm_expr_entry_is_blank (editor
->entry
)) {
4650 GnmExprTop
const *texpr
;
4651 GnmExprParseFlags flags
=
4652 (editor
->data_type
== GOG_DATA_VECTOR
)?
4653 GNM_EXPR_PARSE_PERMIT_MULTIPLE_EXPRESSIONS
|
4654 GNM_EXPR_PARSE_UNKNOWN_NAMES_ARE_STRINGS
:
4655 GNM_EXPR_PARSE_UNKNOWN_NAMES_ARE_STRINGS
;
4657 parse_error_init (&perr
);
4658 /* Setting start_sel=FALSE to avoid */
4659 /* https://bugzilla.gnome.org/show_bug.cgi?id=658223 */
4660 texpr
= gnm_expr_entry_parse (editor
->entry
,
4661 parse_pos_init_sheet (&pos
, sheet
),
4662 &perr
, FALSE
, flags
);
4664 /* TODO : add some error dialogs split out
4665 * the code in workbook_edit to add parens. */
4666 if (texpr
== NULL
) {
4667 if (editor
->data_type
== GOG_DATA_SCALAR
)
4668 texpr
= gnm_expr_top_new_constant (
4670 gnm_expr_entry_get_text (editor
->entry
)));
4672 g_return_if_fail (perr
.err
!= NULL
);
4674 wb_control_validation_msg (GNM_WBC (scg_wbcg (scg
)),
4675 GNM_VALIDATION_STYLE_INFO
, NULL
, perr
.err
->message
);
4676 parse_error_free (&perr
);
4677 gtk_editable_select_region (GTK_EDITABLE (gnm_expr_entry_get_entry (editor
->entry
)), 0, G_MAXINT
);
4678 editor
->changed
= TRUE
;
4683 switch (editor
->data_type
) {
4684 case GOG_DATA_SCALAR
:
4685 data
= gnm_go_data_scalar_new_expr (sheet
, texpr
);
4687 case GOG_DATA_VECTOR
:
4688 data
= gnm_go_data_vector_new_expr (sheet
, texpr
);
4690 case GOG_DATA_MATRIX
:
4691 data
= gnm_go_data_matrix_new_expr (sheet
, texpr
);
4695 /* The SheetObjectGraph does the magic to link things in */
4696 editor
->suppress_update
= TRUE
;
4697 gog_dataset_set_dim (editor
->dataset
, editor
->dim_i
, data
, NULL
);
4698 editor
->suppress_update
= FALSE
;
4702 cb_update_idle (GraphDimEditor
*editor
)
4704 cb_graph_dim_editor_update (editor
->entry
, FALSE
, editor
);
4710 graph_dim_cancel_idle (GraphDimEditor
*editor
)
4713 g_source_remove (editor
->idle
);
4719 cb_graph_dim_entry_focus_out_event (G_GNUC_UNUSED GtkEntry
*ignored
,
4720 G_GNUC_UNUSED GdkEventFocus
*event
,
4721 GraphDimEditor
*editor
)
4723 if (!editor
->changed
)
4725 graph_dim_cancel_idle (editor
);
4726 editor
->idle
= g_idle_add ((GSourceFunc
) cb_update_idle
, editor
);
4732 cb_graph_dim_entry_changed (GraphDimEditor
*editor
)
4734 editor
->changed
= TRUE
;
4738 set_entry_contents (GnmExprEntry
*entry
, GOData
*val
)
4740 if (GNM_IS_GO_DATA_SCALAR (val
)) {
4741 GnmValue
const *v
= gnm_expr_top_get_constant (gnm_go_data_get_expr (val
));
4742 if (v
&& VALUE_IS_NUMBER (v
)) {
4743 double d
= go_data_get_scalar_value (val
);
4744 GODateConventions
const *date_conv
= go_data_date_conv (val
);
4745 gog_data_editor_set_value_double (GOG_DATA_EDITOR (entry
),
4751 if (GO_IS_DATA_SCALAR (val
) && go_data_has_value (val
)) {
4752 double d
= go_data_get_scalar_value (val
);
4753 GODateConventions
const *date_conv
= go_data_date_conv (val
);
4754 gog_data_editor_set_value_double (GOG_DATA_EDITOR (entry
),
4760 SheetControlGUI
*scg
= gnm_expr_entry_get_scg (entry
);
4761 Sheet
const *sheet
= scg_sheet (scg
);
4762 char *txt
= go_data_serialize (val
, (gpointer
)sheet
->convs
);
4763 gnm_expr_entry_load_from_text (entry
, txt
);
4769 cb_dataset_changed (GogDataset
*dataset
,
4771 GraphDimEditor
*editor
)
4773 GOData
*val
= gog_dataset_get_dim (dataset
, editor
->dim_i
);
4774 if (val
!= NULL
&& !editor
->suppress_update
) {
4775 g_signal_handler_block (editor
->entry
,
4776 editor
->entry_update_handler
);
4777 set_entry_contents (editor
->entry
, val
);
4778 g_signal_handler_unblock (editor
->entry
,
4779 editor
->entry_update_handler
);
4784 cb_dim_editor_weakref_notify (GraphDimEditor
*editor
, GogDataset
*dataset
)
4786 g_return_if_fail (editor
->dataset
== dataset
);
4787 editor
->dataset
= NULL
;
4791 graph_dim_editor_free (GraphDimEditor
*editor
)
4793 graph_dim_cancel_idle (editor
);
4794 if (editor
->dataset
) {
4795 g_signal_handler_disconnect (editor
->dataset
, editor
->dataset_changed_handler
);
4796 g_object_weak_unref (G_OBJECT (editor
->dataset
),
4797 (GWeakNotify
) cb_dim_editor_weakref_notify
, editor
);
4802 static GogDataEditor
*
4803 wbcg_data_allocator_editor (GogDataAllocator
*dalloc
,
4804 GogDataset
*dataset
, int dim_i
, GogDataType data_type
)
4806 WBCGtk
*wbcg
= WBC_GTK (dalloc
);
4807 GraphDimEditor
*editor
;
4810 editor
= g_new (GraphDimEditor
, 1);
4811 editor
->dataset
= dataset
;
4812 editor
->dim_i
= dim_i
;
4813 editor
->suppress_update
= FALSE
;
4814 editor
->data_type
= data_type
;
4815 editor
->entry
= gnm_expr_entry_new (wbcg
, TRUE
);
4817 editor
->changed
= FALSE
;
4818 g_object_weak_ref (G_OBJECT (editor
->dataset
),
4819 (GWeakNotify
) cb_dim_editor_weakref_notify
, editor
);
4821 gnm_expr_entry_set_update_policy (editor
->entry
,
4822 GNM_UPDATE_DISCONTINUOUS
);
4824 val
= gog_dataset_get_dim (dataset
, dim_i
);
4826 set_entry_contents (editor
->entry
, val
);
4829 gnm_expr_entry_set_flags (editor
->entry
, GNM_EE_FORCE_ABS_REF
, GNM_EE_MASK
);
4831 editor
->entry_update_handler
= g_signal_connect (G_OBJECT (editor
->entry
),
4833 G_CALLBACK (cb_graph_dim_editor_update
), editor
);
4834 g_signal_connect (G_OBJECT (gnm_expr_entry_get_entry (editor
->entry
)),
4836 G_CALLBACK (cb_graph_dim_entry_focus_out_event
), editor
);
4837 g_signal_connect_swapped (G_OBJECT (gnm_expr_entry_get_entry (editor
->entry
)),
4839 G_CALLBACK (cb_graph_dim_entry_changed
), editor
);
4840 editor
->dataset_changed_handler
= g_signal_connect (G_OBJECT (editor
->dataset
),
4841 "changed", G_CALLBACK (cb_dataset_changed
), editor
);
4842 g_object_set_data_full (G_OBJECT (editor
->entry
),
4843 "editor", editor
, (GDestroyNotify
) graph_dim_editor_free
);
4845 return GOG_DATA_EDITOR (editor
->entry
);
4849 wbcg_data_allocator_allocate (GogDataAllocator
*dalloc
, GogPlot
*plot
)
4851 SheetControlGUI
*scg
= wbcg_cur_scg (WBC_GTK (dalloc
));
4852 sv_selection_to_plot (scg_view (scg
), plot
);
4857 wbcg_go_plot_data_allocator_init (GogDataAllocatorClass
*iface
)
4859 iface
->editor
= wbcg_data_allocator_editor
;
4860 iface
->allocate
= wbcg_data_allocator_allocate
;
4863 /*************************************************************************/
4865 wbcg_get_password (GOCmdContext
*cc
, char const* filename
)
4867 WBCGtk
*wbcg
= WBC_GTK (cc
);
4869 return dialog_get_password (wbcg_toplevel (wbcg
), filename
);
4872 wbcg_set_sensitive (GOCmdContext
*cc
, gboolean sensitive
)
4874 GtkWindow
*toplevel
= wbcg_toplevel (WBC_GTK (cc
));
4875 if (toplevel
!= NULL
)
4876 gtk_widget_set_sensitive (GTK_WIDGET (toplevel
), sensitive
);
4879 wbcg_error_error (GOCmdContext
*cc
, GError
*err
)
4881 go_gtk_notice_dialog (wbcg_toplevel (WBC_GTK (cc
)),
4883 "%s", err
->message
);
4887 wbcg_error_error_info (GOCmdContext
*cc
, GOErrorInfo
*error
)
4889 gnm_go_error_info_dialog_show (
4890 wbcg_toplevel (WBC_GTK (cc
)), error
);
4894 wbcg_error_error_info_list (GOCmdContext
*cc
, GSList
*errs
)
4896 gnm_go_error_info_list_dialog_show
4897 (wbcg_toplevel (WBC_GTK (cc
)), errs
);
4901 wbcg_progress_set (GOCmdContext
*cc
, double val
)
4903 WBCGtk
*wbcg
= WBC_GTK (cc
);
4904 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (wbcg
->progress_bar
), val
);
4907 wbcg_progress_message_set (GOCmdContext
*cc
, gchar
const *msg
)
4909 WBCGtk
*wbcg
= WBC_GTK (cc
);
4910 gtk_progress_bar_set_text (GTK_PROGRESS_BAR (wbcg
->progress_bar
), msg
);
4913 wbcg_gnm_cmd_context_init (GOCmdContextClass
*iface
)
4915 iface
->get_password
= wbcg_get_password
;
4916 iface
->set_sensitive
= wbcg_set_sensitive
;
4917 iface
->error
.error
= wbcg_error_error
;
4918 iface
->error
.error_info
= wbcg_error_error_info
;
4919 iface
->error
.error_info_list
= wbcg_error_error_info_list
;
4920 iface
->progress_set
= wbcg_progress_set
;
4921 iface
->progress_message_set
= wbcg_progress_message_set
;
4924 /*************************************************************************/
4927 wbc_gtk_class_init (GObjectClass
*gobject_class
)
4929 WorkbookControlClass
*wbc_class
=
4930 GNM_WBC_CLASS (gobject_class
);
4932 g_return_if_fail (wbc_class
!= NULL
);
4934 debug_tab_order
= gnm_debug_flag ("tab-order");
4936 parent_class
= g_type_class_peek_parent (gobject_class
);
4937 gobject_class
->get_property
= wbc_gtk_get_property
;
4938 gobject_class
->set_property
= wbc_gtk_set_property
;
4939 gobject_class
->finalize
= wbc_gtk_finalize
;
4941 wbc_class
->edit_line_set
= wbcg_edit_line_set
;
4942 wbc_class
->selection_descr_set
= wbcg_edit_selection_descr_set
;
4943 wbc_class
->update_action_sensitivity
= wbcg_update_action_sensitivity
;
4945 wbc_class
->sheet
.add
= wbcg_sheet_add
;
4946 wbc_class
->sheet
.remove
= wbcg_sheet_remove
;
4947 wbc_class
->sheet
.focus
= wbcg_sheet_focus
;
4948 wbc_class
->sheet
.remove_all
= wbcg_sheet_remove_all
;
4950 wbc_class
->undo_redo
.labels
= wbcg_undo_redo_labels
;
4951 wbc_class
->undo_redo
.truncate
= wbc_gtk_undo_redo_truncate
;
4952 wbc_class
->undo_redo
.pop
= wbc_gtk_undo_redo_pop
;
4953 wbc_class
->undo_redo
.push
= wbc_gtk_undo_redo_push
;
4955 wbc_class
->menu_state
.update
= wbcg_menu_state_update
;
4957 wbc_class
->claim_selection
= wbcg_claim_selection
;
4958 wbc_class
->paste_from_selection
= wbcg_paste_from_selection
;
4959 wbc_class
->validation_msg
= wbcg_validation_msg
;
4961 wbc_class
->control_new
= wbc_gtk_control_new
;
4962 wbc_class
->init_state
= wbc_gtk_init_state
;
4963 wbc_class
->style_feedback
= wbc_gtk_style_feedback
;
4965 g_object_class_install_property (gobject_class
,
4966 WBG_GTK_PROP_AUTOSAVE_PROMPT
,
4967 g_param_spec_boolean ("autosave-prompt",
4968 P_("Autosave prompt"),
4969 P_("Ask about autosave?"),
4971 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
4972 g_object_class_install_property (gobject_class
,
4973 WBG_GTK_PROP_AUTOSAVE_TIME
,
4974 g_param_spec_int ("autosave-time",
4975 P_("Autosave time in seconds"),
4976 P_("Seconds before autosave"),
4978 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
4980 wbc_gtk_signals
[WBC_GTK_MARKUP_CHANGED
] = g_signal_new ("markup-changed",
4983 G_STRUCT_OFFSET (WBCGtkClass
, markup_changed
),
4985 g_cclosure_marshal_VOID__VOID
,
4989 gtk_window_set_default_icon_name ("gnumeric");
4993 wbc_gtk_init (GObject
*obj
)
4995 WBCGtk
*wbcg
= (WBCGtk
*)obj
;
4996 GError
*error
= NULL
;
4999 GEnumClass
*posclass
;
5000 GtkStyleContext
*ctxt
;
5003 wbcg
->gui
= gnm_gtk_builder_load ("wbcg.ui", NULL
, NULL
);
5004 wbcg
->cancel_button
= GET_GUI_ITEM ("cancel_button");
5005 wbcg
->ok_button
= GET_GUI_ITEM ("ok_button");
5006 wbcg
->func_button
= GET_GUI_ITEM ("func_button");
5007 wbcg
->progress_bar
= GET_GUI_ITEM ("progress_bar");
5008 wbcg
->auto_expr_label
= GET_GUI_ITEM ("auto_expr_label");
5009 wbcg
->status_text
= GET_GUI_ITEM ("status_text");
5010 wbcg
->tabs_paned
= GET_GUI_ITEM ("tabs_paned");
5011 wbcg
->status_area
= GET_GUI_ITEM ("status_area");
5012 wbcg
->notebook_area
= GET_GUI_ITEM ("notebook_area");
5013 wbcg
->snotebook
= GET_GUI_ITEM ("snotebook");
5014 wbcg
->selection_descriptor
= GET_GUI_ITEM ("selection_descriptor");
5015 wbcg
->menu_zone
= GET_GUI_ITEM ("menu_zone");
5016 wbcg
->toolbar_zones
[GTK_POS_TOP
] = GET_GUI_ITEM ("toolbar_zone_top");
5017 wbcg
->toolbar_zones
[GTK_POS_BOTTOM
] = NULL
;
5018 wbcg
->toolbar_zones
[GTK_POS_LEFT
] = GET_GUI_ITEM ("toolbar_zone_left");
5019 wbcg
->toolbar_zones
[GTK_POS_RIGHT
] = GET_GUI_ITEM ("toolbar_zone_right");
5020 wbcg
->updating_ui
= FALSE
;
5022 posclass
= G_ENUM_CLASS (g_type_class_peek (gtk_position_type_get_type ()));
5023 for (i
= 0; i
< posclass
->n_values
; i
++) {
5024 GEnumValue
const *ev
= posclass
->values
+ i
;
5025 GtkWidget
*zone
= wbcg
->toolbar_zones
[ev
->value
];
5026 GtkStyleContext
*ctxt
;
5029 ctxt
= gtk_widget_get_style_context (zone
);
5030 gtk_style_context_add_class (ctxt
, "toolbarzone");
5031 gtk_style_context_add_class (ctxt
, ev
->value_nick
);
5034 wbcg
->visibility_widgets
= g_hash_table_new_full (g_str_hash
,
5035 g_str_equal
, (GDestroyNotify
)g_free
, (GDestroyNotify
)g_object_unref
);
5036 wbcg
->undo_for_fullscreen
= NULL
;
5037 wbcg
->hide_for_fullscreen
= NULL
;
5039 wbcg
->autosave_prompt
= FALSE
;
5040 wbcg
->autosave_time
= 0;
5041 wbcg
->autosave_timer
= 0;
5043 /* We are not in edit mode */
5044 wbcg
->editing
= FALSE
;
5045 wbcg
->editing_sheet
= NULL
;
5046 wbcg
->editing_cell
= NULL
;
5048 wbcg
->new_object
= NULL
;
5050 wbcg
->idle_update_style_feedback
= 0;
5052 wbcg_set_toplevel (wbcg
, GET_GUI_ITEM ("toplevel"));
5053 ctxt
= gtk_widget_get_style_context (GTK_WIDGET (wbcg_toplevel (wbcg
)));
5054 gtk_style_context_add_class (ctxt
, "gnumeric");
5056 g_signal_connect (wbcg_toplevel (wbcg
), "window_state_event",
5057 G_CALLBACK (cb_wbcg_window_state_event
),
5060 wbc_gtk_init_actions (wbcg
);
5061 wbcg
->ui
= gtk_ui_manager_new ();
5062 g_object_connect (wbcg
->ui
,
5063 "signal::add_widget", G_CALLBACK (cb_add_menus_toolbars
), wbcg
,
5064 "signal::connect_proxy", G_CALLBACK (cb_connect_proxy
), wbcg
,
5065 "signal::disconnect_proxy", G_CALLBACK (cb_disconnect_proxy
), wbcg
,
5066 "signal::post_activate", G_CALLBACK (cb_post_activate
), wbcg
,
5068 gtk_ui_manager_insert_action_group (wbcg
->ui
, wbcg
->permanent_actions
, 0);
5069 gtk_ui_manager_insert_action_group (wbcg
->ui
, wbcg
->actions
, 0);
5070 gtk_ui_manager_insert_action_group (wbcg
->ui
, wbcg
->font_actions
, 0);
5071 gtk_ui_manager_insert_action_group (wbcg
->ui
, wbcg
->data_only_actions
, 0);
5072 gtk_ui_manager_insert_action_group (wbcg
->ui
, wbcg
->semi_permanent_actions
, 0);
5073 gtk_window_add_accel_group (wbcg_toplevel (wbcg
),
5074 gtk_ui_manager_get_accel_group (wbcg
->ui
));
5077 gtk_action_group_add_actions (wbcg
->actions
, extra_actions
,
5078 extra_actions_nb
, wbcg
);
5081 if (strncmp (uifilename
, "res:", 4) == 0)
5082 uifile
= g_strdup (uifilename
);
5084 uifile
= g_build_filename (gnm_sys_data_dir (),
5088 uifile
= g_strdup ("res:/org/gnumeric/gnumeric/ui/GNOME_Gnumeric-gtk.xml");
5090 if (strncmp (uifile
, "res:", 4) == 0)
5091 merge_id
= gtk_ui_manager_add_ui_from_resource
5092 (wbcg
->ui
, uifile
+ 4, &error
);
5094 merge_id
= gtk_ui_manager_add_ui_from_file
5095 (wbcg
->ui
, uifile
, &error
);
5097 g_message ("building menus failed: %s", error
->message
);
5098 g_error_free (error
);
5103 wbcg
->custom_uis
= g_hash_table_new_full (g_direct_hash
, g_direct_equal
,
5106 wbcg
->file_history
.actions
= NULL
;
5107 wbcg
->file_history
.merge_id
= 0;
5109 wbcg
->toolbar
.merge_id
= gtk_ui_manager_new_merge_id (wbcg
->ui
);
5110 wbcg
->toolbar
.actions
= gtk_action_group_new ("Toolbars");
5111 gtk_ui_manager_insert_action_group (wbcg
->ui
, wbcg
->toolbar
.actions
, 0);
5113 wbcg
->windows
.actions
= NULL
;
5114 wbcg
->windows
.merge_id
= 0;
5116 wbcg
->templates
.actions
= NULL
;
5117 wbcg
->templates
.merge_id
= 0;
5119 gnm_app_foreach_extra_ui ((GFunc
) cb_init_extra_ui
, wbcg
);
5120 g_object_connect ((GObject
*) gnm_app_get_app (),
5121 "swapped-object-signal::window-list-changed",
5122 G_CALLBACK (cb_regenerate_window_menu
), wbcg
,
5123 "object-signal::custom-ui-added",
5124 G_CALLBACK (cb_add_custom_ui
), wbcg
,
5125 "object-signal::custom-ui-removed",
5126 G_CALLBACK (cb_remove_custom_ui
), wbcg
,
5129 gtk_ui_manager_ensure_update (wbcg
->ui
);
5131 /* updates the undo/redo menu labels before check_underlines
5132 * to avoid problems like #324692. */
5133 wb_control_undo_redo_labels (GNM_WBC (wbcg
), NULL
, NULL
);
5134 if (GNM_VERSION_MAJOR
% 2 != 0 ||
5135 gnm_debug_flag ("underlines")) {
5136 gtk_container_foreach (GTK_CONTAINER (wbcg
->menu_zone
),
5137 (GtkCallback
)check_underlines
,
5141 wbcg_set_autosave_time (wbcg
, gnm_conf_get_core_workbook_autosave_time ());
5144 GSF_CLASS_FULL (WBCGtk
, wbc_gtk
, NULL
, NULL
, wbc_gtk_class_init
, NULL
,
5145 wbc_gtk_init
, GNM_WBC_TYPE
, 0,
5146 GSF_INTERFACE (wbcg_go_plot_data_allocator_init
, GOG_TYPE_DATA_ALLOCATOR
);
5147 GSF_INTERFACE (wbcg_gnm_cmd_context_init
, GO_TYPE_CMD_CONTEXT
))
5149 /******************************************************************************/
5152 wbc_gtk_markup_changer (WBCGtk
*wbcg
)
5154 g_signal_emit (G_OBJECT (wbcg
), wbc_gtk_signals
[WBC_GTK_MARKUP_CHANGED
], 0);
5157 /******************************************************************************/
5160 * @optional_view: (allow-none): #WorkbookView
5161 * @optional_wb: (allow-none) (transfer full): #Workbook
5162 * @optional_screen: (allow-none): #GdkScreen.
5163 * @optional_geometry: (allow-none): string.
5165 * Returns: (transfer none): (allow-none): the new #WBCGtk or %NULL.
5168 wbc_gtk_new (WorkbookView
*optional_view
,
5169 Workbook
*optional_wb
,
5170 GdkScreen
*optional_screen
,
5171 gchar
*optional_geometry
)
5175 WBCGtk
*wbcg
= g_object_new (wbc_gtk_get_type (), NULL
);
5176 WorkbookControl
*wbc
= (WorkbookControl
*)wbcg
;
5178 wbcg
->preferred_geometry
= g_strdup (optional_geometry
);
5180 wbc_gtk_create_edit_area (wbcg
);
5181 wbc_gtk_create_status_area (wbcg
);
5182 wbc_gtk_reload_recent_file_menu (wbcg
);
5184 g_signal_connect_object (gnm_app_get_app (),
5185 "notify::file-history-list",
5186 G_CALLBACK (wbc_gtk_reload_recent_file_menu
), wbcg
, G_CONNECT_SWAPPED
);
5188 wb_control_set_view (wbc
, optional_view
, optional_wb
);
5189 wbv
= wb_control_view (wbc
);
5190 sheet
= wbv
->current_sheet
;
5191 if (sheet
!= NULL
) {
5192 wb_control_menu_state_update (wbc
, MS_ALL
);
5193 wb_control_update_action_sensitivity (wbc
);
5194 wb_control_style_feedback (wbc
, NULL
);
5195 cb_zoom_change (sheet
, NULL
, wbcg
);
5198 wbc_gtk_create_notebook_area (wbcg
);
5200 wbcg_view_changed (wbcg
, NULL
, NULL
);
5202 if (optional_screen
)
5203 gtk_window_set_screen (wbcg_toplevel (wbcg
), optional_screen
);
5205 /* Postpone showing the GUI, so that we may resize it freely. */
5206 g_idle_add ((GSourceFunc
)show_gui
, wbcg
);
5208 /* Load this later when thing have calmed down. If this does not
5209 trigger by the time the file menu is activated, then the UI is
5210 updated right then -- and that looks sub-optimal because the
5211 "Templates" menu is empty (and thus not shown) until the
5213 wbcg
->template_loader_handler
=
5214 g_timeout_add (1000, (GSourceFunc
)wbc_gtk_load_templates
, wbcg
);
5216 wb_control_init_state (wbc
);
5224 * Returns: (transfer none): the toplevel #GtkWindow.
5227 wbcg_toplevel (WBCGtk
*wbcg
)
5229 g_return_val_if_fail (GNM_IS_WBC_GTK (wbcg
), NULL
);
5230 return GTK_WINDOW (wbcg
->toplevel
);
5234 wbcg_set_transient (WBCGtk
*wbcg
, GtkWindow
*window
)
5236 go_gtk_window_set_transient (wbcg_toplevel (wbcg
), window
);
5240 wbcg_get_n_scg (WBCGtk
const *wbcg
)
5242 return (GTK_IS_NOTEBOOK (wbcg
->snotebook
))?
5243 gtk_notebook_get_n_pages (wbcg
->snotebook
): -1;
5251 * Returns: (transfer none): the scg associated with the @i-th tab in
5253 * NOTE : @i != scg->sv->sheet->index_in_wb
5256 wbcg_get_nth_scg (WBCGtk
*wbcg
, int i
)
5258 SheetControlGUI
*scg
;
5261 g_return_val_if_fail (GNM_IS_WBC_GTK (wbcg
), NULL
);
5263 if (NULL
!= wbcg
->snotebook
&&
5264 NULL
!= (w
= gtk_notebook_get_nth_page (wbcg
->snotebook
, i
)) &&
5265 NULL
!= (scg
= get_scg (w
)) &&
5266 NULL
!= scg
->grid
&&
5267 NULL
!= scg_sheet (scg
) &&
5268 NULL
!= scg_view (scg
))
5274 #warning merge these and clarfy whether we want the visible scg, or the logical (view) scg
5276 * wbcg_focus_cur_scg:
5277 * @wbcg: The workbook control to operate on.
5279 * A utility routine to safely ensure that the keyboard focus
5280 * is attached to the item-grid. This is required when a user
5281 * edits a combo-box or and entry-line which grab focus.
5283 * It is called for zoom, font name/size, and accept/cancel for the editline.
5284 * Returns: (transfer none): the sheet.
5287 wbcg_focus_cur_scg (WBCGtk
*wbcg
)
5289 SheetControlGUI
*scg
;
5291 g_return_val_if_fail (GNM_IS_WBC_GTK (wbcg
), NULL
);
5293 if (wbcg
->snotebook
== NULL
)
5296 scg
= wbcg_get_nth_scg (wbcg
,
5297 gtk_notebook_get_current_page (wbcg
->snotebook
));
5299 g_return_val_if_fail (scg
!= NULL
, NULL
);
5301 scg_take_focus (scg
);
5302 return scg_sheet (scg
);
5309 * Returns: (transfer none): the current #ShetControlGUI.
5312 wbcg_cur_scg (WBCGtk
*wbcg
)
5314 return wbcg_get_scg (wbcg
, wbcg_cur_sheet (wbcg
));
5321 * Returns: (transfer none): the current #Sheet.
5324 wbcg_cur_sheet (WBCGtk
*wbcg
)
5326 return wb_control_cur_sheet (GNM_WBC (wbcg
));
5329 PangoFontDescription
*
5330 wbcg_get_font_desc (WBCGtk
*wbcg
)
5332 g_return_val_if_fail (GNM_IS_WBC_GTK (wbcg
), NULL
);
5334 if (!wbcg
->font_desc
) {
5335 GtkSettings
*settings
= wbcg_get_gtk_settings (wbcg
);
5336 wbcg
->font_desc
= settings_get_font_desc (settings
);
5337 g_signal_connect_object (settings
, "notify::gtk-font-name",
5338 G_CALLBACK (cb_desktop_font_changed
),
5341 return wbcg
->font_desc
;
5345 * wbcg_find_for_workbook:
5347 * @candidate: a candidate #WBCGtk
5348 * @pref_screen: the preferred screen.
5349 * @pref_display: the preferred display.
5351 * Returns: (transfer none): the found #WBCGtk or %NULL.
5354 wbcg_find_for_workbook (Workbook
*wb
,
5356 GdkScreen
*pref_screen
,
5357 GdkDisplay
*pref_display
)
5359 gboolean has_screen
, has_display
;
5361 g_return_val_if_fail (GNM_IS_WORKBOOK (wb
), NULL
);
5362 g_return_val_if_fail (candidate
== NULL
|| GNM_IS_WBC_GTK (candidate
), NULL
);
5364 if (candidate
&& wb_control_get_workbook (GNM_WBC (candidate
)) == wb
)
5367 if (!pref_screen
&& candidate
)
5368 pref_screen
= wbcg_get_screen (candidate
);
5370 if (!pref_display
&& pref_screen
)
5371 pref_display
= gdk_screen_get_display (pref_screen
);
5375 has_display
= FALSE
;
5376 WORKBOOK_FOREACH_CONTROL(wb
, wbv
, wbc
, {
5377 if (GNM_IS_WBC_GTK (wbc
)) {
5378 WBCGtk
*wbcg
= WBC_GTK (wbc
);
5379 GdkScreen
*screen
= wbcg_get_screen (wbcg
);
5380 GdkDisplay
*display
= gdk_screen_get_display (screen
);
5382 if (pref_screen
== screen
&& !has_screen
) {
5383 has_screen
= has_display
= TRUE
;
5385 } else if (pref_display
== display
&& !has_display
) {
5388 } else if (!candidate
)
5397 wbcg_focus_current_cell_indicator (WBCGtk
const *wbcg
)
5399 gtk_widget_grab_focus (GTK_WIDGET (wbcg
->selection_descriptor
));
5400 gtk_editable_select_region (GTK_EDITABLE (wbcg
->selection_descriptor
), 0, -1);