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