1 /*
2 * dialog-sheet-order.c: Dialog to change the order of sheets in the Gnumeric
3 * spreadsheet
4 *
5 * Author:
6 * Jody Goldberg <jody@gnome.org>
7 * Andreas J. Guelzow <aguelzow@taliesin.ca>
8 *
9 * (C) Copyright 2000, 2001, 2002 Jody Goldberg <jody@gnome.org>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, see <https://www.gnu.org/licenses/>.
23 */
24
25 #include <gnumeric-config.h>
26 #include <glib/gi18n-lib.h>
27 #include <gnumeric.h>
28 #include <dialogs/dialogs.h>
29 #include <dialogs/help.h>
30
31 #include <gui-util.h>
32 #include <wbc-gtk.h>
33 #include <workbook-view.h>
34 #include <workbook.h>
35
36 /* We shouldn't need workbook-priv.h but we need to know whether undo commands are pending */
37 #include <workbook-priv.h>
38
39 #include <sheet.h>
40 #include <style-color.h>
41 #include <commands.h>
42 #include <application.h>
43 #include <widgets/gnm-cell-renderer-text.h>
44 #include <widgets/gnm-cell-renderer-toggle.h>
45 #include <goffice/goffice.h>
46
47 #include <string.h>
48
49
50 #define SHEET_ORDER_KEY "sheet-order-dialog"
51
52 typedef struct {
53 WBCGtk *wbcg;
54
55 GtkBuilder *gui;
56 GtkWidget *dialog;
57 GtkTreeView *sheet_list;
58 GtkListStore *model;
59 GtkWidget *up_btn;
60 GtkWidget *down_btn;
61 GtkWidget *add_btn;
62 GtkWidget *append_btn;
63 GtkWidget *duplicate_btn;
64 GtkWidget *delete_btn;
65 GtkWidget *apply_names_btn;
66 GtkWidget *sort_asc_btn;
67 GtkWidget *sort_desc_btn;
68 GtkWidget *undo_btn;
69 GtkWidget *cancel_btn;
70 GtkWidget *advanced_check;
71 GtkWidget *ccombo_back;
72 GtkWidget *ccombo_fore;
73 GtkWidget *warning;
74
75 GdkPixbuf *image_padlock;
76 GdkPixbuf *image_padlock_no;
77
78 GdkPixbuf *image_ltr;
79 GdkPixbuf *image_rtl;
80
81 GdkPixbuf *image_visible;
82
83 gboolean initial_colors_set;
84
85 GtkTreeViewColumn *dir_column;
86 GtkTreeViewColumn *row_max_column;
87 GtkTreeViewColumn *col_max_column;
88
89 gulong sheet_order_changed_listener;
90 gulong sheet_added_listener;
91 gulong sheet_deleted_listener;
92
93 gulong model_selection_changed_listener;
94 gulong model_row_insertion_listener;
95 } SheetManager;
96
97 enum {
98 SHEET_LOCKED,
99 SHEET_LOCK_IMAGE,
100 SHEET_VISIBLE,
101 SHEET_VISIBLE_IMAGE,
102 SHEET_ROW_MAX,
103 SHEET_COL_MAX,
104 SHEET_NAME,
105 SHEET_NEW_NAME,
106 SHEET_POINTER,
107 BACKGROUND_COLOUR,
108 FOREGROUND_COLOUR,
109 SHEET_DIRECTION,
110 SHEET_DIRECTION_IMAGE,
111 NUM_COLUMNS
112 };
113
114 static char *verify_validity (SheetManager *state, gboolean *pchanged);
115 static void dialog_sheet_order_update_sheet_order (SheetManager *state);
116
117
118 static void
update_undo(SheetManager * state,WorkbookControl * wbc)119 update_undo (SheetManager *state, WorkbookControl *wbc)
120 {
121
122 gtk_widget_set_sensitive (state->undo_btn, TRUE);
123 }
124
125 static void
workbook_signals_block(SheetManager * state)126 workbook_signals_block (SheetManager *state)
127 {
128 WorkbookControl *wbc = GNM_WBC (state->wbcg);
129 Workbook *wb = wb_control_get_workbook (wbc);
130
131 g_signal_handler_block (G_OBJECT (wb),
132 state->sheet_order_changed_listener);
133 g_signal_handler_block (G_OBJECT (wb),
134 state->sheet_added_listener);
135 g_signal_handler_block (G_OBJECT (wb),
136 state->sheet_deleted_listener);
137 }
138
139 static void
workbook_signals_unblock(SheetManager * state)140 workbook_signals_unblock (SheetManager *state)
141 {
142 WorkbookControl *wbc = GNM_WBC (state->wbcg);
143 Workbook *wb = wb_control_get_workbook (wbc);
144
145 g_signal_handler_unblock (G_OBJECT (wb),
146 state->sheet_order_changed_listener);
147 g_signal_handler_unblock (G_OBJECT (wb),
148 state->sheet_added_listener);
149 g_signal_handler_unblock (G_OBJECT (wb),
150 state->sheet_deleted_listener);
151 }
152
153 static void
cb_name_edited(GtkCellRendererText * cell,gchar * path_string,gchar * new_text,SheetManager * state)154 cb_name_edited (GtkCellRendererText *cell,
155 gchar *path_string,
156 gchar *new_text,
157 SheetManager *state)
158 {
159 GtkTreeIter iter;
160 GtkTreePath *path;
161 gboolean changed = FALSE;
162 char *error;
163
164 if (cell != NULL) {
165 path = gtk_tree_path_new_from_string (path_string);
166 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (state->model),
167 &iter, path))
168 gtk_list_store_set (state->model, &iter,
169 SHEET_NEW_NAME, new_text, -1);
170 else
171 g_warning ("Did not get a valid iterator");
172 gtk_tree_path_free (path);
173 }
174
175 error = verify_validity (state, &changed);
176
177 if (error != NULL) {
178 gtk_widget_set_sensitive (state->apply_names_btn, FALSE);
179 gtk_label_set_text (GTK_LABEL (state->warning), error);
180 } else {
181 gtk_widget_set_sensitive (state->apply_names_btn, changed);
182 gtk_label_set_markup (GTK_LABEL (state->warning),
183 changed ? _("<b>Note:</b> A sheet name change is pending.") : "");
184 }
185 }
186
187
188 typedef struct {
189 char *key;
190 int i;
191 } gtmff_sort_t;
192
193 static gint
gtmff_compare_func(gconstpointer a,gconstpointer b)194 gtmff_compare_func (gconstpointer a, gconstpointer b)
195 {
196 gtmff_sort_t const *pa = a, *pb = b;
197
198 return strcmp (pa->key, pb->key);
199 }
200
201
202 static gboolean
gtmff_asc(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)203 gtmff_asc (GtkTreeModel *model, GtkTreePath *path,
204 GtkTreeIter *iter, gpointer data)
205 {
206 GSList **l = data;
207 Sheet *this_sheet;
208 char *name;
209 gtmff_sort_t *ptr;
210
211
212 ptr = g_new (gtmff_sort_t, 1);
213 gtk_tree_model_get (model, iter,
214 SHEET_POINTER, &this_sheet,
215 SHEET_NAME, &name,
216 -1);
217 ptr->i = this_sheet->index_in_wb;
218 ptr->key = g_utf8_collate_key_for_filename (name, -1);
219
220 *l = g_slist_insert_sorted (*l, ptr, (GCompareFunc) gtmff_compare_func);
221
222 return FALSE;
223 }
224
225 static void
sort_asc_desc(SheetManager * state,gboolean asc)226 sort_asc_desc (SheetManager *state, gboolean asc)
227 {
228 WorkbookSheetState *old_state;
229 WorkbookControl *wbc = GNM_WBC (state->wbcg);
230 Workbook *wb = wb_control_get_workbook (wbc);
231 GSList *l = NULL, *l_tmp;
232 gint n = 0;
233
234 gtk_tree_model_foreach (GTK_TREE_MODEL (state->model), gtmff_asc, &l);
235
236 if (!asc)
237 l = g_slist_reverse (l);
238
239 workbook_signals_block (state);
240
241 old_state = workbook_sheet_state_new (wb);
242
243 for (l_tmp = l; l_tmp != NULL; l_tmp = l_tmp->next) {
244 gtmff_sort_t *ptr = l_tmp->data;
245 GtkTreeIter iter;
246 Sheet *sheet;
247
248 gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (state->model),
249 &iter, NULL, ptr->i);
250 g_free (ptr->key);
251 g_free (ptr);
252 l_tmp->data = NULL;
253
254 gtk_tree_model_get (GTK_TREE_MODEL (state->model), &iter,
255 SHEET_POINTER, &sheet,
256 -1);
257 workbook_sheet_move (sheet, n - sheet->index_in_wb);
258 n++;
259 }
260 g_slist_free (l);
261
262 /* Now we change the list store */
263 dialog_sheet_order_update_sheet_order (state);
264
265 cmd_reorganize_sheets (wbc, old_state, NULL);
266 update_undo (state, wbc);
267
268 workbook_signals_unblock (state);
269 }
270
271 static void
cb_asc(G_GNUC_UNUSED GtkWidget * w,SheetManager * state)272 cb_asc (G_GNUC_UNUSED GtkWidget *w, SheetManager *state)
273 {
274 sort_asc_desc (state, TRUE);
275 }
276
277 static void
cb_desc(G_GNUC_UNUSED GtkWidget * w,SheetManager * state)278 cb_desc (G_GNUC_UNUSED GtkWidget *w, SheetManager *state)
279 {
280 sort_asc_desc (state, FALSE);
281 }
282
283 static gboolean
color_equal(const GdkRGBA * color_a,const GnmColor * color_gb)284 color_equal (const GdkRGBA *color_a, const GnmColor *color_gb)
285 {
286 if (color_gb == NULL)
287 return color_a == NULL;
288 /* FIXME: What about ->is_auto? */
289 return color_a && GO_COLOR_FROM_GDK_RGBA (*color_a) == color_gb->go_color;
290 }
291
292 static void
cb_color_changed_fore(G_GNUC_UNUSED GOComboColor * go_combo_color,GOColor color,G_GNUC_UNUSED gboolean custom,G_GNUC_UNUSED gboolean by_user,G_GNUC_UNUSED gboolean is_default,SheetManager * state)293 cb_color_changed_fore (G_GNUC_UNUSED GOComboColor *go_combo_color,
294 GOColor color, G_GNUC_UNUSED gboolean custom,
295 G_GNUC_UNUSED gboolean by_user,
296 G_GNUC_UNUSED gboolean is_default,
297 SheetManager *state)
298 {
299 GList *selected_rows, *l;
300 GtkTreeSelection *selection = gtk_tree_view_get_selection (state->sheet_list);
301 WorkbookSheetState *old_state;
302 WorkbookControl *wbc = GNM_WBC (state->wbcg);
303 Workbook *wb = wb_control_get_workbook (wbc);
304 GdkRGBA gdk_color;
305 GdkRGBA *p_gdk_color;
306 GnmColor *gnm_color;
307
308 g_return_if_fail (selection != NULL);
309
310 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
311
312 p_gdk_color = (color == 0) ? NULL : go_color_to_gdk_rgba (color, &gdk_color);
313 gnm_color = (color == 0) ? NULL : gnm_color_new_gdk (&gdk_color);
314
315 old_state = workbook_sheet_state_new (wb);
316
317 for (l = selected_rows; l != NULL; l = l->next) {
318 Sheet *this_sheet;
319 GtkTreeIter sel_iter;
320 GtkTreePath *path = l->data;
321
322 gtk_tree_model_get_iter (GTK_TREE_MODEL (state->model), &sel_iter, path);
323 gtk_tree_model_get (GTK_TREE_MODEL (state->model), &sel_iter,
324 SHEET_POINTER, &this_sheet,
325 -1);
326 if (color_equal (p_gdk_color, this_sheet->tab_text_color))
327 continue;
328
329 gtk_list_store_set (state->model, &sel_iter,
330 FOREGROUND_COLOUR, p_gdk_color,
331 -1);
332 g_object_set (this_sheet,
333 "tab-foreground", gnm_color,
334 NULL);
335 }
336
337 style_color_unref (gnm_color);
338 cmd_reorganize_sheets (wbc, old_state, NULL);
339 update_undo (state, wbc);
340
341 g_list_free_full (selected_rows, (GDestroyNotify) gtk_tree_path_free);
342 }
343
344 static void
cb_color_changed_back(G_GNUC_UNUSED GOComboColor * go_combo_color,GOColor color,G_GNUC_UNUSED gboolean custom,G_GNUC_UNUSED gboolean by_user,G_GNUC_UNUSED gboolean is_default,SheetManager * state)345 cb_color_changed_back (G_GNUC_UNUSED GOComboColor *go_combo_color,
346 GOColor color, G_GNUC_UNUSED gboolean custom,
347 G_GNUC_UNUSED gboolean by_user,
348 G_GNUC_UNUSED gboolean is_default,
349 SheetManager *state)
350 {
351 GList *selected_rows, *l;
352 GtkTreeSelection *selection = gtk_tree_view_get_selection (state->sheet_list);
353 WorkbookSheetState *old_state;
354 WorkbookControl *wbc = GNM_WBC (state->wbcg);
355 Workbook *wb = wb_control_get_workbook (wbc);
356 GdkRGBA gdk_color;
357 GdkRGBA *p_gdk_color;
358 GnmColor *gnm_color;
359
360 g_return_if_fail (selection != NULL);
361
362 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
363
364 p_gdk_color = (color == 0) ? NULL : go_color_to_gdk_rgba (color, &gdk_color);
365 gnm_color = (color == 0) ? NULL : gnm_color_new_gdk (&gdk_color);
366
367 old_state = workbook_sheet_state_new (wb);
368
369 for (l = selected_rows; l != NULL; l = l->next) {
370 Sheet *this_sheet;
371 GtkTreeIter sel_iter;
372 GtkTreePath *path = l->data;
373
374 gtk_tree_model_get_iter (GTK_TREE_MODEL (state->model), &sel_iter, path);
375 gtk_tree_model_get (GTK_TREE_MODEL (state->model), &sel_iter,
376 SHEET_POINTER, &this_sheet,
377 -1);
378 if (color_equal (p_gdk_color, this_sheet->tab_color))
379 continue;
380
381 gtk_list_store_set (state->model, &sel_iter,
382 BACKGROUND_COLOUR, p_gdk_color,
383 -1);
384 g_object_set (this_sheet,
385 "tab-background", gnm_color,
386 NULL);
387 }
388
389 style_color_unref (gnm_color);
390 cmd_reorganize_sheets (wbc, old_state, NULL);
391 update_undo (state, wbc);
392
393 g_list_free_full (selected_rows, (GDestroyNotify) gtk_tree_path_free);
394 }
395
396 static gboolean
cb_sheet_order_cnt_visible(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)397 cb_sheet_order_cnt_visible (GtkTreeModel *model,
398 GtkTreePath *path,
399 GtkTreeIter *iter,
400 gpointer data)
401 {
402 gint *i = data;
403 gboolean is_visible;
404
405 gtk_tree_model_get (model, iter,
406 SHEET_VISIBLE, &is_visible,
407 -1);
408 if (is_visible)
409 (*i)++;
410
411 return FALSE;
412 }
413
414 static gint
sheet_order_cnt_visible(SheetManager * state)415 sheet_order_cnt_visible (SheetManager *state)
416 {
417 gint data = 0;
418 gtk_tree_model_foreach (GTK_TREE_MODEL (state->model),
419 cb_sheet_order_cnt_visible,
420 &data);
421 return data;
422 }
423
424 /*
425 * Refreshes the buttons on a row (un)selection and selects the chosen sheet
426 * for this view.
427 */
428 static void
cb_selection_changed(G_GNUC_UNUSED GtkTreeSelection * ignored,SheetManager * state)429 cb_selection_changed (G_GNUC_UNUSED GtkTreeSelection *ignored,
430 SheetManager *state)
431 {
432 GtkTreeIter iter;
433 Sheet *sheet;
434 gboolean has_iter;
435 GdkRGBA *fore, *back;
436 GtkTreeSelection *selection = gtk_tree_view_get_selection (state->sheet_list);
437 GList *selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
438 gboolean multiple = gtk_tree_model_iter_n_children(GTK_TREE_MODEL (state->model), NULL) > 1;
439 int cnt_sel = g_list_length (selected_rows);
440 gboolean single_sel = (cnt_sel < 2);
441
442 gtk_widget_set_sensitive (state->sort_asc_btn, multiple);
443 gtk_widget_set_sensitive (state->sort_desc_btn, multiple);
444
445 if (selected_rows == NULL) {
446 gtk_widget_set_sensitive (state->up_btn, FALSE);
447 gtk_widget_set_sensitive (state->down_btn, FALSE);
448 gtk_widget_set_sensitive (state->delete_btn, FALSE);
449 gtk_widget_set_sensitive (state->ccombo_back, FALSE);
450 gtk_widget_set_sensitive (state->ccombo_fore, FALSE);
451 gtk_widget_set_sensitive (state->add_btn, FALSE);
452 gtk_widget_set_sensitive (state->duplicate_btn, FALSE);
453 return;
454 }
455
456 gtk_tree_model_get_iter (GTK_TREE_MODEL (state->model),
457 &iter, (GtkTreePath *) selected_rows->data);
458
459 gtk_tree_model_get (GTK_TREE_MODEL (state->model), &iter,
460 SHEET_POINTER, &sheet,
461 BACKGROUND_COLOUR, &back,
462 FOREGROUND_COLOUR, &fore,
463 -1);
464 if (!state->initial_colors_set) {
465 go_combo_color_set_color_gdk (GO_COMBO_COLOR (state->ccombo_back), back);
466 go_combo_color_set_color_gdk (GO_COMBO_COLOR (state->ccombo_fore), fore);
467 state->initial_colors_set = TRUE;
468 }
469 if (back != NULL)
470 gdk_rgba_free (back);
471 if (fore != NULL)
472 gdk_rgba_free (fore);
473
474 gtk_widget_set_sensitive (state->ccombo_back, TRUE);
475 gtk_widget_set_sensitive (state->ccombo_fore, TRUE);
476 gtk_widget_set_sensitive (state->delete_btn, sheet_order_cnt_visible (state) > cnt_sel);
477 gtk_widget_set_sensitive (state->add_btn, single_sel);
478 gtk_widget_set_sensitive (state->duplicate_btn, single_sel);
479
480 has_iter = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (state->model), &iter);
481 g_return_if_fail (has_iter);
482 gtk_widget_set_sensitive (state->up_btn,
483 single_sel &&
484 !gtk_tree_selection_iter_is_selected (selection, &iter));
485 gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (state->model), &iter, NULL,
486 gtk_tree_model_iter_n_children
487 (GTK_TREE_MODEL (state->model), NULL) - 1);
488 gtk_widget_set_sensitive (state->down_btn,
489 single_sel &&
490 !gtk_tree_selection_iter_is_selected (selection, &iter));
491
492 if (sheet != NULL)
493 wb_view_sheet_focus (
494 wb_control_view (GNM_WBC (state->wbcg)), sheet);
495
496 g_list_free_full (selected_rows, (GDestroyNotify) gtk_tree_path_free);
497 }
498
499 static void
cb_toggled_lock(G_GNUC_UNUSED GtkCellRendererToggle * cell,gchar * path_string,gpointer data)500 cb_toggled_lock (G_GNUC_UNUSED GtkCellRendererToggle *cell,
501 gchar *path_string,
502 gpointer data)
503 {
504 SheetManager *state = data;
505 GtkTreeModel *model = GTK_TREE_MODEL (state->model);
506 GtkTreeIter iter;
507 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
508 gboolean is_locked = TRUE;
509 Sheet *this_sheet = NULL;
510 WorkbookSheetState *old_state = NULL;
511 WorkbookControl *wbc = GNM_WBC (state->wbcg);
512 Workbook *wb = wb_control_get_workbook (wbc);
513
514 if (gtk_tree_model_get_iter (model, &iter, path)) {
515 gtk_tree_model_get (model, &iter,
516 SHEET_LOCKED, &is_locked,
517 SHEET_POINTER, &this_sheet,
518 -1);
519
520 if (is_locked) {
521 gtk_list_store_set
522 (GTK_LIST_STORE (model), &iter, SHEET_LOCKED,
523 FALSE, SHEET_LOCK_IMAGE,
524 state->image_padlock_no, -1);
525 } else {
526 gtk_list_store_set
527 (GTK_LIST_STORE (model), &iter, SHEET_LOCKED,
528 TRUE, SHEET_LOCK_IMAGE,
529 state->image_padlock, -1);
530 }
531 } else {
532 g_warning ("Did not get a valid iterator");
533 gtk_tree_path_free (path);
534 return;
535 }
536 gtk_tree_path_free (path);
537
538 old_state = workbook_sheet_state_new (wb);
539 g_object_set (this_sheet,
540 "protected", !is_locked,
541 NULL);
542 cmd_reorganize_sheets (wbc, old_state, this_sheet);
543 update_undo (state, wbc);
544 }
545
546 static void
cb_toggled_direction(G_GNUC_UNUSED GtkCellRendererToggle * cell,gchar * path_string,SheetManager * state)547 cb_toggled_direction (G_GNUC_UNUSED GtkCellRendererToggle *cell,
548 gchar *path_string,
549 SheetManager *state)
550 {
551 GtkTreeModel *model = GTK_TREE_MODEL (state->model);
552 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
553 GtkTreeIter iter;
554 gboolean is_rtl = TRUE;
555 Sheet *this_sheet = NULL;
556 WorkbookSheetState *old_state = NULL;
557 WorkbookControl *wbc = GNM_WBC (state->wbcg);
558 Workbook *wb = wb_control_get_workbook (wbc);
559
560 if (gtk_tree_model_get_iter (model, &iter, path)) {
561 gtk_tree_model_get (model, &iter,
562 SHEET_DIRECTION, &is_rtl,
563 SHEET_POINTER, &this_sheet,
564 -1);
565 gtk_list_store_set
566 (GTK_LIST_STORE (model), &iter,
567 SHEET_DIRECTION, !is_rtl,
568 SHEET_DIRECTION_IMAGE,
569 is_rtl ? state->image_ltr : state->image_rtl,
570 -1);
571 } else {
572 g_warning ("Did not get a valid iterator");
573 gtk_tree_path_free (path);
574 return;
575 }
576
577 gtk_tree_path_free (path);
578
579 old_state = workbook_sheet_state_new (wb);
580 g_object_set (this_sheet,
581 "text-is-rtl", !is_rtl,
582 NULL);
583 cmd_reorganize_sheets (wbc, old_state, this_sheet);
584 update_undo (state, wbc);
585 }
586
587 static void populate_sheet_list (SheetManager *state);
588
589 static void
cb_toggled_visible(G_GNUC_UNUSED GtkCellRendererToggle * cell,gchar * path_string,gpointer data)590 cb_toggled_visible (G_GNUC_UNUSED GtkCellRendererToggle *cell,
591 gchar *path_string,
592 gpointer data)
593 {
594 SheetManager *state = data;
595 GtkTreeModel *model = GTK_TREE_MODEL (state->model);
596 GtkTreeIter iter;
597 GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
598 gboolean is_visible;
599 Sheet *this_sheet;
600 WorkbookSheetState *old_state;
601 WorkbookControl *wbc = GNM_WBC (state->wbcg);
602 Workbook *wb = wb_control_get_workbook (wbc);
603 int cnt;
604
605 if (!gtk_tree_model_get_iter (model, &iter, path)) {
606 g_warning ("Did not get a valid iterator");
607 gtk_tree_path_free (path);
608 return;
609 }
610
611 gtk_tree_model_get (model, &iter,
612 SHEET_VISIBLE, &is_visible,
613 SHEET_POINTER, &this_sheet,
614 -1);
615
616 if (is_visible) {
617 cnt = sheet_order_cnt_visible (state);
618 if (cnt <= 1) {
619 go_gtk_notice_dialog (GTK_WINDOW (state->dialog), GTK_MESSAGE_ERROR,
620 _("At least one sheet must remain visible!"));
621 gtk_tree_path_free (path);
622 return;
623 }
624 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
625 SHEET_VISIBLE, FALSE,
626 SHEET_VISIBLE_IMAGE, NULL,
627 -1);
628
629 } else {
630 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
631 SHEET_VISIBLE, TRUE,
632 SHEET_VISIBLE_IMAGE,
633 state->image_visible,
634 -1);
635 }
636 gtk_tree_path_free (path);
637
638 old_state = workbook_sheet_state_new (wb);
639 g_object_set (this_sheet,
640 "visibility",
641 !is_visible ? GNM_SHEET_VISIBILITY_VISIBLE
642 : GNM_SHEET_VISIBILITY_HIDDEN,
643 NULL);
644
645 cmd_reorganize_sheets (wbc, old_state, this_sheet);
646 update_undo (state, wbc);
647
648 if (is_visible)
649 populate_sheet_list (state);
650 }
651
652 static gboolean
sheet_selection_can_toggle(GtkTreeSelection * selection,GtkTreeModel * model,GtkTreePath * path,gboolean path_currently_selected,G_GNUC_UNUSED gpointer data)653 sheet_selection_can_toggle(GtkTreeSelection *selection,
654 GtkTreeModel *model,
655 GtkTreePath *path,
656 gboolean path_currently_selected,
657 G_GNUC_UNUSED gpointer data)
658 {
659 GtkTreeIter iter;
660 gboolean is_visible;
661
662 if (path_currently_selected ||
663 !gtk_tree_model_get_iter (model, &iter, path))
664 return TRUE;
665
666 gtk_tree_model_get (model, &iter,
667 SHEET_VISIBLE, &is_visible,
668 -1);
669
670 return is_visible;
671 }
672
673 static void
create_sheet_list(SheetManager * state)674 create_sheet_list (SheetManager *state)
675 {
676 GtkTreeViewColumn *column;
677 GtkTreeSelection *selection;
678 GtkWidget *scrolled = go_gtk_builder_get_widget (state->gui, "scrolled");
679 GtkCellRenderer *renderer;
680
681 state->model = gtk_list_store_new (NUM_COLUMNS,
682 G_TYPE_BOOLEAN,
683 GDK_TYPE_PIXBUF,
684 G_TYPE_BOOLEAN,
685 GDK_TYPE_PIXBUF,
686 G_TYPE_INT,
687 G_TYPE_INT,
688 G_TYPE_STRING,
689 G_TYPE_STRING,
690 G_TYPE_POINTER,
691 GDK_TYPE_RGBA,
692 GDK_TYPE_RGBA,
693 G_TYPE_BOOLEAN,
694 GDK_TYPE_PIXBUF);
695 state->sheet_list = GTK_TREE_VIEW (gtk_tree_view_new_with_model
696 (GTK_TREE_MODEL (state->model)));
697 selection = gtk_tree_view_get_selection (state->sheet_list);
698 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
699
700 renderer = gnm_cell_renderer_toggle_new ();
701 g_signal_connect (G_OBJECT (renderer),
702 "toggled",
703 G_CALLBACK (cb_toggled_lock), state);
704 column = gtk_tree_view_column_new_with_attributes
705 /* xgettext : "Lock" is short for locked. Keep this short. */
706 (_("Lock"),
707 renderer,
708 "active", SHEET_LOCKED,
709 "pixbuf", SHEET_LOCK_IMAGE,
710 NULL);
711 gtk_tree_view_append_column (state->sheet_list, column);
712
713 renderer = gnm_cell_renderer_toggle_new ();
714 g_signal_connect (G_OBJECT (renderer),
715 "toggled",
716 G_CALLBACK (cb_toggled_visible), state);
717 column = gtk_tree_view_column_new_with_attributes
718 /* xgettext : "Viz" is short for visibility. Keep this short. */
719 (_("Viz"),
720 renderer,
721 "active", SHEET_VISIBLE,
722 "pixbuf", SHEET_VISIBLE_IMAGE,
723 NULL);
724 gtk_tree_view_append_column (state->sheet_list, column);
725
726 renderer = gnm_cell_renderer_toggle_new ();
727 g_signal_connect (G_OBJECT (renderer), "toggled",
728 G_CALLBACK (cb_toggled_direction), state);
729 column = gtk_tree_view_column_new_with_attributes
730 /* xgettext : "Dir" is short for direction. Keep this short. */
731 (_("Dir"),
732 renderer,
733 "active", SHEET_DIRECTION,
734 "pixbuf", SHEET_DIRECTION_IMAGE,
735 NULL);
736 gtk_tree_view_column_set_visible (column, FALSE);
737 gtk_tree_view_append_column (state->sheet_list, column);
738 state->dir_column = column;
739
740 column = gtk_tree_view_column_new_with_attributes
741 /*Translators: Table header for column with number of "Rows"*/
742 (C_("sheetlist", "Rows"),
743 gnm_cell_renderer_text_new (),
744 "text", SHEET_ROW_MAX,
745 NULL);
746 gtk_tree_view_column_set_visible (column, FALSE);
747 gtk_tree_view_append_column (state->sheet_list, column);
748 state->row_max_column = column;
749
750 column = gtk_tree_view_column_new_with_attributes
751 /*Translators: Table header for column with number of "Cols"*/
752 (C_("sheetlist", "Cols"),
753 gnm_cell_renderer_text_new (),
754 "text", SHEET_COL_MAX,
755 NULL);
756 gtk_tree_view_column_set_visible (column, FALSE);
757 gtk_tree_view_append_column (state->sheet_list, column);
758 state->col_max_column = column;
759
760 column = gtk_tree_view_column_new_with_attributes (_("Current Name"),
761 gnm_cell_renderer_text_new (),
762 "text", SHEET_NAME,
763 "background-rgba",BACKGROUND_COLOUR,
764 "foreground-rgba",FOREGROUND_COLOUR,
765 NULL);
766 gtk_tree_view_append_column (state->sheet_list, column);
767
768 renderer = gnm_cell_renderer_text_new ();
769 g_object_set (G_OBJECT (renderer),
770 "editable", TRUE,
771 "editable-set", TRUE,
772 NULL);
773 column = gtk_tree_view_column_new_with_attributes (_("New Name"),
774 renderer,
775 "text", SHEET_NEW_NAME,
776 "background-rgba",BACKGROUND_COLOUR,
777 "foreground-rgba",FOREGROUND_COLOUR,
778 NULL);
779 gtk_tree_view_append_column (state->sheet_list, column);
780 g_signal_connect (G_OBJECT (renderer), "edited",
781 G_CALLBACK (cb_name_edited), state);
782
783 gtk_tree_view_set_reorderable (state->sheet_list, TRUE);
784
785 /* Init the buttons & selection */
786 state->model_selection_changed_listener =
787 g_signal_connect (selection,
788 "changed",
789 G_CALLBACK (cb_selection_changed), state);
790 gtk_tree_selection_set_select_function (selection,
791 sheet_selection_can_toggle,
792 NULL, NULL);
793
794 gtk_container_add (GTK_CONTAINER (scrolled), GTK_WIDGET (state->sheet_list));
795 }
796
797 static void
set_sheet_info_at_iter(SheetManager * state,GtkTreeIter * iter,Sheet * sheet)798 set_sheet_info_at_iter (SheetManager *state, GtkTreeIter *iter, Sheet *sheet)
799 {
800 GdkRGBA cback, *color = NULL;
801 GdkRGBA cfore, *text_color = NULL;
802
803 if (sheet->tab_color)
804 color = go_color_to_gdk_rgba (sheet->tab_color->go_color, &cback);
805 if (sheet->tab_text_color)
806 text_color = go_color_to_gdk_rgba (sheet->tab_text_color->go_color, &cfore);
807
808 gtk_list_store_set (state->model, iter,
809 SHEET_LOCKED, sheet->is_protected,
810 SHEET_LOCK_IMAGE, (sheet->is_protected
811 ? state->image_padlock
812 : state->image_padlock_no),
813 SHEET_VISIBLE, (sheet->visibility == GNM_SHEET_VISIBILITY_VISIBLE),
814 SHEET_VISIBLE_IMAGE, (sheet->visibility == GNM_SHEET_VISIBILITY_VISIBLE
815 ? state->image_visible
816 : NULL),
817 SHEET_ROW_MAX, gnm_sheet_get_max_rows (sheet),
818 SHEET_COL_MAX, gnm_sheet_get_max_cols (sheet),
819 SHEET_NAME, sheet->name_unquoted,
820 SHEET_NEW_NAME, "",
821 SHEET_POINTER, sheet,
822 BACKGROUND_COLOUR, color,
823 FOREGROUND_COLOUR, text_color,
824 SHEET_DIRECTION, sheet->text_is_rtl,
825 SHEET_DIRECTION_IMAGE, (sheet->text_is_rtl
826 ? state->image_rtl
827 : state->image_ltr),
828 -1);
829
830
831 }
832
833 /* Add all of the sheets to the sheet_list */
834 static void
populate_sheet_list(SheetManager * state)835 populate_sheet_list (SheetManager *state)
836 {
837 GtkTreeSelection *selection;
838 GtkTreeIter iter;
839 WorkbookControl *wbc = GNM_WBC (state->wbcg);
840 Workbook *wb = wb_control_get_workbook (wbc);
841 Sheet *cur_sheet = wb_control_cur_sheet (wbc);
842 int i, n = workbook_sheet_count (wb);
843 GtkTreePath *sel_path = NULL;
844
845 selection = gtk_tree_view_get_selection (state->sheet_list);
846
847 g_signal_handler_block (selection, state->model_selection_changed_listener);
848 if (state->model_row_insertion_listener)
849 g_signal_handler_block (state->model, state->model_row_insertion_listener);
850
851 gtk_list_store_clear (state->model);
852 gtk_label_set_text (GTK_LABEL (state->warning), "");
853
854 for (i = 0 ; i < n ; i++) {
855 Sheet *sheet = workbook_sheet_by_index (wb, i);
856
857 gtk_list_store_append (state->model, &iter);
858 set_sheet_info_at_iter (state, &iter, sheet);
859
860 if (sheet == cur_sheet)
861 sel_path = gtk_tree_model_get_path (GTK_TREE_MODEL (state->model),
862 &iter);
863 }
864
865 if (sel_path) {
866 gtk_tree_selection_select_path (selection, sel_path);
867 gtk_tree_path_free (sel_path);
868 }
869
870 if (state->model_row_insertion_listener)
871 g_signal_handler_unblock (state->model, state->model_row_insertion_listener);
872 g_signal_handler_unblock (selection, state->model_selection_changed_listener);
873
874 /* Init the buttons & selection */
875 cb_selection_changed (NULL, state);
876 }
877
878 static void
cb_item_move(SheetManager * state,gnm_iter_search_t iter_search)879 cb_item_move (SheetManager *state, gnm_iter_search_t iter_search)
880 {
881 GtkTreeSelection *selection = gtk_tree_view_get_selection (state->sheet_list);
882 GtkTreeIter a, b;
883 GList *selected_rows;
884
885 g_return_if_fail (selection != NULL);
886 g_return_if_fail (gtk_tree_selection_count_selected_rows (selection) == 1);
887
888 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
889 gtk_tree_model_get_iter (GTK_TREE_MODEL (state->model),
890 &a, (GtkTreePath *) selected_rows->data);
891 g_list_free_full (selected_rows, (GDestroyNotify) gtk_tree_path_free);
892
893 b = a;
894 if (!iter_search (GTK_TREE_MODEL (state->model), &b))
895 return;
896
897 gtk_list_store_swap (state->model, &a, &b);
898 cb_selection_changed (NULL, state);
899 }
900
901 static void
cb_up(G_GNUC_UNUSED GtkWidget * w,SheetManager * state)902 cb_up (G_GNUC_UNUSED GtkWidget *w, SheetManager *state)
903 {
904 cb_item_move (state, gtk_tree_model_iter_previous);
905 }
906
907 static void
cb_down(G_GNUC_UNUSED GtkWidget * w,SheetManager * state)908 cb_down (G_GNUC_UNUSED GtkWidget *w, SheetManager *state)
909 {
910 cb_item_move (state, gtk_tree_model_iter_next);
911 }
912
913 static void
cb_add_clicked(G_GNUC_UNUSED GtkWidget * ignore,SheetManager * state)914 cb_add_clicked (G_GNUC_UNUSED GtkWidget *ignore, SheetManager *state)
915 {
916 GtkTreeIter sel_iter, iter;
917 GtkTreeSelection *selection = gtk_tree_view_get_selection (state->sheet_list);
918 GList *selected_rows;
919 int index = -1;
920 WorkbookSheetState *old_state;
921 WorkbookControl *wbc = GNM_WBC (state->wbcg);
922 Workbook *wb = wb_control_get_workbook (wbc);
923 Sheet *sheet, *old_sheet = NULL;
924
925 g_return_if_fail (selection != NULL);
926 g_return_if_fail (gtk_tree_selection_count_selected_rows (selection) == 1);
927
928 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
929 gtk_tree_model_get_iter (GTK_TREE_MODEL (state->model),
930 &sel_iter, (GtkTreePath *) selected_rows->data);
931 g_list_free_full (selected_rows, (GDestroyNotify) gtk_tree_path_free);
932
933 gtk_tree_model_get (GTK_TREE_MODEL (state->model), &sel_iter,
934 SHEET_POINTER, &old_sheet,
935 -1);
936 index = old_sheet->index_in_wb;
937
938 workbook_signals_block (state);
939
940 old_state = workbook_sheet_state_new (wb);
941 workbook_sheet_add (wb, index,
942 gnm_sheet_get_max_cols (old_sheet),
943 gnm_sheet_get_max_rows (old_sheet));
944 cmd_reorganize_sheets (wbc, old_state, NULL);
945 update_undo (state, wbc);
946
947 workbook_signals_unblock (state);
948
949 g_signal_handler_block (state->model, state->model_row_insertion_listener);
950 sheet = workbook_sheet_by_index (wb, index);
951 gtk_list_store_insert_before (state->model, &iter, &sel_iter);
952 g_signal_handler_unblock (state->model, state->model_row_insertion_listener);
953
954 set_sheet_info_at_iter (state, &iter, sheet);
955
956 cb_selection_changed (NULL, state);
957 }
958
959 static void
cb_append_clicked(G_GNUC_UNUSED GtkWidget * ignore,SheetManager * state)960 cb_append_clicked (G_GNUC_UNUSED GtkWidget *ignore, SheetManager *state)
961 {
962 WorkbookSheetState *old_state;
963 WorkbookControl *wbc = GNM_WBC (state->wbcg);
964 Workbook *wb = wb_control_get_workbook (wbc);
965 GtkTreeIter iter;
966 Sheet *sheet, *old_sheet;
967
968 workbook_signals_block (state);
969
970 old_state = workbook_sheet_state_new (wb);
971 old_sheet = workbook_sheet_by_index (wb, 0);
972 workbook_sheet_add (wb, -1,
973 gnm_sheet_get_max_cols (old_sheet),
974 gnm_sheet_get_max_rows (old_sheet));
975 cmd_reorganize_sheets (wbc, old_state, NULL);
976 update_undo (state, wbc);
977
978 workbook_signals_unblock (state);
979
980 sheet = workbook_sheet_by_index (wb, workbook_sheet_count (wb) - 1);
981
982 g_signal_handler_block (state->model, state->model_row_insertion_listener);
983 gtk_list_store_append (state->model, &iter);
984 g_signal_handler_unblock (state->model, state->model_row_insertion_listener);
985
986 set_sheet_info_at_iter (state, &iter, sheet);
987
988 cb_selection_changed (NULL, state);
989 }
990
991 static void
cb_duplicate_clicked(G_GNUC_UNUSED GtkWidget * ignore,SheetManager * state)992 cb_duplicate_clicked (G_GNUC_UNUSED GtkWidget *ignore,
993 SheetManager *state)
994 {
995 GtkTreeIter sel_iter, iter;
996 GtkTreeSelection *selection = gtk_tree_view_get_selection (state->sheet_list);
997 GList *selected_rows;
998 WorkbookSheetState *old_state;
999 int index;
1000 WorkbookControl *wbc = GNM_WBC (state->wbcg);
1001 Workbook *wb = wb_control_get_workbook (wbc);
1002 Sheet *new_sheet, *this_sheet;
1003
1004 g_return_if_fail (selection != NULL);
1005 g_return_if_fail (gtk_tree_selection_count_selected_rows (selection) == 1);
1006
1007 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1008 gtk_tree_model_get_iter (GTK_TREE_MODEL (state->model),
1009 &sel_iter, (GtkTreePath *) selected_rows->data);
1010 g_list_free_full (selected_rows, (GDestroyNotify) gtk_tree_path_free);
1011
1012 gtk_tree_model_get (GTK_TREE_MODEL (state->model), &sel_iter,
1013 SHEET_POINTER, &this_sheet,
1014 -1);
1015
1016 workbook_signals_block (state);
1017
1018 old_state = workbook_sheet_state_new (wb);
1019 index = this_sheet->index_in_wb;
1020 new_sheet = sheet_dup (this_sheet);
1021 workbook_sheet_attach_at_pos (wb, new_sheet, index + 1);
1022 g_signal_emit_by_name (G_OBJECT (wb), "sheet_added", 0);
1023 cmd_reorganize_sheets (wbc, old_state, NULL);
1024 update_undo (state, wbc);
1025
1026 workbook_signals_unblock (state);
1027
1028 g_signal_handler_block (state->model, state->model_row_insertion_listener);
1029 gtk_list_store_insert_after (state->model, &iter, &sel_iter);
1030 g_signal_handler_unblock (state->model, state->model_row_insertion_listener);
1031
1032 set_sheet_info_at_iter (state, &iter, new_sheet);
1033 g_object_unref (new_sheet);
1034
1035 cb_selection_changed (NULL, state);
1036 }
1037
1038 static void
cb_delete_clicked(G_GNUC_UNUSED GtkWidget * ignore,SheetManager * state)1039 cb_delete_clicked (G_GNUC_UNUSED GtkWidget *ignore,
1040 SheetManager *state)
1041 {
1042 GtkTreeSelection *selection = gtk_tree_view_get_selection (state->sheet_list);
1043 GList *selected_rows, *l;
1044 WorkbookSheetState *old_state;
1045 WorkbookControl *wbc = GNM_WBC (state->wbcg);
1046 Workbook *wb = wb_control_get_workbook (wbc);
1047
1048 g_return_if_fail (selection != NULL);
1049
1050 selected_rows = gtk_tree_selection_get_selected_rows (selection, NULL);
1051
1052 for (l = selected_rows; l != NULL; l = l->next)
1053 l->data = gtk_tree_row_reference_new (GTK_TREE_MODEL (state->model),
1054 (GtkTreePath *) l->data);
1055 workbook_signals_block (state);
1056 old_state = workbook_sheet_state_new (wb);
1057
1058 for (l = selected_rows; l != NULL; l = l->next) {
1059 GtkTreeRowReference *ref = l->data;
1060 if (gtk_tree_row_reference_valid (ref)) {
1061 GtkTreePath *path = gtk_tree_row_reference_get_path (ref);
1062 GtkTreeIter sel_iter;
1063 Sheet *sheet;
1064
1065 gtk_tree_model_get_iter (GTK_TREE_MODEL (state->model), &sel_iter, path);
1066 gtk_tree_path_free (path);
1067 gtk_tree_model_get (GTK_TREE_MODEL (state->model), &sel_iter,
1068 SHEET_POINTER, &sheet,
1069 -1);
1070 gtk_list_store_remove (state->model, &sel_iter);
1071 workbook_sheet_delete (sheet);
1072 }
1073 }
1074
1075 cmd_reorganize_sheets (wbc, old_state, NULL);
1076 update_undo (state, wbc);
1077 workbook_signals_unblock (state);
1078
1079 populate_sheet_list (state);
1080 cb_name_edited (NULL, NULL, NULL, state);
1081
1082 g_list_free_full (selected_rows, (GDestroyNotify) gtk_tree_row_reference_free);
1083 }
1084
1085 static void
cb_cancel_clicked(G_GNUC_UNUSED GtkWidget * ignore,SheetManager * state)1086 cb_cancel_clicked (G_GNUC_UNUSED GtkWidget *ignore,
1087 SheetManager *state)
1088 {
1089 gtk_widget_destroy (GTK_WIDGET (state->dialog));
1090 }
1091
1092 static char *
verify_validity(SheetManager * state,gboolean * pchanged)1093 verify_validity (SheetManager *state, gboolean *pchanged)
1094 {
1095 char *result = NULL;
1096 gboolean changed = FALSE;
1097 GHashTable *names = g_hash_table_new_full (g_str_hash, g_str_equal,
1098 (GDestroyNotify)g_free, NULL);
1099 GtkTreeIter this_iter;
1100 gint n = 0;
1101
1102 while (result == NULL &&
1103 gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (state->model),
1104 &this_iter, NULL, n)) {
1105 Sheet *this_sheet;
1106 char *old_name, *new_name, *new_name2;
1107
1108 gtk_tree_model_get (GTK_TREE_MODEL (state->model), &this_iter,
1109 SHEET_POINTER, &this_sheet,
1110 SHEET_NAME, &old_name,
1111 SHEET_NEW_NAME, &new_name,
1112 -1);
1113
1114 new_name2 = g_utf8_casefold (*new_name != 0 ? new_name : old_name, -1);
1115 if (g_hash_table_lookup (names, new_name2)) {
1116 result = g_strdup_printf (_("You may not call more than one sheet \"%s\"."),
1117 *new_name != 0 ? new_name : old_name);
1118 g_free (new_name2);
1119 } else
1120 g_hash_table_insert (names, new_name2, new_name2);
1121
1122 if (*new_name && strcmp (old_name, new_name))
1123 changed = TRUE;
1124
1125 g_free (old_name);
1126 g_free (new_name);
1127 n++;
1128 }
1129
1130 g_hash_table_destroy (names);
1131 *pchanged = changed;
1132 return result;
1133 }
1134
1135
1136 static void
cb_apply_names_clicked(G_GNUC_UNUSED GtkWidget * ignore,SheetManager * state)1137 cb_apply_names_clicked (G_GNUC_UNUSED GtkWidget *ignore, SheetManager *state)
1138 {
1139 WorkbookControl *wbc = GNM_WBC (state->wbcg);
1140 Workbook *wb = wb_control_get_workbook (wbc);
1141 WorkbookSheetState *old_state;
1142 GtkTreeIter this_iter;
1143 gint n = 0;
1144
1145 /* Stop listening to changes in the sheet order. */
1146 workbook_signals_block (state);
1147
1148 old_state = workbook_sheet_state_new (wb);
1149 while (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (state->model),
1150 &this_iter, NULL, n)) {
1151 Sheet *this_sheet;
1152 char *new_name;
1153
1154 gtk_tree_model_get (GTK_TREE_MODEL (state->model), &this_iter,
1155 SHEET_POINTER, &this_sheet,
1156 SHEET_NEW_NAME, &new_name,
1157 -1);
1158
1159 if (*new_name) {
1160 g_object_set (this_sheet,
1161 "name", new_name,
1162 NULL);
1163 gtk_list_store_set (state->model, &this_iter,
1164 SHEET_NAME, new_name,
1165 SHEET_NEW_NAME, "",
1166 -1);
1167 }
1168
1169 g_free (new_name);
1170 n++;
1171 }
1172
1173 cmd_reorganize_sheets (wbc, old_state, NULL);
1174 gtk_label_set_text (GTK_LABEL (state->warning), "");
1175 update_undo (state, wbc);
1176
1177 workbook_signals_unblock (state);
1178 }
1179
1180 static void
cb_sheet_order_destroy(SheetManager * state)1181 cb_sheet_order_destroy (SheetManager *state)
1182 {
1183 Workbook *wb = wb_control_get_workbook (GNM_WBC (state->wbcg));
1184
1185 /* Stop to listen to changes in the sheet order. */
1186 if (state->sheet_order_changed_listener)
1187 g_signal_handler_disconnect (G_OBJECT (wb),
1188 state->sheet_order_changed_listener);
1189 if (state->sheet_added_listener)
1190 g_signal_handler_disconnect (G_OBJECT (wb),
1191 state->sheet_added_listener);
1192 if (state->sheet_deleted_listener)
1193 g_signal_handler_disconnect (G_OBJECT (wb),
1194 state->sheet_deleted_listener);
1195
1196 if (state->model != NULL) {
1197 g_object_unref (state->model);
1198 state->model = NULL;
1199 }
1200 g_object_unref (state->gui);
1201 g_object_set_data (G_OBJECT (wb), SHEET_ORDER_KEY, NULL);
1202 state->gui = NULL;
1203
1204 g_object_unref (state->image_padlock);
1205 state->image_padlock = NULL;
1206
1207 g_object_unref (state->image_padlock_no);
1208 state->image_padlock_no = NULL;
1209
1210 g_object_unref (state->image_visible);
1211 state->image_visible = NULL;
1212
1213 g_object_unref (state->image_rtl);
1214 state->image_rtl = NULL;
1215
1216 g_object_unref (state->image_ltr);
1217 state->image_ltr = NULL;
1218
1219 g_free (state);
1220 }
1221
1222 static void
dialog_sheet_order_update_sheet_order(SheetManager * state)1223 dialog_sheet_order_update_sheet_order (SheetManager *state)
1224 {
1225 GtkTreeIter iter;
1226 Workbook *wb = wb_control_get_workbook (GNM_WBC (state->wbcg));
1227 int i, n_sheets, n_children;
1228 GtkTreeModel *model = GTK_TREE_MODEL (state->model);
1229 GtkTreeSelection *sel = gtk_tree_view_get_selection (state->sheet_list);
1230
1231 n_sheets = workbook_sheet_count (wb);
1232 n_children = gtk_tree_model_iter_n_children (model, NULL);
1233
1234 if (n_sheets != n_children) {
1235 /* This signal also occurs when sheets are added or deleted. We handle this */
1236 /* when those signals arrive. */
1237 return;
1238 }
1239
1240 for (i = 0; i < n_sheets; i++) {
1241 gchar *name, *new_name;
1242 gboolean is_locked;
1243 gboolean is_visible;
1244 gboolean is_rtl;
1245 GdkRGBA *back, *fore;
1246 Sheet *sheet_wb = workbook_sheet_by_index (wb, i);
1247 Sheet *sheet_model;
1248 gboolean selected;
1249 int j, row_max, col_max;
1250
1251 for (j = i; j < n_children; j++) {
1252 if (!gtk_tree_model_iter_nth_child (model, &iter,
1253 NULL, j))
1254 break;
1255 gtk_tree_model_get (model, &iter, SHEET_POINTER,
1256 &sheet_model, -1);
1257 if (sheet_model == sheet_wb)
1258 break;
1259 }
1260 if (j == i)
1261 continue;
1262
1263 if (!gtk_tree_model_iter_nth_child (model, &iter, NULL, j))
1264 break;
1265 selected = gtk_tree_selection_iter_is_selected (sel, &iter);
1266 gtk_tree_model_get (model, &iter,
1267 SHEET_LOCKED, &is_locked,
1268 SHEET_VISIBLE, &is_visible,
1269 SHEET_ROW_MAX, &row_max,
1270 SHEET_COL_MAX, &col_max,
1271 SHEET_NAME, &name,
1272 SHEET_NEW_NAME, &new_name,
1273 SHEET_POINTER, &sheet_model,
1274 BACKGROUND_COLOUR, &back,
1275 FOREGROUND_COLOUR, &fore,
1276 SHEET_DIRECTION, &is_rtl,
1277 -1);
1278 gtk_list_store_remove (state->model, &iter);
1279 g_signal_handler_block (state->model, state->model_row_insertion_listener);
1280 gtk_list_store_insert (state->model, &iter, i);
1281 g_signal_handler_unblock (state->model, state->model_row_insertion_listener);
1282 gtk_list_store_set (state->model, &iter,
1283 SHEET_LOCKED, is_locked,
1284 SHEET_LOCK_IMAGE, is_locked ?
1285 state->image_padlock : state->image_padlock_no,
1286 SHEET_VISIBLE, is_visible,
1287 SHEET_VISIBLE_IMAGE, is_visible ?
1288 state->image_visible : NULL,
1289 SHEET_ROW_MAX, row_max,
1290 SHEET_COL_MAX, col_max,
1291 SHEET_NAME, name,
1292 SHEET_NEW_NAME, new_name,
1293 SHEET_POINTER, sheet_model,
1294 BACKGROUND_COLOUR, back,
1295 FOREGROUND_COLOUR, fore,
1296 SHEET_DIRECTION, is_rtl,
1297 SHEET_DIRECTION_IMAGE,
1298 is_rtl ? state->image_rtl : state->image_ltr,
1299 -1);
1300 if (back)
1301 gdk_rgba_free (back);
1302 if (fore)
1303 gdk_rgba_free (fore);
1304 g_free (name);
1305 g_free (new_name);
1306 if (selected)
1307 gtk_tree_selection_select_iter (sel, &iter);
1308 }
1309
1310 cb_selection_changed (NULL, state);
1311 }
1312
1313 static void
cb_sheet_order_changed(Workbook * wb,SheetManager * state)1314 cb_sheet_order_changed (Workbook *wb, SheetManager *state)
1315 {
1316 dialog_sheet_order_update_sheet_order (state);
1317 }
1318
1319 static void
cb_sheet_deleted(Workbook * wb,SheetManager * state)1320 cb_sheet_deleted (Workbook *wb, SheetManager *state)
1321 {
1322 populate_sheet_list (state);
1323 }
1324
1325 static void
cb_sheet_added(Workbook * wb,SheetManager * state)1326 cb_sheet_added (Workbook *wb, SheetManager *state)
1327 {
1328 populate_sheet_list (state);
1329 }
1330
1331
1332
1333 static void
dialog_sheet_order_changed(SheetManager * state)1334 dialog_sheet_order_changed (SheetManager *state)
1335 {
1336 WorkbookControl *wbc = GNM_WBC (state->wbcg);
1337 Workbook *wb = wb_control_get_workbook (wbc);
1338 WorkbookSheetState *old_state;
1339 GtkTreeIter this_iter;
1340 gint n = 0, changes = 0;
1341
1342 workbook_signals_block (state);
1343
1344 old_state = workbook_sheet_state_new (wb);
1345 while (gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (state->model),
1346 &this_iter, NULL, n)) {
1347 Sheet *this_sheet;
1348 gtk_tree_model_get (GTK_TREE_MODEL (state->model), &this_iter,
1349 SHEET_POINTER, &this_sheet,
1350 -1);
1351 if (this_sheet->index_in_wb != n) {
1352 changes++;
1353 workbook_sheet_move (this_sheet, n - this_sheet->index_in_wb);
1354 }
1355 n++;
1356 }
1357
1358 if (changes > 0) {
1359 cmd_reorganize_sheets (wbc, old_state, NULL);
1360 update_undo (state, wbc);
1361 } else
1362 workbook_sheet_state_unref (old_state);
1363
1364 workbook_signals_unblock (state);
1365 }
1366
1367 static void
cb_dialog_order_changed(G_GNUC_UNUSED GtkListStore * model,G_GNUC_UNUSED GtkTreePath * path,G_GNUC_UNUSED GtkTreeIter * iter,G_GNUC_UNUSED gpointer arg3,SheetManager * state)1368 cb_dialog_order_changed (G_GNUC_UNUSED GtkListStore *model,
1369 G_GNUC_UNUSED GtkTreePath *path,
1370 G_GNUC_UNUSED GtkTreeIter *iter,
1371 G_GNUC_UNUSED gpointer arg3,
1372 SheetManager *state)
1373 {
1374 dialog_sheet_order_changed (state);
1375 }
1376
1377 static gboolean
dialog_sheet_order_changed_idle_handler(SheetManager * state)1378 dialog_sheet_order_changed_idle_handler (SheetManager *state)
1379 {
1380 dialog_sheet_order_changed (state);
1381 return FALSE;
1382 }
1383
1384
1385 static void
cb_dialog_order_changed_by_insertion(G_GNUC_UNUSED GtkListStore * model,G_GNUC_UNUSED GtkTreePath * path,G_GNUC_UNUSED GtkTreeIter * iter,SheetManager * state)1386 cb_dialog_order_changed_by_insertion (G_GNUC_UNUSED GtkListStore *model,
1387 G_GNUC_UNUSED GtkTreePath *path,
1388 G_GNUC_UNUSED GtkTreeIter *iter,
1389 SheetManager *state)
1390 {
1391 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1392 (GSourceFunc)dialog_sheet_order_changed_idle_handler,
1393 state, NULL);
1394 }
1395
1396 static void
cb_undo_clicked(G_GNUC_UNUSED GtkWidget * ignore,SheetManager * state)1397 cb_undo_clicked (G_GNUC_UNUSED GtkWidget *ignore, SheetManager *state)
1398 {
1399 WorkbookControl *wbc = GNM_WBC (state->wbcg);
1400 Workbook *wb = wb_control_get_workbook (wbc);
1401
1402 command_undo (wbc);
1403 gtk_widget_set_sensitive (state->undo_btn, wb->undo_commands != NULL);
1404
1405 populate_sheet_list (state);
1406 }
1407
1408 static void
cb_adv_check_toggled(G_GNUC_UNUSED GtkToggleButton * ignored,SheetManager * state)1409 cb_adv_check_toggled (G_GNUC_UNUSED GtkToggleButton *ignored,
1410 SheetManager *state)
1411 {
1412 gboolean visible = gtk_toggle_button_get_active
1413 (GTK_TOGGLE_BUTTON (state->advanced_check));
1414
1415 gtk_tree_view_column_set_visible (state->dir_column, visible);
1416 gtk_tree_view_column_set_visible (state->col_max_column, visible);
1417 gtk_tree_view_column_set_visible (state->row_max_column, visible);
1418 }
1419
1420 static void
destroy_cb(GObject * obj)1421 destroy_cb (GObject *obj)
1422 {
1423 g_object_set_data (obj, "state", NULL);
1424 }
1425
1426 void
dialog_sheet_order(WBCGtk * wbcg)1427 dialog_sheet_order (WBCGtk *wbcg)
1428 {
1429 SheetManager *state;
1430 GtkBuilder *gui;
1431 GtkGrid *grid;
1432 GOColorGroup *cg;
1433 Workbook *wb;
1434 GtkWidget *widget;
1435 GdkPixbuf *icon;
1436
1437 g_return_if_fail (wbcg != NULL);
1438
1439 widget = GTK_WIDGET (wbcg_toplevel (wbcg));
1440
1441 gui = gnm_gtk_builder_load ("res:ui/sheet-order.ui", NULL, GO_CMD_CONTEXT (wbcg));
1442 if (gui == NULL)
1443 return;
1444
1445 wb = wb_control_get_workbook (GNM_WBC (wbcg));
1446 if (g_object_get_data (G_OBJECT (wb), SHEET_ORDER_KEY)) {
1447 GtkWidget *dialog = gtk_message_dialog_new
1448 (wbcg_toplevel (wbcg),
1449 GTK_DIALOG_DESTROY_WITH_PARENT,
1450 GTK_MESSAGE_WARNING,
1451 GTK_BUTTONS_CLOSE,
1452 _("Another view is already managing sheets"));
1453 go_gtk_dialog_run (GTK_DIALOG (dialog), wbcg_toplevel (wbcg));
1454 return;
1455 }
1456 g_object_set_data (G_OBJECT (wb), SHEET_ORDER_KEY, (gpointer) gui);
1457 state = g_new0 (SheetManager, 1);
1458 state->gui = gui;
1459 state->wbcg = wbcg;
1460 state->dialog = go_gtk_builder_get_widget (gui, "sheet-order-dialog");
1461 state->warning = go_gtk_builder_get_widget (gui, "warning");
1462 state->up_btn = go_gtk_builder_get_widget (gui, "up_button");
1463 state->down_btn = go_gtk_builder_get_widget (gui, "down_button");
1464 state->add_btn = go_gtk_builder_get_widget (gui, "add_button");
1465 state->append_btn = go_gtk_builder_get_widget (gui, "append_button");
1466 state->duplicate_btn = go_gtk_builder_get_widget (gui, "duplicate_button");
1467 state->delete_btn = go_gtk_builder_get_widget (gui, "delete_button");
1468
1469 state->apply_names_btn = go_gtk_builder_get_widget (gui, "ok_button");
1470 state->sort_asc_btn = go_gtk_builder_get_widget (gui, "sort-asc-button");
1471 state->sort_desc_btn = go_gtk_builder_get_widget (gui, "sort-desc-button");
1472 state->undo_btn = go_gtk_builder_get_widget (gui, "undo-button");
1473 state->cancel_btn = go_gtk_builder_get_widget (gui, "cancel_button");
1474 state->advanced_check = go_gtk_builder_get_widget (gui, "advanced-check");
1475 state->initial_colors_set = FALSE;
1476 state->image_padlock = go_gtk_widget_render_icon_pixbuf (widget, "gnumeric-protection-yes", GTK_ICON_SIZE_MENU);
1477 state->image_padlock_no = go_gtk_widget_render_icon_pixbuf (widget, "gnumeric-protection-no", GTK_ICON_SIZE_MENU);
1478 state->image_visible = go_gtk_widget_render_icon_pixbuf (widget, "gnumeric-visible", GTK_ICON_SIZE_MENU);
1479 state->image_ltr = go_gtk_widget_render_icon_pixbuf (widget, "format-text-direction-ltr", GTK_ICON_SIZE_MENU);
1480 state->image_rtl = go_gtk_widget_render_icon_pixbuf (widget, "format-text-direction-rtl", GTK_ICON_SIZE_MENU);
1481 /* Listen for changes in the sheet order. */
1482 state->sheet_order_changed_listener = g_signal_connect (G_OBJECT (wb),
1483 "sheet_order_changed", G_CALLBACK (cb_sheet_order_changed),
1484 state);
1485 state->sheet_added_listener = g_signal_connect (G_OBJECT (wb),
1486 "sheet_added", G_CALLBACK (cb_sheet_added),
1487 state);
1488 state->sheet_deleted_listener = g_signal_connect (G_OBJECT (wb),
1489 "sheet_deleted", G_CALLBACK (cb_sheet_deleted),
1490 state);
1491
1492 grid = GTK_GRID (go_gtk_builder_get_widget (gui,"main-grid"));
1493 cg = go_color_group_fetch ("back_color_group",
1494 wb_control_view (GNM_WBC (wbcg)));
1495 icon = go_gtk_widget_render_icon_pixbuf (widget, "gnumeric-bucket", GTK_ICON_SIZE_LARGE_TOOLBAR);
1496 state->ccombo_back = go_combo_color_new (icon, _("Default"), 0, cg);
1497 g_object_unref (icon);
1498 g_object_unref (cg);
1499 go_combo_color_set_instant_apply (
1500 GO_COMBO_COLOR (state->ccombo_back), TRUE);
1501 gtk_grid_attach (grid, state->ccombo_back, 1, 4, 1, 1);
1502 gtk_widget_set_sensitive (state->ccombo_back, FALSE);
1503
1504 cg = go_color_group_fetch ("fore_color_group",
1505 wb_control_view (GNM_WBC (wbcg)));
1506 icon = go_gtk_widget_render_icon_pixbuf (widget, "font", GTK_ICON_SIZE_LARGE_TOOLBAR);
1507 state->ccombo_fore = go_combo_color_new (icon, _("Default"), 0, cg);
1508 g_object_unref (icon);
1509 g_object_unref (cg);
1510 go_combo_color_set_instant_apply (
1511 GO_COMBO_COLOR (state->ccombo_fore), TRUE);
1512 gtk_grid_attach (grid, state->ccombo_fore, 2, 4, 1, 1);
1513 gtk_widget_set_sensitive (state->ccombo_fore, FALSE);
1514
1515 create_sheet_list (state);
1516 populate_sheet_list (state);
1517
1518 #define CONNECT(o,s,c) g_signal_connect(G_OBJECT(o),s,G_CALLBACK(c),state)
1519 CONNECT (state->up_btn, "clicked", cb_up);
1520 CONNECT (state->down_btn, "clicked", cb_down);
1521 CONNECT (state->sort_asc_btn, "clicked", cb_asc);
1522 CONNECT (state->sort_desc_btn, "clicked", cb_desc);
1523 CONNECT (state->add_btn, "clicked", cb_add_clicked);
1524 CONNECT (state->append_btn, "clicked", cb_append_clicked);
1525 CONNECT (state->duplicate_btn, "clicked", cb_duplicate_clicked);
1526 CONNECT (state->delete_btn, "clicked", cb_delete_clicked);
1527 CONNECT (state->apply_names_btn, "clicked", cb_apply_names_clicked);
1528 CONNECT (state->cancel_btn, "clicked", cb_cancel_clicked);
1529 CONNECT (state->undo_btn, "clicked", cb_undo_clicked);
1530 CONNECT (state->advanced_check, "toggled", cb_adv_check_toggled);
1531 CONNECT (state->ccombo_back, "color_changed", cb_color_changed_back);
1532 CONNECT (state->ccombo_fore, "color_changed", cb_color_changed_fore);
1533 CONNECT (state->model, "rows-reordered", cb_dialog_order_changed);
1534 state->model_row_insertion_listener =
1535 CONNECT (state->model, "row-inserted", cb_dialog_order_changed_by_insertion);
1536 #undef CONNECT
1537
1538 cb_adv_check_toggled (NULL, state);
1539
1540 gnm_init_help_button (
1541 go_gtk_builder_get_widget (state->gui, "help_button"),
1542 GNUMERIC_HELP_LINK_SHEET_MANAGER);
1543
1544 gtk_widget_set_sensitive (state->undo_btn, wb->undo_commands != NULL);
1545 gtk_widget_set_sensitive (state->apply_names_btn, FALSE);
1546
1547 /* a candidate for merging into attach guru */
1548 wbc_gtk_attach_guru (state->wbcg, GTK_WIDGET (state->dialog));
1549 g_object_set_data_full (G_OBJECT (state->dialog),
1550 "state", state, (GDestroyNotify) cb_sheet_order_destroy);
1551 g_signal_connect (G_OBJECT (state->dialog), "destroy", G_CALLBACK (destroy_cb), NULL);
1552
1553 gnm_restore_window_geometry (GTK_WINDOW (state->dialog),
1554 SHEET_ORDER_KEY);
1555
1556 go_gtk_nonmodal_dialog (wbcg_toplevel (state->wbcg),
1557 GTK_WINDOW (state->dialog));
1558 gtk_widget_show_all (GTK_WIDGET (state->dialog));
1559 }
1560