fitsview-hartmann/src/fitsheaders.c
2014-09-22 14:48:19 +04:00

531 lines
16 KiB
C

// fitsheaders.c - functions to edit FITS head
//
// Copyright 2011 Edward V. Emelianoff <eddy@sao.ru>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
// MA 02110-1301, USA.
#include "fitsheaders.h"
#include "fits.h"
GtkListStore *combo_ = NULL;
GtkTreeSelection *sel;
GtkTreeView* Tree;
GtkWidget *del_button;
GtkWidget *window;
// Data types to write FITS keys into menu
gchar* key_types[] = {
"TSTRING", "TLOGICAL", "TBYTE", "TSHORT", "TUSHORT", "TINT", "TUINT",
"TLONG", "TULONG", "TLONGLONG", "TFLOAT", "TDOUBLE", "TCOMPLEX",
"TDBLCOMPLEX"
};
// Values itself
int type_vals[] = {
TSTRING,
TLOGICAL,
TBYTE,
TSHORT,
TUSHORT,
TINT,
TUINT,
TLONG,
TULONG,
TLONGLONG,
TFLOAT,
TDOUBLE,
TCOMPLEX,
TDBLCOMPLEX
};
int VALS_N; // Number of items
gchar *colnames[] = {N_("Type"), N_("Name"), N_("Value"), N_("Comment")};
#define VISIBLE_COLS 4 // The amount of visible columns
int find_list_type(char *type){
int i;
for(i = 0; i < VALS_N; i++)
if(strcmp(key_types[i], type) == 0) break;
if(i == VALS_N) // Error: there's no such value in list
i = 0;
return i;
}
gboolean ll_conv(gchar **value, int type){
long long ll_val, oll_val;
gboolean ret = TRUE;
ll_val = oll_val = strtoll(*value, NULL, 0);
//g_free(*value);
switch(type){
case TBYTE:
if(ll_val > CHAR_MAX || ll_val < CHAR_MIN) ll_val = 0L;
break;
case TSHORT:
if(ll_val > SHRT_MAX || ll_val < SHRT_MIN) ll_val = 0L;
break;
case TINT:
if(ll_val > INT_MAX || ll_val < INT_MIN) ll_val = 0L;
break;
case TLONG:
if(ll_val > LONG_MAX || ll_val < LONG_MIN) ll_val = 0L;
break;
case TLONGLONG:
if(ll_val > LLONG_MAX || ll_val < LLONG_MIN) ll_val = 0L;
break;
case TUSHORT:
if(ll_val > USHRT_MAX || ll_val < 0L) ll_val = 0L;
break;
case TUINT:
if(ll_val > UINT_MAX || ll_val < 0L) ll_val = 0L;
break;
case TULONG:
if((unsigned long)ll_val > ULONG_MAX || ll_val < 0L) ll_val = 0L;
break;
default:
ll_val = 0L;
break;
}
DBG("long long: %lld", ll_val);
if(ll_val != oll_val){
g_err(_("Error in value range!"));
ret = FALSE;
}
g_free(*value);
*value = g_strdup_printf("%lld", ll_val);
return ret;
}
gboolean mystrtod(gchar **val){
double d_val;
gboolean ret = TRUE;
char *eptr = NULL, *ptr = *val;
errno = 0;
d_val = strtod(ptr, &eptr);
DBG("double: %.16e (was: %s)", d_val, *val);
if((eptr && *eptr) || errno == ERANGE){
g_free(*val);
DBG("try convert %g into val", d_val);
*val = g_strdup_printf("%.16e", d_val);
g_err(_("Invalid double number!"));
ret = FALSE;
}
DBG("val: %s", *val);
return ret;
}
gboolean check_complex_number(gchar **val){
double re = 0., im = 0.;
gboolean ret = TRUE;
if(!cmplx_conv(*val, &re, &im)){
gchar *msg = g_strdup_printf("%s\n%s",
_("Format error: complex number must be in format"),
"RE + iIM, RE + jIM, RE iIM, RE jIM, (RE, IM) or (RE IM)");
g_err(msg);
g_free(msg);
ret = FALSE;
}
DBG("complex: (%g, %g)", re, im);
g_free(*val);
*val = g_strdup_printf("(%g, %g)", re, im);
return ret;
}
gboolean check_val(gchar **newval, int type){
gboolean b_val, ret = TRUE;
switch(type){
case TBYTE:
case TSHORT:
case TINT:
case TLONG:
case TLONGLONG:
case TUSHORT:
case TUINT:
case TULONG:
// convert *char -> long long vith checking value
ret = ll_conv(newval, type);
break;
case TFLOAT:
case TDOUBLE:
// convert *char -> double
ret = mystrtod(newval);
break;
case TCOMPLEX:
case TDBLCOMPLEX:
// check whether a number is complex
ret = check_complex_number(newval);
break;
case TLOGICAL:
// convert value into boolean
if(g_ascii_strncasecmp("true", *newval, 4)==0) b_val = TRUE;
else
b_val = atoi(*newval);
g_free(*newval);
*newval = g_strdup_printf("%s", b_val ? "TRUE" : "FALSE");
DBG("boolean: %s", *newval);
break;
default:
DBG("string: %s", *newval); // all other values a strings
}
return ret;
}
gboolean try_change_val(IMAGE *ima, gchar **newval){
int type;
GtkTreeIter cur_items;
gboolean ret;
GtkTreeModel *model;
FNAME();
if(!gtk_tree_selection_get_selected(sel,&model, &cur_items)) return FALSE;
gtk_tree_model_get(GTK_TREE_MODEL(ima->store), &cur_items, L_TYPE, &type, -1);
ret = check_val(newval, type);
gtk_list_store_set(ima->store, &cur_items, L_VAL, *newval, -1);
DBG("ret");
return ret;
}
guint cur_column;
void edit_cell( GtkCellRendererText *cell,
gchar *path,
gchar *new_text,
IMAGE *ima){
GtkTreeModel *model;
GtkTreeIter cur_items;
cur_column = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(cell), "column_num"));
gboolean nxtcol = TRUE, editable = TRUE;
if(!path) return;
DBG("path: %s, new: %s, colnum: %d", path, new_text, cur_column);
gchar *textptr;
if(!gtk_tree_selection_get_selected(sel,&model, &cur_items)) return;
switch(cur_column){
case L_TYPENM: // key type was changed
gtk_list_store_set(ima->store, &cur_items, L_TYPE,
type_vals[find_list_type(new_text)], -1);
gtk_list_store_set(ima->store, &cur_items, cur_column, new_text, -1);
break;
case L_VAL: // key value was changed
textptr = g_strdup_printf("%s", new_text);
nxtcol = try_change_val(ima, &textptr); // try to change parameter according to its type
g_free(textptr);
break;
default:
gtk_list_store_set(ima->store, &cur_items, cur_column, new_text, -1);
break;
}
if(nxtcol){
if(cur_column < L_COMM) // it's not a last column - select next
cur_column++;
else{
cur_column = 0;
editable = FALSE;
}
}
DBG("set cursor col:%d, e=%d", cur_column, editable);
gtk_tree_view_set_cursor(Tree,
gtk_tree_model_get_path(GTK_TREE_MODEL(ima->store), &cur_items),
gtk_tree_view_get_column(Tree, cur_column), editable);
DBG("ret");
}
/*
void cancel_edit(GtkCellRenderer *renderer){
cur_column = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(renderer), "column_num"));
gtk_tree_view_set_cursor(Tree,
gtk_tree_model_get_path(GTK_TREE_MODEL(store), &cur_items),
gtk_tree_view_get_column(Tree, cur_column), FALSE);
}
*/
void add_key( IMAGE *ima,
gchar *keytype, gchar *keyname, gchar *keyval,
gchar *keycomment, int keyfitstype){
GtkTreeIter iter;
//~ FNAME();
gtk_list_store_append(ima->store, &iter);
gtk_list_store_set(ima->store, &iter,
L_TYPENM, keytype,
L_KEY, keyname,
L_VAL, keyval,
L_COMM, keycomment,
L_TYPE, keyfitstype,
-1);// the last element must be -1
}
void init_keylist(IMAGE *ima){
/* Create a list model.
* We will have 4 columns: name of key type, key name, key value, comment, type
*/
if(ima->store) g_object_unref(G_OBJECT(ima->store)); // clear list, if it already exists
ima->store = gtk_list_store_new(L_MAX, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT);
}
GtkWidget *create_treeview(IMAGE *ima){
int i, i_max = sizeof(key_types)/sizeof(char*);;
GtkTreeIter iter;
GtkCellRenderer *renderer;
GtkTreeViewColumn *column;
if(combo_)
gtk_list_store_clear(combo_);
combo_ = gtk_list_store_new(1, G_TYPE_STRING);
for(i = 0; i < i_max; i++){
gtk_list_store_append(combo_, &iter);
gtk_list_store_set(combo_, &iter, 0, key_types[i], -1);
}
// create list (treeview) according model store
Tree = (GtkTreeView*)gtk_tree_view_new_with_model(GTK_TREE_MODEL(ima->store));
// Create columns in list
for(i = 0; i < VISIBLE_COLS; i++){
if(i == 0){
renderer = gtk_cell_renderer_combo_new();
g_object_set(renderer, "text-column", 0,
"editable", TRUE, "has-entry", FALSE, "model",
GTK_TREE_MODEL(combo_), NULL);
}
else{
renderer = gtk_cell_renderer_text_new ();
g_object_set(renderer, "editable", TRUE, NULL);
}
g_object_set_data(G_OBJECT(renderer), "column_num", GINT_TO_POINTER(i));
column = gtk_tree_view_column_new_with_attributes(_(colnames[i]),
renderer, "text", i, NULL);
gtk_tree_view_append_column(Tree, column); // insert column
gtk_tree_view_column_set_sort_column_id(column, i);// it may be sorted
g_signal_connect(renderer, "edited", G_CALLBACK(edit_cell), ima);
// g_signal_connect(renderer, "editing-canceled", G_CALLBACK(cancel_edit), NULL);
}
/*
renderer = gtk_cell_renderer_text_new ();
column = gtk_tree_view_column_new_with_attributes ("hidden",renderer, NULL);
g_object_set(column, "visible", FALSE, NULL);
gtk_tree_view_append_column(Tree, column);
*/
return GTK_WIDGET(Tree);
}
static void list_changed(GtkTreeSelection *sel){
GtkTreeModel *model;
GtkTreeIter cur_items;
if(gtk_tree_selection_get_selected(sel,&model, &cur_items))
gtk_widget_set_sensitive(del_button, TRUE);
else
gtk_widget_set_sensitive(del_button, FALSE);
/*
GtkTreeModel *model;
FNAME();
gboolean showdel = FALSE;
if(!GTK_IS_TREE_SELECTION(sel)){
showdel = TRUE;
if(GTK_IS_TREE_SELECTION(obj)) DBG("!!!");
cur_column = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(sel), "column_num"));
DBG("curcol: %d", cur_column);
}
else if(gtk_tree_selection_get_selected(sel,&model, &cur_items)){
gint column = L_VAL;
gchar *value;
gtk_tree_model_get(model, &cur_items, column, &value, -1);
DBG("selected: %s", value);
gtk_tree_view_set_cursor(Tree,
gtk_tree_model_get_path(model, &cur_items),
gtk_tree_view_get_column(Tree, cur_column), FALSE);
g_free(value);
showdel = TRUE;
}
gtk_widget_set_sensitive(del_button, showdel);
DBG("ret");*/
}
void newline(IMAGE *ima){
FNAME();
GtkTreeIter cur_items;
gtk_list_store_append(ima->store, &cur_items);
gtk_list_store_set(ima->store, &cur_items,
L_TYPENM, key_types[0],
L_KEY, "NEW_KEY",
L_VAL, "",
L_COMM, "",
L_TYPE, type_vals[0],
-1);
// Set cursor onto a first field
gtk_tree_view_set_cursor(Tree,
gtk_tree_model_get_path(GTK_TREE_MODEL(ima->store), &cur_items),
gtk_tree_view_get_column(Tree, 0), TRUE);
DBG("ret");
}
gboolean del_line(IMAGE *ima){
GtkTreeIter iter;
GtkTreeModel *model = GTK_TREE_MODEL(ima->store);
gboolean ret = FALSE;
FNAME();
GtkTreeSelection *selection = gtk_tree_view_get_selection(Tree);
GtkTreePath *path;
if(gtk_tree_selection_get_selected(selection, NULL, &iter)){
path = gtk_tree_model_get_path(model, &iter);
ret = gtk_list_store_remove(ima->store, &iter);
// check wherher &cur_items was last
if(gtk_list_store_iter_is_valid(ima->store, &iter)){
gtk_tree_path_free(path);
path = gtk_tree_model_get_path(model, &iter);
}
else gtk_tree_path_prev(path); // goto previous
if(path && gtk_tree_model_get_iter_first(model, &iter))
gtk_tree_view_set_cursor(Tree, path, NULL, FALSE);
else DBG("No entries");
gtk_tree_path_free(path);
}
DBG("ret");
return ret;
}
/*
void show_menu(){
GtkWidget *menu, *menuitem;
gchar *text;
int i;
menu = gtk_menu_new();
for(i = 0; i < 10; i++){
text = g_strdup_printf("%d", i);
menuitem = gtk_menu_item_new_with_label(text);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), menuitem);
gtk_widget_show(menuitem);
g_free(text);
}
gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, 0);
}
gboolean treeview_button_press_cb(GtkWidget *wi, GdkEventButton *ev, gpointer user_data)
{
GtkTreeSelection *sel;
GtkTreeModel *model;
GtkTreePath *path, *oldpath;
GtkTreeViewColumn *column;
GtkTreeIter iter;
gchar *colname;
// if there's no path where the click occurred...
if(!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(wi), ev->x, ev->y, &path, &column, NULL, NULL))
return FALSE; // ...return FALSE to allow the event to continue
colname = strdup(gtk_tree_view_column_get_title(column));
// get the tree view's selection
sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(wi));
switch(ev->button)
{
// LMB
case 1:
// check to see if this is already the selected path
if(gtk_tree_selection_get_selected(sel, &model, &iter))
{
oldpath = gtk_tree_model_get_path(model, &iter);
if(gtk_tree_path_compare(oldpath, path) == 0)
{
gtk_tree_path_free(oldpath);
gtk_tree_path_free(path);
return FALSE;
}
gtk_tree_path_free(oldpath);
}
// select the clicked path
gtk_tree_selection_select_path(sel, path);
return TRUE;
// RMB
case 3:
if(strncmp(colname, "No.", 3)){
gtk_tree_path_free(path);
return TRUE;
}
show_menu();
// gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, ev->time);
break;
}
// free our path
gtk_tree_path_free(path);
return FALSE; // allow it to continue
}
*/
void close_dlg(){
gtk_widget_destroy(window);
}
static gboolean press_key(IMAGE *ima, GdkEventKey * event){
gboolean ret = FALSE;
if(event->state & (GDK_CONTROL_MASK)){
switch(event->keyval){
// ctrl+n inserts a line
case 'n': case 'N':
newline(ima);
ret = TRUE;
break;
// ctrl+d deletes current line
case 'd': case 'D':
ret = del_line(ima);
break;
// ctrl+w closes the window
case 'w': case 'W':
close_dlg();
}
}
return ret;
}
void edit_headers(Window *parent){
VALS_N = sizeof(key_types) / sizeof(gchar *);
GtkWidget *treeview, *vbox, *hbox, *button;
if(!parent->image || !parent->image->store){
g_err(_("Open fits file first"));
return;
}
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "List");
gtk_signal_connect(GTK_OBJECT(window),"delete_event",GTK_SIGNAL_FUNC(gtk_false),NULL);
gtk_signal_connect(GTK_OBJECT(window),"destroy",GTK_SIGNAL_FUNC(close_dlg), NULL);
treeview = create_treeview(parent->image);
sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeview));
g_signal_connect(G_OBJECT(sel), "changed", G_CALLBACK(list_changed), NULL);
//g_signal_connect(G_OBJECT(treeview), "cursor-changed", G_CALLBACK(list_changed), NULL);
vbox = gtk_vbox_new(FALSE, 5);
gtk_container_add (GTK_CONTAINER (window), vbox);
GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_ETCHED_IN);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw), GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
gtk_box_pack_start(GTK_BOX(vbox), sw, TRUE, TRUE, 0);
gtk_container_add(GTK_CONTAINER(sw), treeview);
gtk_window_set_default_size(GTK_WINDOW (window), 640, 480);
//~ gtk_box_pack_start(GTK_BOX(vbox), treeview, TRUE, TRUE, 0);
hbox = gtk_hbox_new(FALSE, 5);
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
button = gtk_button_new_with_label(_("New entry (ctrl+n)"));
g_signal_connect_swapped(G_OBJECT(button), "clicked",
G_CALLBACK(newline), parent->image);
gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
del_button = gtk_button_new_with_label(_("Delete entry (ctrl+d)"));
g_signal_connect_swapped(G_OBJECT(del_button), "clicked",
G_CALLBACK(del_line), parent->image);
gtk_box_pack_end(GTK_BOX(hbox), del_button, FALSE, FALSE, 0);
gtk_widget_set_sensitive(del_button, FALSE);
button = gtk_button_new_with_label(_("Close (ctrl+w)"));
g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(close_dlg), NULL);
gtk_box_pack_end(GTK_BOX(hbox), button, FALSE, FALSE, 0);
g_signal_connect_swapped(G_OBJECT(window), "key-press-event",
G_CALLBACK(press_key), parent->image);
run_modal_window(GTK_WINDOW(window), parent);
}