1 /*
2  * This file is part of Siril, an astronomy image processor.
3  * Copyright (C) 2005-2011 Francois Meyer (dulle at free.fr)
4  * Copyright (C) 2012-2021 team free-astro (see more in AUTHORS file)
5  * Reference site is https://free-astro.org/index.php/Siril
6  *
7  * Siril is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * Siril is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with Siril. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 /*
22  * GTK File Chooser static functions
23  */
24 
25 #include <stdio.h>
26 #include <string.h>
27 
28 #include "core/siril.h"
29 #include "core/proto.h"
30 #include "core/OS_utils.h"
31 #include "core/initfile.h"
32 #include "algos/sorting.h"
33 #include "io/conversion.h"
34 #include "io/films.h"
35 #include "io/sequence.h"
36 #include "io/single_image.h"
37 #include "io/fits_sequence.h"
38 #include "gui/utils.h"
39 #include "gui/callbacks.h"
40 #include "gui/progress_and_log.h"
41 #include "gui/message_dialog.h"
42 #include "gui/dialog_preview.h"
43 #include "gui/conversion.h"
44 
45 #include "open_dialog.h"
46 
gtk_filter_add(GtkFileChooser * file_chooser,const gchar * title,const gchar * pattern,gboolean set_default)47 static void gtk_filter_add(GtkFileChooser *file_chooser, const gchar *title,
48 		const gchar *pattern, gboolean set_default) {
49 	gchar **patterns;
50 	gint i;
51 
52 	GtkFileFilter *f = gtk_file_filter_new();
53 	gtk_file_filter_set_name(f, title);
54 	/* get the patterns */
55 	patterns = g_strsplit(pattern, ";", -1);
56 	for (i = 0; patterns[i] != NULL; i++)
57 		gtk_file_filter_add_pattern(f, patterns[i]);
58 	/* free the patterns */
59 	g_strfreev(patterns);
60 	gtk_file_chooser_add_filter(file_chooser, f);
61 	if (set_default)
62 		gtk_file_chooser_set_filter(file_chooser, f);
63 }
64 
set_filters_dialog(GtkFileChooser * chooser,int whichdial)65 static void set_filters_dialog(GtkFileChooser *chooser, int whichdial) {
66 	GString *all_filter = NULL;
67 	gchar *fits_filter = "*.fit;*.FIT;*.fits;*.FITS;*.fts;*.FTS;*.fits.fz";
68 	gchar *netpbm_filter = "*.ppm;*.PPM;*.pnm;*.PNM;*.pgm;*.PGM";
69 	gchar *pic_filter = "*.pic;*.PIC";
70 	gchar *ser_filter = "*.ser;*.SER";
71 	if (whichdial != OD_CONVERT && whichdial != OD_OPEN) {
72 		gtk_filter_add(chooser, _("FITS Files (*.fit, *.fits, *.fts, *.fits.fz)"),
73 				fits_filter, com.filter == TYPEFITS);
74 	} else {
75 		all_filter = g_string_new(fits_filter);
76 	}
77 	if (whichdial == OD_OPEN || whichdial == OD_CONVERT) {
78 #ifdef HAVE_LIBRAW
79 		/* RAW FILES */
80 		int nb_raw;
81 		char *raw;
82 		int i;
83 
84 		nb_raw = get_nb_raw_supported();
85 		raw = calloc(sizeof(char), nb_raw * 12 + 1);// we assume the extension size of 3 char "*.xxx;*.XXX;" = 12
86 		for (i = 0; i < nb_raw; i++) {
87 			char *ext;
88 			gchar *upcase;
89 
90 			upcase = g_ascii_strup(supported_raw[i].extension, strlen(supported_raw[i].extension));
91 			ext = g_strdup_printf("*.%s;*.%s;", supported_raw[i].extension,
92 					upcase);
93 			strcat(raw, ext);
94 
95 			g_free(ext);
96 			g_free(upcase);
97 		}
98 		raw[strlen(raw) - 1] = '\0';
99 		if (whichdial != OD_CONVERT && whichdial != OD_OPEN) {
100 			gtk_filter_add(chooser, _("RAW DSLR Camera Files"), raw,
101 				com.filter == TYPERAW);
102 		} else {
103 			all_filter = g_string_append(all_filter, ";");
104 			all_filter = g_string_append(all_filter, raw);
105 		}
106 
107 		free(raw);
108 
109 #endif
110 		/* GRAPHICS FILES */
111 		GString *s_supported_graph, *s_pattern;
112 		gchar *graphics_supported, *graphics_filter;
113 
114 		s_supported_graph = g_string_new(_("Graphics Files (*.bmp"));
115 		s_pattern = g_string_new("*.bmp;*.BMP;");
116 #ifdef HAVE_LIBJPEG
117 		s_supported_graph = g_string_append(s_supported_graph, ", *.jpg, *.jpeg");
118 		s_pattern = g_string_append(s_pattern, "*.jpg;*.JPG;*.jpeg;*.JPEG;");
119 #endif
120 
121 #ifdef HAVE_LIBHEIF
122 		s_supported_graph = g_string_append(s_supported_graph, ", *.heic, *.heif");
123 		s_pattern = g_string_append(s_pattern, "*.heic;*.HEIC;*.heif;*.HEIF;");
124 #endif
125 
126 #ifdef HAVE_LIBPNG
127 		s_supported_graph = g_string_append(s_supported_graph, ", *.png");
128 		s_pattern = g_string_append(s_pattern, "*.png;*.PNG;");
129 #endif
130 
131 #ifdef HAVE_LIBTIFF
132 		s_supported_graph = g_string_append(s_supported_graph, ", *.tif, *.tiff");
133 		s_pattern = g_string_append(s_pattern, "*.tif;*.TIF;*.tiff;*.TIFF");
134 #endif
135 		s_supported_graph = g_string_append(s_supported_graph, ")");
136 
137 		graphics_supported = g_string_free(s_supported_graph, FALSE);
138 		graphics_filter = g_string_free(s_pattern, FALSE);
139 		if (whichdial != OD_CONVERT && whichdial != OD_OPEN) {
140 			gtk_filter_add(chooser, graphics_supported, graphics_filter,
141 					com.filter == TYPEBMP || com.filter == TYPEJPG
142 							|| com.filter == TYPEPNG || com.filter == TYPETIFF);
143 
144 			/* NETPBM FILES */
145 			gtk_filter_add(chooser, _("Netpbm Files (*.ppm, *.pnm, *.pgm)"),
146 					netpbm_filter, com.filter == TYPEPNM);
147 			/* IRIS FILES */
148 			gtk_filter_add(chooser, _("IRIS PIC Files (*.pic)"), pic_filter,
149 					com.filter == TYPEPIC);
150 			/* SER FILES */
151 			gtk_filter_add(chooser, _("SER files (*.ser)"), ser_filter,
152 					com.filter == TYPESER);
153 		} else {
154 			all_filter = g_string_append(all_filter, ";");
155 			all_filter = g_string_append(all_filter, graphics_filter);
156 			all_filter = g_string_append(all_filter, ";");
157 			all_filter = g_string_append(all_filter, netpbm_filter);
158 			all_filter = g_string_append(all_filter, ";");
159 			all_filter = g_string_append(all_filter, pic_filter);
160 			all_filter = g_string_append(all_filter, ";");
161 			all_filter = g_string_append(all_filter, ser_filter);
162 		}
163 
164 #ifdef HAVE_FFMS2
165 		/* FILM FILES */
166 		int nb_film;
167 		char *film_filter;
168 		int j;
169 
170 		nb_film = get_nb_film_ext_supported();
171 		film_filter = calloc(sizeof(char), nb_film * 14 + 1);// we assume the extension size of 4 char "*.xxxx;*.XXXX;" = 14
172 		for (j = 0; j < nb_film; j++) {
173 			char *ext;
174 			gchar *upcase;
175 
176 			upcase = g_ascii_strup(supported_film[j].extension,
177 					strlen(supported_film[j].extension));
178 			ext = g_strdup_printf("*.%s;*.%s;", supported_film[j].extension,
179 					upcase);
180 			strcat(film_filter, ext);
181 
182 			g_free(ext);
183 			g_free(upcase);
184 		}
185 		film_filter[strlen(film_filter) - 1] = '\0';
186 
187 		if (whichdial != OD_CONVERT && whichdial != OD_OPEN) {
188 		gtk_filter_add(chooser, _("Film Files (*.avi, *.mpg, ...)"), film_filter,
189 				com.filter == TYPEAVI);
190 		} else {
191 			all_filter = g_string_append(all_filter, ";");
192 			all_filter = g_string_append(all_filter, film_filter);
193 		}
194 		free(film_filter);
195 #endif
196 		g_free(graphics_supported);
197 		g_free(graphics_filter);
198 
199 		if (whichdial == OD_CONVERT || whichdial == OD_OPEN) {
200 			gchar *filter = g_string_free(all_filter, FALSE);
201 
202 			gtk_filter_add(chooser, _("All supported files"), filter, TRUE);
203 			g_free(filter);
204 		}
205 	}
206 }
207 
on_debayer_toggled(GtkToggleButton * togglebutton,gpointer user_data)208 static void on_debayer_toggled(GtkToggleButton *togglebutton, gpointer user_data) {
209 	gtk_toggle_button_set_active((GtkToggleButton *)user_data, gtk_toggle_button_get_active(togglebutton));
210 }
211 
siril_add_debayer_toggle_button(GtkFileChooser * dialog)212 static void siril_add_debayer_toggle_button(GtkFileChooser *dialog) {
213 	GtkToggleButton *main_debayer_button = GTK_TOGGLE_BUTTON(lookup_widget("demosaicingButton"));
214 	GtkWidget *toggle_debayer = gtk_check_button_new_with_label(_("Debayer"));
215 
216 	gtk_widget_show(toggle_debayer);
217 	gtk_file_chooser_set_extra_widget(dialog, toggle_debayer);
218 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggle_debayer), gtk_toggle_button_get_active(main_debayer_button));
219 	g_signal_connect(GTK_TOGGLE_BUTTON(toggle_debayer), "toggled", G_CALLBACK(on_debayer_toggled), (gpointer) main_debayer_button);
220 }
221 
opendial(int whichdial)222 static void opendial(int whichdial) {
223 	SirilWidget *widgetdialog;
224 	GtkFileChooser *dialog = NULL;
225 	fileChooserPreview *preview = NULL;
226 	GtkWindow *control_window = GTK_WINDOW(GTK_APPLICATION_WINDOW(lookup_widget("control_window")));
227 	gint res;
228 	int retval;
229 
230 	if (!com.wd)
231 		return;
232 
233 	switch (whichdial) {
234 	case OD_NULL:
235 		fprintf(stderr, "whichdial undefined, should not happen\n");
236 		return;
237 	case OD_FLAT:
238 	case OD_DARK:
239 	case OD_OFFSET:
240 		widgetdialog = siril_file_chooser_open(control_window, GTK_FILE_CHOOSER_ACTION_OPEN);
241 		dialog = GTK_FILE_CHOOSER(widgetdialog);
242 		gtk_file_chooser_set_current_folder(dialog, com.wd);
243 		gtk_file_chooser_set_select_multiple(dialog, FALSE);
244 		set_filters_dialog(dialog, whichdial);
245 		siril_file_chooser_add_preview(dialog, preview);
246 		break;
247 	case OD_CWD:
248 		widgetdialog = siril_file_chooser_open(control_window, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
249 		dialog = GTK_FILE_CHOOSER(widgetdialog);
250 		gtk_file_chooser_set_current_folder(dialog, com.wd);
251 		gtk_file_chooser_set_select_multiple(dialog, FALSE);
252 		break;
253 	case OD_OPEN:
254 		widgetdialog = siril_file_chooser_open(control_window, GTK_FILE_CHOOSER_ACTION_OPEN);
255 		dialog = GTK_FILE_CHOOSER(widgetdialog);
256 		gtk_file_chooser_set_current_folder(dialog, com.wd);
257 		gtk_file_chooser_set_select_multiple(dialog, FALSE);
258 		set_filters_dialog(dialog, whichdial);
259 		siril_file_chooser_add_preview(dialog, preview);
260 		siril_add_debayer_toggle_button(dialog);
261 		break;
262 	case OD_CONVERT:
263 		widgetdialog = siril_file_chooser_add(control_window, GTK_FILE_CHOOSER_ACTION_OPEN);
264 		dialog = GTK_FILE_CHOOSER(widgetdialog);
265 		gtk_file_chooser_set_current_folder(dialog, com.wd);
266 		gtk_file_chooser_set_select_multiple(dialog, TRUE);
267 		set_filters_dialog(dialog, whichdial);
268 	}
269 
270 	if (!dialog)
271 		return;
272 
273 	wait:
274 	res = siril_dialog_run(widgetdialog);
275 
276 	if (res == GTK_RESPONSE_ACCEPT) {
277 		GSList *list = NULL;
278 		gchar *filename, *err;
279 		gboolean anything_loaded;
280 		GtkFileChooser *chooser = GTK_FILE_CHOOSER(dialog);
281 		GtkEntry *flat_entry, *dark_entry, *bias_entry;
282 		GtkToggleButton *flat_button, *dark_button, *bias_button;
283 		GtkWidget *pbutton;
284 
285 		filename = gtk_file_chooser_get_filename(chooser);
286 		if (!filename)
287 			goto wait;
288 
289 		if (fitseq_is_fitseq(filename, NULL)
290 				&& ((whichdial == OD_FLAT) || (whichdial == OD_DARK) || (whichdial == OD_OFFSET))) {
291 			siril_message_dialog(GTK_MESSAGE_ERROR, _("Format not supported."),
292 					_("FITS sequences are not supported for master files. Please select a single FITS file."));
293 			goto wait;
294 		}
295 
296 		pbutton  = lookup_widget("prepro_button");
297 		flat_entry = GTK_ENTRY(lookup_widget("flatname_entry"));
298 		dark_entry = GTK_ENTRY(lookup_widget("darkname_entry"));
299 		bias_entry = GTK_ENTRY(lookup_widget("offsetname_entry"));
300 
301 		flat_button = GTK_TOGGLE_BUTTON(lookup_widget("useflat_button"));
302 		dark_button = GTK_TOGGLE_BUTTON(lookup_widget("usedark_button"));
303 		bias_button = GTK_TOGGLE_BUTTON(lookup_widget("useoffset_button"));
304 
305 		anything_loaded = sequence_is_loaded() || single_image_is_loaded();
306 
307 		switch (whichdial) {
308 		case OD_FLAT:
309 			gtk_entry_set_text(flat_entry, filename);
310 			gtk_toggle_button_set_active(flat_button, TRUE);
311 			gtk_widget_set_sensitive(pbutton, anything_loaded);
312 			break;
313 
314 		case OD_DARK:
315 			gtk_entry_set_text(dark_entry, filename);
316 			gtk_toggle_button_set_active(dark_button, TRUE);
317 			gtk_widget_set_sensitive(pbutton, anything_loaded);
318 			break;
319 
320 		case OD_OFFSET:
321 			gtk_entry_set_text(bias_entry, filename);
322 			gtk_toggle_button_set_active(bias_button, TRUE);
323 			gtk_widget_set_sensitive(pbutton, anything_loaded);
324 			break;
325 
326 		case OD_CWD:
327 			if (!siril_change_dir(filename, &err)) {
328 				writeinitfile();
329 				set_GUI_CWD();
330 			} else {
331 				siril_message_dialog(GTK_MESSAGE_ERROR, _("Error"), err);
332 			}
333 			break;
334 
335 		case OD_OPEN:
336 			set_cursor_waiting(TRUE);
337 			retval = open_single_image(filename);
338 			set_cursor_waiting(FALSE);
339 			if (retval == OPEN_IMAGE_CANCEL) goto wait;
340 			break;
341 
342 		case OD_CONVERT:
343 			list = gtk_file_chooser_get_filenames(chooser);
344 			list = g_slist_sort(list, (GCompareFunc) strcompare);
345 			fill_convert_list(list);
346 			g_slist_free(list);
347 			break;
348 		}
349 		g_free(filename);
350 	}
351 	siril_preview_free(preview);
352 	siril_widget_destroy(widgetdialog);
353 }
354 
on_darkfile_button_clicked(GtkButton * button,gpointer user_data)355 void on_darkfile_button_clicked(GtkButton *button, gpointer user_data) {
356 	opendial(OD_DARK);
357 }
358 
cwd_btton_clicked()359 void cwd_btton_clicked() {
360 	opendial(OD_CWD);
361 }
362 
on_offsetfile_button_clicked(GtkButton * button,gpointer user_data)363 void on_offsetfile_button_clicked(GtkButton *button, gpointer user_data) {
364 	opendial(OD_OFFSET);
365 }
366 
on_flatfile_button_clicked(GtkButton * button,gpointer user_data)367 void on_flatfile_button_clicked(GtkButton *button, gpointer user_data) {
368 	opendial(OD_FLAT);
369 }
370 
header_open_button_clicked()371 void header_open_button_clicked() {
372 	opendial(OD_OPEN);
373 }
374 
on_select_convert_button_clicked(GtkToolButton * button,gpointer user_data)375 void on_select_convert_button_clicked(GtkToolButton *button, gpointer user_data) {
376 	opendial(OD_CONVERT);
377 }
378 
379 /** callback function for recent document opened
380  *
381  * @param chooser
382  * @param user_data
383  */
on_open_recent_action_item_activated(GtkRecentChooser * chooser,gpointer user_data)384 void on_open_recent_action_item_activated(GtkRecentChooser *chooser,
385 		gpointer user_data) {
386 	gchar *uri, *path;
387 	GError *error = NULL;
388 
389 	uri = gtk_recent_chooser_get_current_uri(chooser);
390 
391 	path = g_filename_from_uri(uri, NULL, &error);
392 	if (error) {
393 		g_warning("Could not convert uri \"%s\" to a local path: %s", uri,
394 				error->message);
395 		g_clear_error(&error);
396 		return;
397 	}
398 
399 	open_single_image(path);
400 
401 	g_free(uri);
402 	g_free(path);
403 }
404