1 /*
2  * dialog-sheet-rename.c: Dialog to rename current sheet.
3  *
4  * Author:
5  *	Morten Welinder <terra@gnome.org>
6  *
7  * (C) Copyright 2013 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 RENAME_DIALOG_KEY "sheet-rename-dialog"
37 
38 typedef struct {
39 	WBCGtk *wbcg;
40 	Sheet *sheet;
41 	GtkWidget *dialog;
42 	GtkWidget *old_name, *new_name;
43 	GtkWidget *ok_button, *cancel_button;
44 	gint signal_connect_id_cb_dialog_size_allocate;
45 } RenameState;
46 
47 static void
cb_name_changed(GtkEntry * e,RenameState * state)48 cb_name_changed (GtkEntry *e, RenameState *state)
49 {
50 	const gchar *name = gtk_entry_get_text (e);
51 	Sheet *sheet2 = workbook_sheet_by_name (state->sheet->workbook, name);
52 	gboolean valid;
53 
54 	valid = (*name != 0) && (sheet2 == NULL || sheet2 == state->sheet);
55 
56 	gtk_widget_set_sensitive (state->ok_button, valid);
57 }
58 
59 static void
cb_ok_clicked(RenameState * state)60 cb_ok_clicked (RenameState *state)
61 {
62 	const gchar *name = gtk_entry_get_text (GTK_ENTRY (state->new_name));
63 
64 	if (! cmd_rename_sheet (GNM_WBC (state->wbcg),
65 			  state->sheet,
66 			  name))
67 		gtk_widget_destroy (state->dialog);
68 }
69 
70 static void
gtk_entry_set_size_all_text_visible(GtkEntry * entry)71 gtk_entry_set_size_all_text_visible (GtkEntry *entry)
72 {
73 	PangoContext *context;
74 	PangoFontMetrics *metrics;
75 	gint char_width;
76 	gint digit_width;
77 	gint char_pixels;
78 	PangoLayout *pango_layout;
79 	gint char_count;
80 	gint min_width;
81 	gint actual_width;
82 
83 	/* Logic borrowed from GtkEntry::gtk_entry_measure() */
84 	context = gtk_widget_get_pango_context (GTK_WIDGET (entry));
85 	metrics = pango_context_get_metrics (context,
86 					     pango_context_get_font_description (context),
87 					     pango_context_get_language (context));
88 
89 	char_width = pango_font_metrics_get_approximate_char_width (metrics);
90 	digit_width = pango_font_metrics_get_approximate_digit_width (metrics);
91 	char_pixels = (MAX (char_width, digit_width) + PANGO_SCALE - 1) / PANGO_SCALE;
92 
93 	pango_layout = gtk_entry_get_layout (entry);
94 	char_count = pango_layout_get_character_count (pango_layout);
95 	min_width = char_pixels * char_count;
96 	actual_width = gtk_widget_get_allocated_width (GTK_WIDGET (entry));
97 
98 	if (actual_width < min_width)
99 		gtk_entry_set_width_chars (entry, char_count);
100 }
101 
102 static void
cb_dialog_size_allocate(GtkWidget * dialog,GdkRectangle * allocation,RenameState * state)103 cb_dialog_size_allocate (GtkWidget *dialog, GdkRectangle *allocation, RenameState *state)
104 {
105 	GdkGeometry hints;
106 
107 	g_signal_handler_disconnect (G_OBJECT (dialog),
108 				     state->signal_connect_id_cb_dialog_size_allocate);
109 
110 	/* dummy values for min/max_width to not restrict horizontal resizing */
111 	hints.min_width = 0;
112 	hints.max_width = G_MAXINT;
113 	/* do not allow vertial resizing */
114 	hints.min_height = allocation->height;
115 	hints.max_height = allocation->height;
116 	gtk_window_set_geometry_hints (GTK_WINDOW (dialog), (GtkWidget *) NULL,
117 				       &hints,
118 				       (GdkWindowHints) (GDK_HINT_MIN_SIZE | GDK_HINT_MAX_SIZE));
119 
120 	gtk_entry_set_size_all_text_visible (GTK_ENTRY (state->new_name));
121 }
122 
123 void
dialog_sheet_rename(WBCGtk * wbcg,Sheet * sheet)124 dialog_sheet_rename (WBCGtk *wbcg, Sheet *sheet)
125 {
126 	GtkBuilder *gui;
127 	RenameState *state;
128 
129 	if (gnm_dialog_raise_if_exists (wbcg, RENAME_DIALOG_KEY))
130 		return;
131 	gui = gnm_gtk_builder_load ("res:ui/sheet-rename.ui", NULL, GO_CMD_CONTEXT (wbcg));
132 	if (gui == NULL)
133 		return;
134 
135 	state = g_new (RenameState, 1);
136 	state->wbcg = wbcg;
137 	state->dialog = go_gtk_builder_get_widget (gui, "Rename");
138 	state->sheet = sheet;
139 	g_return_if_fail (state->dialog != NULL);
140 
141 	state->signal_connect_id_cb_dialog_size_allocate =
142 		g_signal_connect (G_OBJECT (state->dialog),
143 				  "size-allocate",
144 				  G_CALLBACK (cb_dialog_size_allocate),
145 				  state);
146 
147 	state->old_name = go_gtk_builder_get_widget (gui, "old_name");
148 	gtk_entry_set_text (GTK_ENTRY (state->old_name), sheet->name_unquoted);
149 
150 	state->new_name = go_gtk_builder_get_widget (gui, "new_name");
151 	gtk_entry_set_text (GTK_ENTRY (state->new_name), sheet->name_unquoted);
152 
153 	gtk_editable_select_region (GTK_EDITABLE (state->new_name), 0, -1);
154 	gtk_widget_grab_focus (state->new_name);
155 	g_signal_connect (G_OBJECT (state->new_name),
156 			  "changed", G_CALLBACK (cb_name_changed),
157 			  state);
158 	gnm_editable_enters (GTK_WINDOW (state->dialog), state->new_name);
159 
160 	state->ok_button = go_gtk_builder_get_widget (gui, "ok_button");
161 	g_signal_connect_swapped (G_OBJECT (state->ok_button),
162 				  "clicked", G_CALLBACK (cb_ok_clicked),
163 				  state);
164 
165 	state->cancel_button = go_gtk_builder_get_widget (gui, "cancel_button");
166 	g_signal_connect_swapped (G_OBJECT (state->cancel_button),
167 				  "clicked", G_CALLBACK (gtk_widget_destroy),
168 				  state->dialog);
169 
170 	gnm_dialog_setup_destroy_handlers (GTK_DIALOG (state->dialog), wbcg,
171 					   GNM_DIALOG_DESTROY_SHEET_REMOVED);
172 
173 	gnm_keyed_dialog (wbcg, GTK_WINDOW (state->dialog),
174 			       RENAME_DIALOG_KEY);
175 
176 	g_object_set_data_full (G_OBJECT (state->dialog),
177 				"state", state,
178 				(GDestroyNotify) g_free);
179 	g_object_unref (gui);
180 
181 	gtk_widget_show (state->dialog);
182 }
183