1 /*
2  * TilEm II
3  *
4  * Copyright (c) 2011 Benjamin Moody
5  *
6  * This program is free software: you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 # include <config.h>
22 #endif
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <gtk/gtk.h>
28 #include <ticalcs.h>
29 #include <tilem.h>
30 
31 #include "gui.h"
32 #include "files.h"
33 #include "filedlg.h"
34 
35 /* Convert to an absolute path */
canonicalize_filename(const char * name)36 static char *canonicalize_filename(const char *name)
37 {
38 #ifdef G_OS_WIN32
39 	static const char delim[] = "/\\";
40 #else
41 	static const char delim[] = G_DIR_SEPARATOR_S;
42 #endif
43 	char *result, **parts, *p;
44 	int i;
45 
46 	if (name == NULL || g_path_is_absolute(name))
47 		return g_strdup(name);
48 
49 	result = g_get_current_dir();
50 	parts = g_strsplit_set(name, delim, -1);
51 	for (i = 0; parts[i]; i++) {
52 		if (!strcmp(parts[i], "..")) {
53 			p = g_path_get_dirname(result);
54 			g_free(result);
55 			result = p;
56 		}
57 		else if (strcmp(parts[i], ".")
58 		         && strcmp(parts[i], "")) {
59 			p = g_build_filename(result, parts[i], NULL);
60 			g_free(result);
61 			result = p;
62 		}
63 	}
64 	g_strfreev(parts);
65 	return result;
66 }
67 
68 /* check if two file names are equivalent (of course, if this fails,
69    it doesn't necessarily mean the files are distinct) */
file_names_equal(const char * a,const char * b)70 static gboolean file_names_equal(const char *a, const char *b)
71 {
72 	char *ca, *cb;
73 	gboolean status;
74 
75 	if (a == NULL && b == NULL)
76 		return TRUE;
77 	else if (a == NULL || b == NULL)
78 		return FALSE;
79 
80 	ca = canonicalize_filename(a);
81 	cb = canonicalize_filename(b);
82 	status = !strcmp(ca, cb);
83 	g_free(ca);
84 	g_free(cb);
85 	return status;
86 }
87 
save_skin_name(TilemEmulatorWindow * ewin)88 static void save_skin_name(TilemEmulatorWindow *ewin)
89 {
90 	const char *model = ewin->emu->calc->hw.name;
91 	char *base, *shared;
92 
93 	/* don't save pref unless skin was actually loaded */
94 	if (!ewin->skin_file_name || !ewin->skin)
95 		return;
96 
97 	/* if file is stored in shared skins directory, save
98 	   only the relative path; otherwise, save the
99 	   absolute path */
100 	base = g_path_get_basename(ewin->skin_file_name);
101 	shared = get_shared_file_path("skins", base, NULL);
102 
103 	if (file_names_equal(shared, ewin->skin_file_name))
104 		tilem_config_set(model,
105 		                 "skin/f", base,
106 		                 NULL);
107 	else
108 		tilem_config_set(model,
109 		                 "skin/f", ewin->skin_file_name,
110 		                 NULL);
111 
112 	g_free(base);
113 	g_free(shared);
114 }
115 
speed_changed(GtkToggleButton * btn,gpointer data)116 static void speed_changed(GtkToggleButton *btn, gpointer data)
117 {
118 	TilemEmulatorWindow *ewin = data;
119 	gboolean setting = gtk_toggle_button_get_active(btn);
120 	tilem_calc_emulator_set_limit_speed(ewin->emu, setting);
121 	tilem_config_set("emulation",
122 	                 "limit_speed/b", setting,
123 	                 NULL);
124 }
125 
grayscale_changed(GtkToggleButton * btn,gpointer data)126 static void grayscale_changed(GtkToggleButton *btn, gpointer data)
127 {
128 	TilemEmulatorWindow *ewin = data;
129 	gboolean setting = gtk_toggle_button_get_active(btn);
130 	tilem_calc_emulator_set_grayscale(ewin->emu, setting);
131 	tilem_config_set("emulation",
132 	                 "grayscale/b", setting,
133 	                 NULL);
134 }
135 
smooth_changed(GtkToggleButton * btn,gpointer data)136 static void smooth_changed(GtkToggleButton *btn, gpointer data)
137 {
138 	TilemEmulatorWindow *ewin = data;
139 	gboolean setting = gtk_toggle_button_get_active(btn);
140 	ewin->lcd_smooth_scale = setting;
141 	tilem_config_set("settings",
142 	                 "smooth_scaling/b", setting,
143 	                 NULL);
144 }
145 
146 
skin_enable_changed(GtkToggleButton * btn,gpointer data)147 static void skin_enable_changed(GtkToggleButton *btn, gpointer data)
148 {
149 	TilemEmulatorWindow *ewin = data;
150 	gboolean enable = gtk_toggle_button_get_active(btn);
151 
152 	if (ewin->skin_disabled == !enable)
153 		return;
154 
155 	tilem_emulator_window_set_skin_disabled(ewin, !enable);
156 	tilem_config_set("settings",
157 	                 "skin_disabled/b", !enable,
158 	                 NULL);
159 
160 	save_skin_name(ewin);
161 }
162 
skin_file_changed(GtkWidget * fe,gpointer data)163 static void skin_file_changed(GtkWidget *fe, gpointer data)
164 {
165 	TilemEmulatorWindow *ewin = data;
166 	char *fname = file_entry_get_filename(fe);
167 
168 	if (fname && !file_names_equal(fname, ewin->skin_file_name)) {
169 		tilem_emulator_window_set_skin(ewin, fname);
170 		save_skin_name(ewin);
171 		g_free(fname);
172 	}
173 }
174 
175 /* Run preferences dialog. */
tilem_preferences_dialog(TilemEmulatorWindow * ewin)176 void tilem_preferences_dialog(TilemEmulatorWindow *ewin)
177 {
178 	GtkWidget *dlg, *vbox1, *vbox2, *frame, *slow_rb, *fast_rb,
179 		*grayscale_cb, *smooth_cb, *hbox, *skin_cb, *skin_entry;
180 
181 	g_return_if_fail(ewin != NULL);
182 	g_return_if_fail(ewin->emu != NULL);
183 
184 	dlg = gtk_dialog_new_with_buttons("Preferences",
185 	                                  GTK_WINDOW(ewin->window),
186 	                                  GTK_DIALOG_MODAL,
187 	                                  GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
188 	                                  NULL);
189 
190 	vbox1 = gtk_vbox_new(FALSE, 12);
191 	gtk_container_set_border_width(GTK_CONTAINER(vbox1), 6);
192 
193 	/* Emulation speed */
194 
195 	vbox2 = gtk_vbox_new(FALSE, 6);
196 
197 	slow_rb = gtk_radio_button_new_with_mnemonic
198 		(NULL, "_Limit to actual calculator speed");
199 	gtk_box_pack_start(GTK_BOX(vbox2), slow_rb, FALSE, FALSE, 0);
200 
201 	fast_rb = gtk_radio_button_new_with_mnemonic_from_widget
202 		(GTK_RADIO_BUTTON(slow_rb), "As _fast as possible");
203 	gtk_box_pack_start(GTK_BOX(vbox2), fast_rb, FALSE, FALSE, 0);
204 
205 	if (!ewin->emu->limit_speed)
206 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fast_rb), TRUE);
207 
208 	g_signal_connect(slow_rb, "toggled",
209 	                 G_CALLBACK(speed_changed), ewin);
210 
211 	frame = new_frame("Emulation Speed", vbox2);
212 	gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0);
213 
214 	/* Display settings */
215 
216 	vbox2 = gtk_vbox_new(FALSE, 6);
217 
218 	grayscale_cb = gtk_check_button_new_with_mnemonic("Emulate _grayscale");
219 	gtk_box_pack_start(GTK_BOX(vbox2), grayscale_cb, FALSE, FALSE, 0);
220 
221 	if (ewin->emu->grayscale)
222 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(grayscale_cb), TRUE);
223 
224 	g_signal_connect(grayscale_cb, "toggled",
225 	                 G_CALLBACK(grayscale_changed), ewin);
226 
227 	smooth_cb = gtk_check_button_new_with_mnemonic("Use _smooth scaling");
228 	gtk_box_pack_start(GTK_BOX(vbox2), smooth_cb, FALSE, FALSE, 0);
229 
230 	if (ewin->lcd_smooth_scale)
231 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(smooth_cb), TRUE);
232 
233 	g_signal_connect(smooth_cb, "toggled",
234 	                 G_CALLBACK(smooth_changed), ewin);
235 
236 	hbox = gtk_hbox_new(FALSE, 6);
237 
238 	skin_cb = gtk_check_button_new_with_mnemonic("Use s_kin:");
239 	gtk_box_pack_start(GTK_BOX(hbox), skin_cb, FALSE, FALSE, 0);
240 
241 	skin_entry = file_entry_new("Select Skin",
242 	                            "Skin files", "*.skn",
243 	                            "All files", "*",
244 	                            NULL);
245 	gtk_box_pack_start(GTK_BOX(hbox), skin_entry, TRUE, TRUE, 0);
246 
247 	gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0);
248 
249 	if (!ewin->skin_disabled)
250 		gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(skin_cb), TRUE);
251 
252 	if (ewin->skin_file_name)
253 		file_entry_set_filename(skin_entry, ewin->skin_file_name);
254 
255 	g_signal_connect(skin_cb, "toggled",
256 	                 G_CALLBACK(skin_enable_changed), ewin);
257 
258 	g_signal_connect(skin_entry, "selection-changed",
259 	                 G_CALLBACK(skin_file_changed), ewin);
260 
261 	frame = new_frame("Display", vbox2);
262 	gtk_box_pack_start(GTK_BOX(vbox1), frame, FALSE, FALSE, 0);
263 
264 	vbox2 = gtk_dialog_get_content_area(GTK_DIALOG(dlg));
265 	gtk_box_pack_start(GTK_BOX(vbox2), vbox1, FALSE, FALSE, 0);
266 	gtk_widget_show_all(vbox1);
267 
268 	gtk_dialog_run(GTK_DIALOG(dlg));
269 	gtk_widget_destroy(dlg);
270 }
271