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 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include "core/siril.h"
26 #include "core/proto.h"
27 #include "core/OS_utils.h"
28 #include "io/single_image.h"
29 #include "io/sequence.h"
30 #include "gui/utils.h"
31 #include "gui/callbacks.h"
32 #include "gui/image_display.h"
33 #include "gui/image_interactions.h"
34 #include "gui/dialogs.h"
35 #include "gui/message_dialog.h"
36 #include "gui/PSF_list.h"
37 #include "gui/progress_and_log.h"
38 #include "algos/PSF.h"
39 #include "algos/star_finder.h"
40 
41 static GtkListStore *liststore_stars = NULL;
42 
43 enum {
44 	COLUMN_CHANNEL,		// int
45 	COLUMN_B,			// gdouble
46 	COLUMN_A,			// gdouble
47 	COLUMN_X0,			// gdouble
48 	COLUMN_Y0,			// gdouble
49 	COLUMN_FWHMX,		// gdouble
50 	COLUMN_FWHMY,		// gdouble
51 	COLUMN_MAG,		    // gdouble
52 	COLUMN_ROUNDNESS,	// gdouble
53 	COLUMN_ANGLE,		// gdouble
54 	COLUMN_RMSE,		// gdouble
55 	N_COLUMNS
56 };
57 
58 enum {					//different context_id of the GtkStatusBar
59 	COUNT_STATE
60 };
61 
62 static gchar *units = "";
63 
gdouble_fwhmx_cell_data_function(GtkTreeViewColumn * col,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer user_data)64 static void gdouble_fwhmx_cell_data_function(GtkTreeViewColumn *col,
65 		GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter,
66 		gpointer user_data) {
67 	gdouble var;
68 	gchar *buf;
69 	gtk_tree_model_get(model, iter, COLUMN_FWHMX, &var, -1);
70 	buf = g_strdup_printf("%.2f%s", var, units);
71 	g_object_set(renderer, "text", buf, NULL);
72 
73 	g_free(buf);
74 }
75 
gdouble_fwhmy_cell_data_function(GtkTreeViewColumn * col,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer user_data)76 static void gdouble_fwhmy_cell_data_function(GtkTreeViewColumn *col,
77 		GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter,
78 		gpointer user_data) {
79 	gdouble var;
80 	gchar *buf;
81 	gtk_tree_model_get(model, iter, COLUMN_FWHMY, &var, -1);
82 	buf = g_strdup_printf("%.2f%s", var, units);
83 	g_object_set(renderer, "text", buf, NULL);
84 
85 	g_free(buf);
86 }
87 
gdouble_x0_cell_data_function(GtkTreeViewColumn * col,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer user_data)88 static void gdouble_x0_cell_data_function(GtkTreeViewColumn *col,
89 		GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter,
90 		gpointer user_data) {
91 	gdouble var;
92 	gchar *buf;
93 	gtk_tree_model_get(model, iter, COLUMN_X0, &var, -1);
94 	buf = g_strdup_printf("%.2f", var);
95 	g_object_set(renderer, "text", buf, NULL);
96 
97 	g_free(buf);
98 }
99 
gdouble_y0_cell_data_function(GtkTreeViewColumn * col,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer user_data)100 static void gdouble_y0_cell_data_function(GtkTreeViewColumn *col,
101 		GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter,
102 		gpointer user_data) {
103 	gdouble var;
104 	gchar *buf;
105 	gtk_tree_model_get(model, iter, COLUMN_Y0, &var, -1);
106 	buf = g_strdup_printf("%.2f", var);
107 	g_object_set(renderer, "text", buf, NULL);
108 
109 	g_free(buf);
110 }
111 
gdouble_mag_cell_data_function(GtkTreeViewColumn * col,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer user_data)112 static void gdouble_mag_cell_data_function(GtkTreeViewColumn *col,
113 		GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter,
114 		gpointer user_data) {
115 	gdouble var;
116 	gchar *buf;
117 	gtk_tree_model_get(model, iter, COLUMN_MAG, &var, -1);
118 	buf = g_strdup_printf("%.2f", var);
119 	g_object_set(renderer, "text", buf, NULL);
120 
121 	g_free(buf);
122 }
123 
gdouble_r_cell_data_function(GtkTreeViewColumn * col,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer user_data)124 static void gdouble_r_cell_data_function(GtkTreeViewColumn *col,
125 		GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter,
126 		gpointer user_data) {
127 	gdouble var;
128 	gchar *buf;
129 	gtk_tree_model_get(model, iter, COLUMN_ROUNDNESS, &var, -1);
130 	buf = g_strdup_printf("%.3f", var);
131 	g_object_set(renderer, "text", buf, NULL);
132 
133 	g_free(buf);
134 }
135 
gdouble_angle_cell_data_function(GtkTreeViewColumn * col,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer user_data)136 static void gdouble_angle_cell_data_function(GtkTreeViewColumn *col,
137 		GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter,
138 		gpointer user_data) {
139 	gdouble var;
140 	gchar *buf;
141 	gtk_tree_model_get(model, iter, COLUMN_ANGLE, &var, -1);
142 	if (var == 0.0)
143 		buf = g_strdup_printf("%s", "N/A");
144 	else
145 		buf = g_strdup_printf("%.2f", var);
146 	g_object_set(renderer, "text", buf, NULL);
147 
148 	g_free(buf);
149 }
150 
gdouble_rmse_cell_data_function(GtkTreeViewColumn * col,GtkCellRenderer * renderer,GtkTreeModel * model,GtkTreeIter * iter,gpointer user_data)151 static void gdouble_rmse_cell_data_function(GtkTreeViewColumn *col,
152 		GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter,
153 		gpointer user_data) {
154 	gdouble var;
155 	gchar *buf;
156 	gtk_tree_model_get(model, iter, COLUMN_RMSE, &var, -1);
157 	buf = g_strdup_printf("%.2e", var);
158 	g_object_set(renderer, "text", buf, NULL);
159 
160 	g_free(buf);
161 }
162 
get_stars_list_store()163 static void get_stars_list_store() {
164 	if (liststore_stars == NULL)
165 		liststore_stars = GTK_LIST_STORE(gtk_builder_get_object(builder, "liststore_stars"));
166 
167 	GtkTreeViewColumn *col;
168 	GtkCellRenderer *cell;
169 
170 	col = GTK_TREE_VIEW_COLUMN(gtk_builder_get_object(builder, "treeviewcolumn7"));
171 	cell = GTK_CELL_RENDERER(gtk_builder_get_object(builder, "cell_x0"));
172 	gtk_tree_view_column_set_cell_data_func(col, cell, gdouble_x0_cell_data_function, NULL, NULL);
173 
174 	col = GTK_TREE_VIEW_COLUMN(gtk_builder_get_object(builder, "treeviewcolumn8"));
175 	cell = GTK_CELL_RENDERER(gtk_builder_get_object(builder, "cell_y0"));
176 	gtk_tree_view_column_set_cell_data_func(col, cell, gdouble_y0_cell_data_function, NULL, NULL);
177 
178 	col = GTK_TREE_VIEW_COLUMN(gtk_builder_get_object(builder, "treeviewcolumn9"));
179 	cell = GTK_CELL_RENDERER(gtk_builder_get_object(builder, "cell_fwhmx"));
180 	gtk_tree_view_column_set_cell_data_func(col, cell, gdouble_fwhmx_cell_data_function, NULL, NULL);
181 
182 	col = GTK_TREE_VIEW_COLUMN(gtk_builder_get_object(builder, "treeviewcolumn10"));
183 	cell = GTK_CELL_RENDERER(gtk_builder_get_object(builder, "cell_fwhmy"));
184 	gtk_tree_view_column_set_cell_data_func(col, cell, gdouble_fwhmy_cell_data_function, NULL, NULL);
185 
186 	col = GTK_TREE_VIEW_COLUMN(gtk_builder_get_object(builder, "treeviewcolumn_mag"));
187 	cell = GTK_CELL_RENDERER(gtk_builder_get_object(builder, "cell_mag"));
188 	gtk_tree_view_column_set_cell_data_func(col, cell, gdouble_mag_cell_data_function, NULL, NULL);
189 
190 	col = GTK_TREE_VIEW_COLUMN(gtk_builder_get_object(builder, "treeviewcolumn14"));
191 	cell = GTK_CELL_RENDERER(gtk_builder_get_object(builder, "cell_r"));
192 	gtk_tree_view_column_set_cell_data_func(col, cell, gdouble_r_cell_data_function, NULL, NULL);
193 
194 	col = GTK_TREE_VIEW_COLUMN(gtk_builder_get_object(builder, "treeviewcolumn6"));
195 	cell = GTK_CELL_RENDERER(gtk_builder_get_object(builder, "cell_angle"));
196 	gtk_tree_view_column_set_cell_data_func(col, cell, gdouble_angle_cell_data_function, NULL, NULL);
197 
198 	col = GTK_TREE_VIEW_COLUMN(gtk_builder_get_object(builder, "treeviewcolumn15"));
199 	cell = GTK_CELL_RENDERER(gtk_builder_get_object(builder, "cell_rmse"));
200 	gtk_tree_view_column_set_cell_data_func(col, cell, gdouble_rmse_cell_data_function, NULL, NULL);
201 }
202 
display_PSF(fitted_PSF ** result)203 static void display_PSF(fitted_PSF **result) {
204 	if (result) {
205 		gchar *msg;
206 		int i = 0;
207 		double FWHMx = 0.0, FWHMy = 0.0, B = 0.0, A = 0.0, r = 0.0, angle = 0.0,
208 				rmse = 0.0;
209 		gboolean unit_is_arcsec;
210 
211 		while (result[i]) {
212 			double fwhmx, fwhmy;
213 			char *unit;
214 			gboolean is_as = get_fwhm_as_arcsec_if_possible(result[i], &fwhmx, &fwhmy, &unit);
215 			if (i == 0)
216 				unit_is_arcsec = is_as;
217 			else if (is_as != unit_is_arcsec) {
218 				siril_message_dialog(GTK_MESSAGE_ERROR, _("Error"),
219 						_("Stars FWHM must have the same units."));
220 				return;
221 			}
222 
223 			B += result[i]->B;
224 			A += result[i]->A;
225 			FWHMx += fwhmx;
226 			FWHMy += fwhmy;
227 			angle += result[i]->angle;
228 			rmse += result[i]->rmse;
229 			i++;
230 		}
231 		if (i <= 0) return;
232 		/* compute average */
233 		B = B / (double)i;
234 		A = A / (double)i;
235 		FWHMx = FWHMx / (double)i;
236 		FWHMy = FWHMy / (double)i;
237 		r = FWHMy / FWHMx;
238 		angle = angle / (double)i;
239 		rmse = rmse / (double)i;
240 
241 		msg = g_strdup_printf(_("Average Gaussian PSF\n\n"
242 				"N:\t%d stars\nB:\t%.6f\nA:\t%.6f\nFWHMx:\t%.2f%s\n"
243 				"FWHMy:\t%.2f%s\nr:\t%.3f\nAngle:\t%.2f deg\nrmse:\t%.3e\n"),
244 				i, B, A, FWHMx, result[0]->units, FWHMy,
245 				result[0]->units, r, angle, rmse);
246 		show_data_dialog(msg, _("Average Star Data"), "stars_list_window", NULL);
247 		g_free(msg);
248 	}
249 }
250 
get_index_of_selected_star(gdouble x,gdouble y)251 static gint get_index_of_selected_star(gdouble x, gdouble y) {
252 	int i = 0;
253 
254 	while (com.stars && com.stars[i]) {
255 		if ((com.stars[i]->xpos == x) && (com.stars[i]->ypos == y)) {
256 			return i;
257 		}
258 		i++;
259 	}
260 	return -1;
261 }
262 
display_status()263 static void display_status() {
264 	gchar *text;
265 	int i = 0;
266 	GtkStatusbar *statusbar;
267 
268 	statusbar = GTK_STATUSBAR(lookup_widget("statusbar_PSF"));
269 
270 	while (com.stars && com.stars[i])
271 		i++;
272 	if (com.selected_star == -1) {
273 		if (i > 0) {
274 			text = ngettext("%d star", "%d stars", i);
275 			text = g_strdup_printf(text, i);
276 		} else {
277 			text = g_strdup(" ");
278 		}
279 	} else {
280 		text = g_strdup_printf(_("Star %d of %d"), com.selected_star + 1, i);
281 	}
282 	gtk_statusbar_push(statusbar, COUNT_STATE, text);
283 	g_free(text);
284 }
285 
remove_selected_star(int index)286 static void remove_selected_star(int index) {
287 	GtkTreeSelection *selection = GTK_TREE_SELECTION(gtk_builder_get_object(builder, "treeview-selection"));
288 	GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(gtk_builder_get_object(builder, "Stars_stored")));
289 	GtkTreeIter iter;
290 
291 	if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
292 		gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
293 		gtk_tree_selection_unselect_all(selection);
294 
295 		remove_star(index);
296 
297 		com.selected_star = -1;
298 		display_status();
299 	}
300 }
301 
remove_all_stars()302 static void remove_all_stars(){
303 	clear_stars_list();
304 	com.selected_star = -1;
305 	display_status();
306 	redraw(com.cvport, REMAP_NONE);
307 }
308 
save_list(gchar * filename)309 static int save_list(gchar *filename) {
310 	int i = 0;
311 	if (!com.stars)
312 		return 1;
313 	GError *error = NULL;
314 
315 	GFile *file = g_file_new_for_path(filename);
316 	GOutputStream *output_stream = (GOutputStream*) g_file_replace(file, NULL, FALSE,
317 			G_FILE_CREATE_NONE, NULL, &error);
318 
319 	if (output_stream == NULL) {
320 		if (error != NULL) {
321 			g_warning("%s\n", error->message);
322 			g_clear_error(&error);
323 			fprintf(stderr, "save_list: Cannot save star list\n");
324 		}
325 		g_object_unref(file);
326 		return 1;
327 	}
328 
329 	while (com.stars[i]) {
330 		gchar *buffer = g_strdup_printf(
331 				"%d\t%d\t%10.6f %10.6f %10.2f %10.2f %10.2f %10.2f %3.2f %10.3e %10.2f%s",
332 				i + 1, com.stars[i]->layer, com.stars[i]->B, com.stars[i]->A,
333 				com.stars[i]->xpos, com.stars[i]->ypos, com.stars[i]->fwhmx,
334 				com.stars[i]->fwhmy, com.stars[i]->angle, com.stars[i]->rmse, com.stars[i]->mag, SIRIL_EOL);
335 
336 		if (!g_output_stream_write_all(output_stream, buffer, strlen(buffer), NULL, NULL, &error)) {
337 			g_warning("%s\n", error->message);
338 			g_free(buffer);
339 			g_clear_error(&error);
340 			g_object_unref(output_stream);
341 			g_object_unref(file);
342 			return 1;
343 		}
344 		i++;
345 		g_free(buffer);
346 	}
347 	siril_log_message(_("The file %s has been created.\n"), filename);
348 	g_object_unref(file);
349 
350 	return 0;
351 }
352 
set_filter(GtkFileChooser * dialog)353 static void set_filter(GtkFileChooser *dialog) {
354 	GtkFileFilter *f = gtk_file_filter_new();
355 	gtk_file_filter_set_name(f, _("Star list file (*.lst)"));
356 	gtk_file_filter_add_pattern(f, "*.lst");
357 	gtk_file_chooser_add_filter(dialog, f);
358 	gtk_file_chooser_set_filter(dialog, f);
359 }
360 
save_stars_dialog()361 static void save_stars_dialog() {
362 	SirilWidget *widgetdialog;
363 	GtkFileChooser *dialog = NULL;
364 	GtkWindow *parent = GTK_WINDOW(lookup_widget("stars_list_window"));
365 	gint res;
366 
367 	widgetdialog = siril_file_chooser_save(parent, GTK_FILE_CHOOSER_ACTION_SAVE);
368 	dialog = GTK_FILE_CHOOSER(widgetdialog);
369 	gtk_file_chooser_set_current_folder(dialog, com.wd);
370 	gtk_file_chooser_set_select_multiple(dialog, FALSE);
371 	gtk_file_chooser_set_do_overwrite_confirmation(dialog, TRUE);
372 	gtk_file_chooser_set_current_name(dialog, "stars.lst");
373 	set_filter(dialog);
374 
375 	res = siril_dialog_run(widgetdialog);
376 	if (res == GTK_RESPONSE_ACCEPT) {
377 		gchar *file = gtk_file_chooser_get_filename(dialog);
378 		save_list(file);
379 
380 		g_free(file);
381 	}
382 	siril_widget_destroy(widgetdialog);
383 }
384 
385 /********************* public ***********************/
386 
add_star_to_list(fitted_PSF * star)387 void add_star_to_list(fitted_PSF *star) {
388 	static GtkTreeSelection *selection = NULL;
389 	GtkTreeIter iter;
390 
391 	get_stars_list_store();
392 	if (!selection)
393 		selection = GTK_TREE_SELECTION(gtk_builder_get_object(builder, "treeview-selection"));
394 	if (star == NULL) {
395 		gtk_list_store_clear(liststore_stars);
396 		return;		// just clear the list
397 	}
398 
399 	gtk_list_store_append (liststore_stars, &iter);
400 	gtk_list_store_set (liststore_stars, &iter,
401 			COLUMN_CHANNEL, star->layer,
402 			COLUMN_B, star->B,
403 			COLUMN_A, star->A,
404 			COLUMN_X0, star->xpos,
405 			COLUMN_Y0, star->ypos,
406 			COLUMN_FWHMX, star->fwhmx,
407 			COLUMN_FWHMY, star->fwhmy,
408 			COLUMN_MAG, star->mag,
409 			COLUMN_ROUNDNESS, star->fwhmy/star->fwhmx,
410 			COLUMN_ANGLE, star->angle,
411 			COLUMN_RMSE, star->rmse,
412 			-1);
413 
414 	units = star->units;
415 	display_status();
416 }
417 
fill_stars_list(fits * fit,fitted_PSF ** stars)418 void fill_stars_list(fits *fit, fitted_PSF **stars) {
419 	int i = 0;
420 	if (stars == NULL)
421 		return;
422 	add_star_to_list(NULL);	// clear
423 
424 	while (stars[i]) {
425 		/* update units if needed */
426 		fwhm_to_arcsec_if_needed(fit, stars[i]);
427 		add_star_to_list(stars[i]);
428 		i++;
429 	}
430 	com.selected_star = -1;
431 }
432 
refresh_star_list(fitted_PSF ** star)433 void refresh_star_list(fitted_PSF **star){
434 	get_stars_list_store();
435 	gtk_list_store_clear(liststore_stars);
436 	fill_stars_list(&gfit, com.stars);
437 	redraw(com.cvport, REMAP_NONE);
438 }
439 
clear_stars_list()440 void clear_stars_list() {
441 	if (com.stars) {
442 		if (!com.headless) {
443 			get_stars_list_store();
444 			gtk_list_store_clear(liststore_stars);
445 		}
446 		if (com.stars[0]) {
447 			/* freeing found stars. It must not be done when the only star in
448 			 * com.stars is the same as com.seq.imgparam[xxx].fwhm, as set in
449 			 * set_fwhm_star_as_star_list(), because it will be reused */
450 			if (com.stars[1] || !com.star_is_seqdata) {
451 				int i = 0;
452 				while (i < MAX_STARS && com.stars[i])
453 					free(com.stars[i++]);
454 			}
455 		}
456 		free(com.stars);
457 		com.stars = NULL;
458 	}
459 	com.star_is_seqdata = FALSE;
460 }
461 
pick_a_star()462 void pick_a_star() {
463 	int layer = match_drawing_area_widget(com.vport[com.cvport], FALSE);
464 	int new_index;
465 
466 	if (layer != -1) {
467 		if (!(com.selection.h && com.selection.w))
468 			return;
469 		if (com.selection.w > 300 || com.selection.h > 300) {
470 			siril_message_dialog(GTK_MESSAGE_WARNING, _("Current selection is too large"),
471 					_("To determine the PSF, please make a selection around a star."));
472 			return;
473 		}
474 		fitted_PSF *new_star = add_star(&gfit, layer, &new_index);
475 		if (new_star) {
476 			add_star_to_list(new_star);
477 			siril_open_dialog("stars_list_window");
478 		} else
479 			return;
480 	}
481 	redraw(com.cvport, REMAP_NONE);
482 }
483 
484 /***************** callbacks ****************/
485 
on_treeview_cursor_changed(GtkTreeView * tree_view,gpointer user_data)486 void on_treeview_cursor_changed(GtkTreeView *tree_view,
487 		gpointer user_data) {
488 	GtkTreeModel *treeModel = gtk_tree_view_get_model(tree_view);
489 	GtkTreeSelection *selection = gtk_tree_view_get_selection (tree_view);
490 	GtkTreeIter iter;
491 	GValue value_x = G_VALUE_INIT;
492 	GValue value_y = G_VALUE_INIT;
493 
494 	if (gtk_tree_model_get_iter_first(treeModel, &iter) == FALSE)
495 		return;	//The tree is empty
496 	if (gtk_tree_selection_get_selected(selection, &treeModel, &iter)) { //get selected item
497 		gdouble x0, y0;
498 
499 		gtk_tree_model_get_value(treeModel, &iter, COLUMN_X0, &value_x);
500 		x0 = g_value_get_double(&value_x);
501 		gtk_tree_model_get_value(treeModel, &iter, COLUMN_Y0, &value_y);
502 		y0 = g_value_get_double(&value_y);
503 
504 		g_value_unset(&value_x);
505 		g_value_unset(&value_y);
506 
507 		com.selected_star = get_index_of_selected_star(x0, y0);
508 		display_status();
509 		redraw(com.cvport, REMAP_NONE);
510 	}
511 }
512 
on_Stars_stored_key_release_event(GtkWidget * widget,GdkEventKey * event,gpointer user_data)513 void on_Stars_stored_key_release_event(GtkWidget *widget, GdkEventKey *event,
514 		gpointer user_data) {
515 	if (event->keyval == GDK_KEY_Delete || event->keyval == GDK_KEY_KP_Delete
516 			|| event->keyval == GDK_KEY_BackSpace) {
517 
518 		remove_selected_star(com.selected_star);
519 	}
520 }
521 
on_stars_list_window_hide(GtkWidget * object,gpointer user_data)522 void on_stars_list_window_hide(GtkWidget *object, gpointer user_data) {
523 	com.selected_star = -1;
524 }
525 
on_sum_button_clicked(GtkButton * button,gpointer user_data)526 void on_sum_button_clicked(GtkButton *button, gpointer user_data) {
527 	display_PSF(com.stars);
528 }
529 
on_remove_button_clicked(GtkButton * button,gpointer user_data)530 void on_remove_button_clicked(GtkButton *button, gpointer user_data) {
531 	remove_selected_star(com.selected_star);
532 }
533 
on_remove_all_button_clicked(GtkButton * button,gpointer user_data)534 void on_remove_all_button_clicked(GtkButton *button, gpointer user_data) {
535 	remove_all_stars();
536 }
537 
on_process_starfinder_button_clicked(GtkButton * button,gpointer user_data)538 void on_process_starfinder_button_clicked(GtkButton *button, gpointer user_data) {
539 	int nbstars = 0;
540 	int layer = com.cvport == RGB_VPORT ? GLAYER : com.cvport;
541 	if (!single_image_is_loaded() && !sequence_is_loaded()) {
542 		siril_log_color_message(_("Load an image first, aborted.\n"), "red");
543 		return;
544 	}
545 	set_cursor_waiting(TRUE);
546 
547 	confirm_peaker_GUI(); //making sure the spin buttons values are read even without confirmation
548 	delete_selected_area();
549 	com.stars = peaker(&gfit, layer, &com.starfinder_conf, &nbstars, NULL, TRUE);
550 	siril_log_message(_("Found %d stars in image, channel #%d\n"), nbstars, layer);
551 	if (com.stars)
552 		refresh_star_list(com.stars);
553 	set_cursor_waiting(FALSE);
554 }
555 
on_export_button_clicked(GtkButton * button,gpointer user_data)556 void on_export_button_clicked(GtkButton *button, gpointer user_data) {
557 	if (com.stars) {
558 		save_stars_dialog();
559 	} else {
560 		siril_message_dialog(GTK_MESSAGE_WARNING, _("Nothing to export"),
561 				_("There are no stars in the list."));
562 	}
563 }
564 
on_stars_list_window_show(GtkWidget * widget,gpointer user_data)565 void on_stars_list_window_show(GtkWidget *widget, gpointer user_data) {
566 	update_peaker_GUI();
567 	fill_stars_list(&gfit, com.stars);
568 }
569 
on_button_stars_list_ok_clicked(GtkButton * button,gpointer user_data)570 void on_button_stars_list_ok_clicked(GtkButton *button, gpointer user_data) {
571 	siril_close_dialog("stars_list_window");
572 }
573 
574