1 /*
2  * dialog-sheet-resize.c: Dialog to resize current or all sheets.
3  *
4  * Author:
5  *	Morten Welinder <terra@gnome.org>
6  *
7  * (C) Copyright 2009 Morten Welinder <terra@gnome.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, see <https://www.gnu.org/licenses/>.
21  */
22 
23 #include <gnumeric-config.h>
24 #include <glib/gi18n-lib.h>
25 #include <gnumeric.h>
26 #include <dialogs/dialogs.h>
27 #include <dialogs/help.h>
28 
29 #include <gui-util.h>
30 #include <wbc-gtk.h>
31 #include <workbook-view.h>
32 #include <workbook.h>
33 #include <sheet.h>
34 #include <commands.h>
35 
36 #define RESIZE_DIALOG_KEY "sheet-resize-dialog"
37 
38 typedef struct {
39 	WBCGtk *wbcg;
40 	Sheet *sheet;
41 	GtkWidget *dialog;
42 	GtkWidget *columns_scale, *rows_scale;
43 	GtkWidget *columns_label, *rows_label;
44 	GtkWidget *ok_button, *cancel_button;
45 	GtkWidget *all_sheets_button;
46 } ResizeState;
47 
48 static void
get_sizes(ResizeState * state,int * cols,int * rows)49 get_sizes (ResizeState *state, int *cols, int *rows)
50 {
51 	GtkAdjustment *adj;
52 
53 	adj = gtk_range_get_adjustment (GTK_RANGE (state->columns_scale));
54 	*cols = 1 << (int)gtk_adjustment_get_value (adj);
55 
56 	adj = gtk_range_get_adjustment (GTK_RANGE (state->rows_scale));
57 	*rows = 1 << (int)gtk_adjustment_get_value (adj);
58 }
59 
60 static void
set_count(GtkWidget * l,int count)61 set_count (GtkWidget *l, int count)
62 {
63 	char *text;
64 
65 	if (count >= (1 << 20))
66 		text = g_strdup_printf ("%dM", count >> 20);
67 	else
68 		text = g_strdup_printf ("%d", count);
69 	gtk_label_set_text (GTK_LABEL (l), text);
70 	g_free (text);
71 }
72 
73 static void
cb_scale_changed(ResizeState * state)74 cb_scale_changed (ResizeState *state)
75 {
76 	int cols, rows;
77 	get_sizes (state, &cols, &rows);
78 	set_count (state->columns_label, cols);
79 	set_count (state->rows_label, rows);
80 	gtk_widget_set_sensitive (state->ok_button,
81 				  gnm_sheet_valid_size (cols, rows));
82 }
83 
84 static int
mylog2(int N)85 mylog2 (int N)
86 {
87 	int l2 = 0;
88 	while (N > 1)
89 		N >>= 1, l2++;
90 	return l2;
91 }
92 
93 static void
init_scale(GtkWidget * scale,int N,int lo,int hi)94 init_scale (GtkWidget *scale, int N, int lo, int hi)
95 {
96 	GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (scale));
97 	g_object_set (G_OBJECT (adj),
98 		      "lower", (double)mylog2 (lo),
99 		      "upper", (double)mylog2 (hi) + 1.,
100 		      NULL);
101 	gtk_adjustment_set_value (adj, mylog2 (N));
102 }
103 
104 static void
cb_ok_clicked(ResizeState * state)105 cb_ok_clicked (ResizeState *state)
106 {
107 	GSList *changed_sheets = NULL;
108 	WorkbookControl *wbc;
109 	Workbook *wb;
110 	gboolean all_sheets;
111 	int cols, rows;
112 
113 	get_sizes (state, &cols, &rows);
114 	all_sheets = gtk_toggle_button_get_active
115 		(GTK_TOGGLE_BUTTON (state->all_sheets_button));
116 
117 	wbc = GNM_WBC (state->wbcg);
118 	wb = wb_control_get_workbook (wbc);
119 
120 	if (all_sheets) {
121 		GPtrArray *sheets = workbook_sheets (wb);
122 		unsigned ui;
123 
124 		for (ui = 0; ui < sheets->len; ui++) {
125 			Sheet *this_sheet = g_ptr_array_index (sheets, ui);
126 
127 			if (this_sheet == state->sheet)
128 				continue;
129 
130 			if (cols == gnm_sheet_get_max_cols (this_sheet) &&
131 			    rows == gnm_sheet_get_max_rows (this_sheet))
132 				continue;
133 
134 			changed_sheets = g_slist_prepend (changed_sheets, this_sheet);
135 		}
136 		g_ptr_array_unref (sheets);
137 	}
138 
139 	if (changed_sheets ||
140 	    cols != gnm_sheet_get_max_cols (state->sheet) ||
141 	    rows != gnm_sheet_get_max_rows (state->sheet)) {
142 		/* We also append the sheet if it isn't changed in size */
143 		/* to ensure that the focus stays on the current sheet. */
144 		changed_sheets = g_slist_prepend (changed_sheets, state->sheet);
145 	}
146 
147 
148 
149 	if (changed_sheets)
150 		cmd_resize_sheets (wbc, changed_sheets,
151 				   cols, rows);
152 
153 	gtk_widget_destroy (state->dialog);
154 }
155 
156 void
dialog_sheet_resize(WBCGtk * wbcg)157 dialog_sheet_resize (WBCGtk *wbcg)
158 {
159 	GtkBuilder *gui;
160 	ResizeState *state;
161 	int slider_width;
162 
163 	if (gnm_dialog_raise_if_exists (wbcg, RESIZE_DIALOG_KEY))
164 		return;
165 	gui = gnm_gtk_builder_load ("res:ui/sheet-resize.ui", NULL, GO_CMD_CONTEXT (wbcg));
166 	if (gui == NULL)
167 		return;
168 
169 	state = g_new (ResizeState, 1);
170 	state->wbcg   = wbcg;
171 	state->dialog = go_gtk_builder_get_widget (gui, "Resize");
172 	state->sheet = wbcg_cur_sheet (wbcg);
173 	g_return_if_fail (state->dialog != NULL);
174 
175 	slider_width = mylog2 (MAX (GNM_MAX_ROWS / GNM_MIN_ROWS,
176 				    GNM_MAX_COLS / GNM_MIN_COLS)) *
177 		gnm_widget_measure_string (GTK_WIDGET (wbcg_toplevel (wbcg)),
178 					   "00");
179 
180 	state->columns_scale = go_gtk_builder_get_widget (gui, "columns_scale");
181 	gtk_widget_set_size_request (state->columns_scale, slider_width, -1);
182 	state->columns_label = go_gtk_builder_get_widget (gui, "columns_label");
183 	state->rows_scale = go_gtk_builder_get_widget (gui, "rows_scale");
184 	gtk_widget_set_size_request (state->rows_scale, slider_width, -1);
185 	state->rows_label = go_gtk_builder_get_widget (gui, "rows_label");
186 	state->all_sheets_button = go_gtk_builder_get_widget (gui, "all_sheets_button");
187 	state->ok_button = go_gtk_builder_get_widget (gui, "ok_button");
188 	state->cancel_button = go_gtk_builder_get_widget (gui, "cancel_button");
189 
190 	g_signal_connect_swapped (G_OBJECT (state->columns_scale),
191 				  "value-changed", G_CALLBACK (cb_scale_changed),
192 				  state);
193 	init_scale (state->columns_scale,
194 		    gnm_sheet_get_max_cols (state->sheet),
195 		    GNM_MIN_COLS, GNM_MAX_COLS);
196 
197 	g_signal_connect_swapped (G_OBJECT (state->rows_scale),
198 				  "value-changed", G_CALLBACK (cb_scale_changed),
199 				  state);
200 	init_scale (state->rows_scale,
201 		    gnm_sheet_get_max_rows (state->sheet),
202 		    GNM_MIN_ROWS, GNM_MAX_ROWS);
203 
204 	cb_scale_changed (state);
205 
206 	g_signal_connect_swapped (G_OBJECT (state->cancel_button),
207 				  "clicked", G_CALLBACK (gtk_widget_destroy),
208 				  state->dialog);
209 
210 	g_signal_connect_swapped (G_OBJECT (state->ok_button),
211 				  "clicked", G_CALLBACK (cb_ok_clicked),
212 				  state);
213 
214 	gnm_dialog_setup_destroy_handlers (GTK_DIALOG (state->dialog), wbcg,
215 					   GNM_DIALOG_DESTROY_SHEET_REMOVED);
216 
217 	gnm_keyed_dialog (wbcg, GTK_WINDOW (state->dialog),
218 			       RESIZE_DIALOG_KEY);
219 
220 	g_object_set_data_full (G_OBJECT (state->dialog),
221 	                        "state", state,
222 	                        (GDestroyNotify) g_free);
223 	g_object_unref (gui);
224 
225 	gtk_widget_show (state->dialog);
226 }
227