/* Copyright (C) 2003 by Sean David Fleming sean@ivec.org 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. The GNU GPL can also be found at http://www.gnu.org */ #include #include #include #include #include #ifndef __WIN32 #include #endif #include "gdis.h" #include "coords.h" #include "edit.h" #include "matrix.h" #include "opengl.h" #include "parse.h" #include "render.h" #include "spatial.h" #include "select.h" #include "shortcuts.h" #include "interface.h" #include "dialog.h" #include "measure.h" extern struct sysenv_pak sysenv; extern struct elem_pak elements[]; gdouble measure_min_12 = 0.1, measure_max_12 = 2.0; gdouble measure_min_23 = 0.1, measure_max_23 = 2.0; gdouble measure_min_a = 0.1, measure_max_a = 180.0; GtkWidget *meas_tv; GtkWidget *match1, *match2, *match3, *match4, *search, *entry; GtkWidget *spin1, *spin2, *spin3, *spin4, *spin5, *spin6; GtkTreeStore *meas_ts=NULL; enum {MEAS_NAME, MEAS_ATOMS, MEAS_VALUE, MEAS_MODEL, MEAS_POINTER, MEAS_NCOLS}; /************************/ /* measurement printout */ /************************/ void meas_dump_all(void) { struct model_pak *model; model = sysenv.active_model; if (model) measure_dump_all(model); } /*****************************/ /* safely acquire tree model */ /*****************************/ GtkTreeModel *meas_treemodel(void) { if (!meas_tv) return(NULL); if (!GTK_IS_TREE_VIEW(meas_tv)) return(NULL); return(gtk_tree_view_get_model(GTK_TREE_VIEW(meas_tv))); } /*********************************************/ /* free data pointers assoc with an iterator */ /*********************************************/ void meas_free_iter_data(GtkTreeIter *iter) { gchar *text; GtkTreeModel *treemodel; treemodel = meas_treemodel(); if (!treemodel) return; gtk_tree_model_get(treemodel, iter, MEAS_NAME, &text, -1); g_free(text); gtk_tree_model_get(treemodel, iter, MEAS_ATOMS, &text, -1); g_free(text); gtk_tree_model_get(treemodel, iter, MEAS_VALUE, &text, -1); g_free(text); } /************************/ /* graft model iterator */ /************************/ gint meas_model_iter(GtkTreeIter *iter, struct model_pak *model) { GtkTreeModel *treemodel; struct model_pak *target=NULL; /* checks */ g_assert(model != NULL); if (!meas_ts) return(0); treemodel = meas_treemodel(); if (!treemodel) return(0); /* get the first parent iterator */ if (gtk_tree_model_get_iter_first(treemodel, iter)) { /* search for target model, until we run out of iterators */ gtk_tree_model_get(treemodel, iter, MEAS_MODEL, &target, -1); while (target != model) { if (gtk_tree_model_iter_next(treemodel, iter)) gtk_tree_model_get(treemodel, iter, MEAS_MODEL, &target, -1); else break; } } /* append new if model is not on the tree */ if (target != model) { gtk_tree_store_append(meas_ts, iter, NULL); gtk_tree_store_set(meas_ts, iter, MEAS_NAME, model->basename, MEAS_MODEL, model, MEAS_POINTER, NULL, -1); } return(1); } /**************************************/ /* locate a single measurement's iter */ /**************************************/ void measure_iter_find(GtkTreeIter *iter, gpointer measure, struct model_pak *model) { /* TODO */ } /*******************************/ /* update a single measurement */ /*******************************/ void measure_update_selected(gpointer measure, struct model_pak *model) { gchar *value; GtkTreeIter iter; GtkTreeModel *treemodel; GtkTreeSelection *selection; /* checks */ treemodel = meas_treemodel(); if (!treemodel) return; /* get selected iterator */ if (!(selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(meas_tv)))) return; if (!gtk_tree_selection_get_selected(selection, &treemodel, &iter)) return; value = g_strdup(measure_value_get(measure)); gtk_tree_store_set(meas_ts, &iter, MEAS_VALUE, value, -1); g_free(value); } /******************************/ /* graft a single measurement */ /******************************/ void meas_graft_single(gpointer measure, struct model_pak *model) { gchar *info[3]; GtkTreeIter parent, iter; /* checks */ g_assert(model != NULL); g_assert(measure != NULL); /* update dialog (if it exists) */ if (!meas_ts) return; if (!meas_model_iter(&parent, model)) return; /* set child iterator data */ gtk_tree_store_append(meas_ts, &iter, &parent); info[0] = measure_type_label_create(measure); info[1] = measure_constituents_create(measure); info[2] = g_strdup(measure_value_get(measure)); /* add the info */ gtk_tree_store_set(meas_ts, &iter, MEAS_NAME, info[0], MEAS_ATOMS, info[1], MEAS_VALUE, info[2], MEAS_MODEL, model, MEAS_POINTER, measure, -1); g_free(info[0]); g_free(info[1]); g_free(info[2]); gtk_tree_view_expand_all(GTK_TREE_VIEW(meas_tv)); } /***********************************************************/ /* remove a model's measurements, leaving the model's iter */ /***********************************************************/ void meas_prune_all(struct model_pak *model) { GtkTreeIter parent, child; GtkTreeModel *treemodel; /* checks */ g_assert(model != NULL); if (!meas_ts) return; treemodel = meas_treemodel(); if (!treemodel) return; if (meas_model_iter(&parent, model)) { /* remove all child iterators and free their data */ while (gtk_tree_model_iter_children(treemodel, &child, &parent)) { meas_free_iter_data(&child); gtk_tree_store_remove(meas_ts, &child); } } } /**********************************/ /* graft measurements to the tree */ /**********************************/ void meas_graft_all(struct model_pak *model) { GSList *list; GtkTreeModel *treemodel; GtkTreeIter parent; GtkTreePath *path; /* return */ g_assert(model != NULL); if (!meas_ts) return; if (!meas_tv) return; /* get model's iterator (if any) */ if (!meas_model_iter(&parent, model)) return; if (!g_slist_length(model->measure_list)) { /* no measurements - remove parent iterator */ gtk_tree_store_remove(meas_ts, &parent); } else { /* add all measurements */ for (list=model->measure_list ; list ; list=g_slist_next(list)) meas_graft_single(list->data, model); /* expand model's iterator row */ treemodel = meas_treemodel(); if (treemodel) { path = gtk_tree_model_get_path(treemodel, &parent); gtk_tree_view_expand_row(GTK_TREE_VIEW(meas_tv), path, FALSE); gtk_tree_path_free(path); } } } /***********************************/ /* update measurements for a model */ /***********************************/ void meas_graft_model(struct model_pak *model) { g_assert(model != NULL); /* update underlying measurements */ measure_update_global(model); meas_prune_all(model); meas_graft_all(model); } /*************************************/ /* remove model and its measurements */ /*************************************/ void meas_prune_model(struct model_pak *model) { GtkTreeIter parent, child; GtkTreeModel *treemodel; /* checks */ g_assert(model != NULL); if (!meas_ts) return; treemodel = meas_treemodel(); if (!treemodel) return; if (meas_model_iter(&parent, model)) { /* free all child iterator data (iterators themselves are auto freed after) */ if (gtk_tree_model_iter_children(treemodel, &child, &parent)) { do { meas_free_iter_data(&child); } while (gtk_tree_model_iter_next(treemodel, &child)); } /* remove parent (auto removes all child iterators) */ gtk_tree_store_remove(meas_ts, &parent); } } /***************************/ /* geometry label deletion */ /***************************/ void meas_prune_selected(void) { GtkTreeIter iter; GtkTreeModel *treemodel; GtkTreeSelection *selection; gpointer measurement=NULL; struct model_pak *model=NULL; /* checks */ treemodel = meas_treemodel(); if (!treemodel) return; /* get selected iterator */ if (!(selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(meas_tv)))) return; if (!gtk_tree_selection_get_selected(selection, &treemodel, &iter)) return; gtk_tree_model_get(treemodel, &iter, MEAS_MODEL, &model, -1); gtk_tree_model_get(treemodel, &iter, MEAS_POINTER, &measurement, -1); g_assert(model != NULL); /* if measurement selected - delete single, else delete all */ if (measurement) { /* free data */ meas_free_iter_data(&iter); gtk_tree_store_remove(meas_ts, &iter); measure_free(measurement, model); } else { meas_prune_model(model); measure_free_all(model); } redraw_canvas(SINGLE); } /*****************/ /* info on bonds */ /*****************/ void info_bond(GtkWidget *w, gint x, gint y, struct model_pak *model) { gpointer measure; struct core_pak *core[2]; struct bond_pak *bond; /* seek */ bond = gl_seek_bond(x, y, model); if (!bond) return; /* FIXME - have to ignore hydrogen bonds to stop dodgy bond measurements being drawn */ if (bond->type == BOND_HBOND) return; core[0] = bond->atom1; core[1] = bond->atom2; measure = measure_bond_test(core, 0.0, 0.0, model); if (measure) { measure_update_single(measure, model); meas_graft_single(measure, model); } redraw_canvas(SINGLE); } /*****************/ /* info on dists */ /*****************/ void info_dist(gint x, gint y, struct model_pak *model) { gpointer measure; static struct core_pak *core[2]; /* attempt to find core */ if (!(core[model->state] = gl_seek_core(x, y, model))) return; /* are we looking at the 1st or the 2nd call? */ switch (model->state) { case 0: /* select */ select_add_core(core[0], model); model->state++; break; case 1: /* remove from selection */ select_del_core(core[0], model); select_del_core(core[1], model); /* create the measurement (if it doesn't exist) */ measure = measure_distance_test(MEASURE_DISTANCE, core, 0.0, 0.0, model); if (measure) { measure_update_single(measure, model); meas_graft_single(measure, model); } model->state--; break; default: g_assert_not_reached(); model->state=0; return; } redraw_canvas(SINGLE); } /******************/ /* info on angles */ /******************/ void info_angle(gint x, gint y, struct model_pak *model) { gpointer measure; static struct core_pak *core[3]; /* attempt to find core */ if (!(core[model->state] = gl_seek_core(x, y, model))) return; /* are we looking at the 1st or the 2nd call? */ switch (model->state) { case 0: case 1: select_add_core(core[model->state], model); model->state++; break; case 2: /* remove highlighting */ select_del_core(core[0], model); select_del_core(core[1], model); select_del_core(core[2], model); /* create the measurement (if it doesn't exist) */ measure = measure_angle_test(core, 0.0, 180.0, model); if (measure) { measure_update_single(measure, model); meas_graft_single(measure, model); } model->state=0; break; default: g_assert_not_reached(); model->state=0; return; } redraw_canvas(SINGLE); } /****************************/ /* info on torsional angles */ /****************************/ void info_torsion(gint x, gint y, struct model_pak *model) { gpointer measure; static struct core_pak *core[4]; /* attempt to find core */ if (!(core[model->state] = gl_seek_core(x, y, model))) return; /* what stage are we at? */ switch (model->state) { case 0: case 1: case 2: select_add_core(core[model->state], model); model->state++; break; case 3: /* remove highlighting */ select_del_core(core[0], model); select_del_core(core[1], model); select_del_core(core[2], model); select_del_core(core[3], model); /* create the measurement (if it doesn't exist) */ measure = measure_torsion_test(core, 0.0, 180.0, model); if (measure) { measure_update_single(measure, model); meas_graft_single(measure, model); } model->state=0; break; default: g_assert_not_reached(); } redraw_canvas(SINGLE); } /************************/ /* match pairs of atoms */ /************************/ #define DEBUG_PAIR_MATCH 0 gint pair_match(const gchar *label1, const gchar *label2, struct core_pak *core1, struct core_pak *core2) { gint a, b, i, j, mask; #if DEBUG_PAIR_MATCH printf("[%s,%s] : [%s,%s]\n", label1, label2, core1->label, core2->label); #endif /* if a or b = 0 => any i or j is accepted */ /* otherwise it must match either i or j */ /* if a and b are non zero, both i & j must match both a & b */ a = elem_symbol_test(label1); b = elem_symbol_test(label2); i = core1->atom_code; j = core2->atom_code; /* fill out the mask */ mask = 0; if (i == a) { /* if input label doesn't match the element symbol length - it means the */ /* user has put in something like H1 - compare this with the atom label */ if (g_ascii_strcasecmp(label1, elements[i].symbol) != 0) { if (g_ascii_strcasecmp(core1->atom_label, label1) == 0) mask |= 1; } else mask |= 1; } if (j == a) { if (g_ascii_strcasecmp(label1, elements[j].symbol) != 0) { if (g_ascii_strcasecmp(core2->atom_label, label1) == 0) mask |= 2; } else mask |= 2; } if (i == b) { if (g_ascii_strcasecmp(label2, elements[i].symbol) != 0) { if (g_ascii_strcasecmp(core1->atom_label, label2) == 0) mask |= 4; } else mask |= 4; } if (j == b) { if (g_ascii_strcasecmp(label2, elements[j].symbol) != 0) { if (g_ascii_strcasecmp(core2->atom_label, label2) == 0) mask |= 8; } else mask |= 8; } #if DEBUG_PAIR_MATCH printf("mask = %d\n", mask); #endif /* if both types must match - only two possibilities (a,b) or (b,a) */ /* but we can get further matches (than the required 2) when labels are compared */ if (a && b) { switch(mask) { /* single valid pair match */ case 6: case 9: /* valid pair match plus one extra */ case 7: case 11: case 13: case 14: /* pair match in all possible combinations */ case 15: break; /* bad match - exit */ default: return(0); } } /* if only one type to match - any match at all will do */ if (a || b) if (!mask) return(0); #if DEBUG_PAIR_MATCH printf("accepted [%d][%d] as a match.\n",i,j); #endif return(1); } /***************************/ /* geometry search - bonds */ /***************************/ #define DEBUG_BOND_SEARCH 0 void measure_bond_setup(struct model_pak *model) { const gchar *label[2]; /* checks */ g_assert(model != NULL); label[0] = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(match1)->entry)); label[1] = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(match2)->entry)); measure_bond_search(label, measure_min_12, measure_max_12, model); meas_graft_model(model); } /*******************************/ /* geometry search - distances */ /*******************************/ #define DEBUG_DIST_SEARCH 0 void measure_dist_setup(gint type, struct model_pak *model) { const gchar *label[2]; /* checks */ g_assert(model != NULL); /* get pattern matching entries */ label[0] = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(match1)->entry)); label[1] = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(match2)->entry)); measure_distance_search(label, type, measure_min_12, measure_max_12, model); meas_graft_model(model); } /*********************************/ /* geometry search - bond angles */ /*********************************/ #define DEBUG_ANGLE_SEARCH 0 void measure_bangle_setup(struct model_pak *model) { const gchar *label[3]; g_assert(model != NULL); /* get pattern matching entries */ label[0] = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(match1)->entry)); label[1] = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(match2)->entry)); label[2] = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(match3)->entry)); measure_bangle_search(label, measure_min_a, measure_max_a, model); meas_graft_model(model); } /********************************/ /* geometry search - any angles */ /********************************/ void measure_angle_setup(struct model_pak *model) { gdouble measure_range[6]; const gchar *label[3]; /* checks */ g_assert(model != NULL); /* get pattern matching entries */ label[0] = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(match1)->entry)); label[1] = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(match2)->entry)); label[2] = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(match3)->entry)); VEC2SET(&measure_range[0], measure_min_12, measure_max_12); VEC2SET(&measure_range[2], measure_min_23, measure_max_23); VEC2SET(&measure_range[4], measure_min_a, measure_max_a); measure_angle_search(label, measure_range, model); meas_graft_model(model); } /******************************/ /* geometry search - torsions */ /******************************/ void measure_dihedral_setup(struct model_pak *data) { printf("Not implemented yet!\n"); } /********************************************/ /* geometry search callback - general setup */ /********************************************/ void measure_search_setup(void) { const gchar *text; struct model_pak *model; /* checks */ model = sysenv.active_model; if (!model) return; /* get the measurement type (ensure match mask ignores irrelevant bits) */ text = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(search)->entry)); if (g_ascii_strncasecmp(text, "Bonds", 5) == 0) measure_bond_setup(model); if (g_ascii_strncasecmp(text, "Distance", 8) == 0) measure_dist_setup(MEASURE_DISTANCE, model); if (g_ascii_strncasecmp(text, "Intermolecular", 14) == 0) measure_dist_setup(MEASURE_INTER, model); if (g_ascii_strncasecmp(text, "Bond Angles", 11) == 0) measure_bangle_setup(model); if (g_ascii_strncasecmp(text, "Angles", 6) == 0) measure_angle_setup(model); redraw_canvas(SINGLE); } /********************/ /* clean up on exit */ /********************/ void meas_cleanup(void) { /* NB: since the tree store is independant of the model's geom list */ /* it must be completely removed (and then restored) with the dialog */ gtk_tree_store_clear(meas_ts); meas_tv = NULL; meas_ts = NULL; } /**************************************/ /* measurement commit change callback */ /**************************************/ #define DEBUG_MEASURE_VALUE_CHANGED 0 void measure_value_changed(GtkWidget *w, gpointer dummy) { gdouble old, new, delta; gdouble v[3], o[3], mat[9]; gpointer measure; GSList *list, *list1, *list2; GtkTreeSelection *selection; GtkTreeModel *treemodel; GtkTreeIter iter; struct core_pak *core, *core1, *core2, *core3, *core4; struct shel_pak *shel; struct model_pak *model; /* CURRENT */ selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(meas_tv)); if (gtk_tree_selection_get_selected(selection, &treemodel, &iter)) { gtk_tree_model_get(treemodel, &iter, MEAS_MODEL, &model, MEAS_POINTER, &measure, -1); if (measure && model) { #if DEBUG_MEASURE_VALUE_CHANGED printf("old value: [%s]\n", measure_value_get(measure)); printf("new value: [%s]\n", gtk_entry_get_text(GTK_ENTRY(entry))); #endif old = str_to_float(measure_value_get(measure)); new = str_to_float(gtk_entry_get_text(GTK_ENTRY(entry))); delta = new - old; switch (measure_type_get(measure)) { case MEASURE_BOND: /* TODO - check that atoms are bonded */ list1 = measure_cores_get(measure); core1 = g_slist_nth_data(list1, 0); core2 = g_slist_nth_data(list1, 1); ARR3SET(v, core2->x); ARR3SUB(v, core1->x); /* TODO - get the PBC adjusted offset vector */ /* ARR3SUB(v, bond->offset); */ vecmat(model->latmat, v); normalize(v, 3); VEC3MUL(v, delta); vecmat(model->ilatmat, v); #if DEBUG_MEASURE_VALUE_CHANGED P3VEC("offset: ", v); printf("[%s] - [%s]\n", core1->atom_label, core2->atom_label); #endif connect_fragment_init(model); list2 = connect_fragment_get(core1, core2, model); #if DEBUG_MEASURE_VALUE_CHANGED printf("Adjusting position of %d atoms.\n", g_slist_length(list2)); #endif for (list=list2 ; list ; list=g_slist_next(list)) { core = list->data; /* adjust core position via delta scaled vector */ /* NB: the vector needs to take PBCs into account */ #if DEBUG_MEASURE_VALUE_CHANGED printf(" + [%s]\n", core->atom_label); #endif ARR3ADD(core->x, v); } break; case MEASURE_ANGLE: list1 = measure_cores_get(measure); core = g_slist_nth_data(list1, 0); core1 = g_slist_nth_data(list1, 1); core2 = g_slist_nth_data(list1, 2); compute_normal(v, core->x, core1->x, core2->x); /* cartesian offset to move middle core to origin */ ARR3SET(o, core1->x); vecmat(model->latmat, o); /* convert to radians and build an appropriate rotation matrix */ delta *= D2R; matrix_v_rotation(mat, v, delta); connect_fragment_init(model); list2 = connect_fragment_get(core1, core2, model); #if DEBUG_MEASURE_VALUE_CHANGED printf("Adjusting position of %d atoms.\n", g_slist_length(list2)); #endif for (list=list2 ; list ; list=g_slist_next(list)) { core = list->data; /* transform */ vecmat(model->latmat, core->x); ARR3SUB(core->x, o); vecmat(mat, core->x); ARR3ADD(core->x, o); vecmat(model->ilatmat, core->x); /* assoc. shell? */ if (core->shell) { shel = core->shell; /* transform */ vecmat(model->latmat, shel->x); ARR3SUB(core->x, o); vecmat(mat, shel->x); ARR3ADD(core->x, o); vecmat(model->ilatmat, shel->x); } } break; case MEASURE_TORSION: list1 = measure_cores_get(measure); core2 = g_slist_nth_data(list1, 1); core3 = g_slist_nth_data(list1, 2); core4 = g_slist_nth_data(list1, 3); ARR3SET(v, core3->x); ARR3SUB(v, core2->x); normalize(v, 3); /* cartesian offset to move middle core to origin */ ARR3SET(o, core3->x); vecmat(model->latmat, o); /* convert to radians and build an appropriate rotation matrix */ delta *= -D2R; matrix_v_rotation(mat, v, delta); connect_fragment_init(model); list2 = connect_fragment_get(core3, core4, model); for (list=list2 ; list ; list=g_slist_next(list)) { core = list->data; /* transform */ vecmat(model->latmat, core->x); ARR3SUB(core->x, o); vecmat(mat, core->x); ARR3ADD(core->x, o); vecmat(model->ilatmat, core->x); /* assoc. shell? */ if (core->shell) { shel = core->shell; /* transform */ vecmat(model->latmat, shel->x); ARR3SUB(core->x, o); vecmat(mat, shel->x); ARR3ADD(core->x, o); vecmat(model->ilatmat, shel->x); } } break; default: gui_text_show(WARNING, "Sorry, can't adjust this measurement.\n"); return; } } } /* update */ coords_compute(model); connect_refresh(model); measure_update_single(measure, model); measure_update_selected(measure, model); gui_refresh(GUI_CANVAS); } /*****************************/ /* manually add measurements */ /*****************************/ void meas_manual_page(GtkWidget *box) { GtkWidget *frame, *vbox, *hbox, *label; /* Frame - type */ frame = gtk_frame_new (NULL); gtk_box_pack_start(GTK_BOX(box),frame,FALSE,FALSE,0); gtk_container_set_border_width(GTK_CONTAINER(frame), PANEL_SPACING); vbox = gtk_vbox_new(TRUE, PANEL_SPACING); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), PANEL_SPACING); gui_button_x("Measure distances", gtk_mode_switch, GINT_TO_POINTER(DIST_INFO), vbox); gui_button_x("Measure bonds", gtk_mode_switch, GINT_TO_POINTER(BOND_INFO), vbox); gui_button_x("Measure angles", gtk_mode_switch, GINT_TO_POINTER(ANGLE_INFO), vbox); gui_button_x("Measure torsions", gtk_mode_switch, GINT_TO_POINTER(DIHEDRAL_INFO), vbox); frame = gtk_frame_new (NULL); gtk_box_pack_start(GTK_BOX(box),frame,FALSE,FALSE,0); gtk_container_set_border_width(GTK_CONTAINER(frame), PANEL_SPACING); vbox = gtk_vbox_new(FALSE, PANEL_SPACING); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(vbox), PANEL_SPACING); hbox = gtk_hbox_new(FALSE, PANEL_SPACING); gtk_box_pack_start(GTK_BOX(vbox),hbox,FALSE,FALSE,0); label = gtk_label_new("Measurement value"); gtk_box_pack_start(GTK_BOX(hbox),label,FALSE,FALSE,0); entry = gtk_entry_new(); gtk_box_pack_end(GTK_BOX(hbox),entry,FALSE,FALSE,0); g_signal_connect(GTK_OBJECT(entry), "activate", GTK_SIGNAL_FUNC(measure_value_changed), NULL); } /*******************************************/ /* search mode changed - update dist range */ /*******************************************/ void search_type_changed(GtkWidget *w, gpointer *data) { const gchar *tmp; tmp = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(search)->entry)); /* default atom pair matching */ gtk_widget_set_sensitive(GTK_WIDGET(match3), FALSE); gtk_widget_set_sensitive(GTK_WIDGET(spin3), FALSE); gtk_widget_set_sensitive(GTK_WIDGET(spin4), FALSE); gtk_widget_set_sensitive(GTK_WIDGET(spin5), FALSE); gtk_widget_set_sensitive(GTK_WIDGET(spin6), FALSE); if (g_ascii_strncasecmp(tmp, "Angles", 6) == 0) { gtk_widget_set_sensitive(GTK_WIDGET(match3), TRUE); gtk_widget_set_sensitive(GTK_WIDGET(spin3), TRUE); gtk_widget_set_sensitive(GTK_WIDGET(spin4), TRUE); gtk_widget_set_sensitive(GTK_WIDGET(spin5), TRUE); gtk_widget_set_sensitive(GTK_WIDGET(spin6), TRUE); } if (g_ascii_strncasecmp(tmp, "Bond Angles", 11) == 0) { gtk_widget_set_sensitive(GTK_WIDGET(match3), TRUE); gtk_widget_set_sensitive(GTK_WIDGET(spin3), TRUE); gtk_widget_set_sensitive(GTK_WIDGET(spin4), TRUE); gtk_widget_set_sensitive(GTK_WIDGET(spin5), TRUE); gtk_widget_set_sensitive(GTK_WIDGET(spin6), TRUE); } } /***************/ /* search page */ /***************/ void meas_search_page(GtkWidget *box) { GtkWidget *frame, *hbox, *table, *button, *label; GList *match_list=NULL, *search_list=NULL; /* main frame */ frame = gtk_frame_new (NULL); gtk_box_pack_start(GTK_BOX(box),frame,FALSE,FALSE,0); gtk_container_set_border_width(GTK_CONTAINER(frame), PANEL_SPACING/2); hbox = gtk_hbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(frame), hbox); search_list = g_list_append(search_list, "Bonds"); search_list = g_list_append(search_list, "Distances"); search_list = g_list_append(search_list, "Intermolecular"); search_list = g_list_append(search_list, "Bond Angles"); search_list = g_list_append(search_list, "Angles"); search = gtk_combo_new(); gtk_box_pack_start(GTK_BOX(hbox),search,FALSE,FALSE,0); gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(search)->entry), "Bonds"); gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(search)->entry), FALSE); gtk_combo_set_popdown_strings(GTK_COMBO(search), search_list); g_signal_connect(GTK_OBJECT(GTK_COMBO(search)->entry), "changed", GTK_SIGNAL_FUNC(search_type_changed), (gpointer *) search); button = gui_icon_button(GTK_STOCK_FIND, "Search ", measure_search_setup, NULL, NULL); gtk_box_pack_end(GTK_BOX(hbox),button,FALSE,FALSE,0); /* setup frame */ frame = gtk_frame_new(NULL); gtk_box_pack_start(GTK_BOX(box),frame,FALSE,FALSE,0); gtk_container_set_border_width(GTK_CONTAINER(frame), PANEL_SPACING/2); table = gtk_table_new(3, 4, FALSE); gtk_container_add(GTK_CONTAINER(frame), table); /* labels for top row */ label = gtk_label_new("Atom 1"); gtk_table_attach_defaults(GTK_TABLE(table),label,0,1,0,1); label = gtk_label_new("Atom 2"); gtk_table_attach_defaults(GTK_TABLE(table),label,1,2,0,1); label = gtk_label_new("Atom 3"); gtk_table_attach_defaults(GTK_TABLE(table),label,2,3,0,1); /* construct combo options */ match_list = g_list_append(match_list, "Any"); match_list = g_list_append(match_list, "Selected"); /* atom match combo boxes */ match1 = gtk_combo_new(); gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(match1)->entry), "Any"); gtk_combo_set_popdown_strings(GTK_COMBO(match1), match_list); gtk_table_attach_defaults(GTK_TABLE(table),match1,0,1,1,2); match2 = gtk_combo_new(); gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(match2)->entry), "Any"); gtk_combo_set_popdown_strings(GTK_COMBO(match2), match_list); gtk_table_attach_defaults(GTK_TABLE(table),match2,1,2,1,2); match3 = gtk_combo_new(); gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(match3)->entry), "Any"); gtk_combo_set_popdown_strings(GTK_COMBO(match3), match_list); gtk_table_attach_defaults(GTK_TABLE(table),match3,2,3,1,2); /* measurement ranges */ label = gtk_label_new(" 1 - 2 cutoffs "); gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 2, 3); hbox = gtk_hbox_new(TRUE, 0); gtk_table_attach_defaults(GTK_TABLE(table),hbox,0,1,3,4); spin1 = gui_direct_spin(NULL, &measure_min_12, 0.1, 100.0, 0.1, NULL, NULL, NULL); gtk_box_pack_start(GTK_BOX(hbox), spin1, TRUE, TRUE, 0); spin2 = gui_direct_spin(NULL, &measure_max_12, 0.1, 100.0, 0.1, NULL, NULL, NULL); gtk_box_pack_start(GTK_BOX(hbox), spin2, TRUE, TRUE, 0); label = gtk_label_new(" 2 - 3 cutoffs "); gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 2, 3); hbox = gtk_hbox_new(TRUE, 0); gtk_table_attach_defaults(GTK_TABLE(table),hbox,1,2,3,4); spin3 = gui_direct_spin(NULL, &measure_min_23, 0.1, 100.0, 0.1, NULL, NULL, NULL); gtk_box_pack_start(GTK_BOX(hbox), spin3, TRUE, TRUE, 0); spin4 = gui_direct_spin(NULL, &measure_max_23, 0.1, 100.0, 0.1, NULL, NULL, NULL); gtk_box_pack_start(GTK_BOX(hbox), spin4, TRUE, TRUE, 0); label = gtk_label_new(" Angle cutoffs "); gtk_table_attach_defaults(GTK_TABLE(table), label, 2, 3, 2, 3); hbox = gtk_hbox_new(TRUE, 0); gtk_table_attach_defaults(GTK_TABLE(table),hbox,2,3,3,4); spin5 = gui_direct_spin(NULL, &measure_min_a, 0.0, 180.0, 5.0, NULL, NULL, NULL); gtk_box_pack_start(GTK_BOX(hbox), spin5, TRUE, TRUE, 0); spin6 = gui_direct_spin(NULL, &measure_max_a, 0.0, 180.0, 5.0, NULL, NULL, NULL); gtk_box_pack_start(GTK_BOX(hbox), spin6, TRUE, TRUE, 0); /* I don't like doing this, but the default size is */ /* much larger than it needs to be & wastes too much space */ gtk_widget_set_size_request(match1, 13*sysenv.gtk_fontsize, -1); gtk_widget_set_size_request(match2, 13*sysenv.gtk_fontsize, -1); gtk_widget_set_size_request(match3, 13*sysenv.gtk_fontsize, -1); gtk_widget_set_size_request(search, 13*sysenv.gtk_fontsize, -1); gtk_table_set_row_spacings(GTK_TABLE(table), PANEL_SPACING); gtk_table_set_col_spacings(GTK_TABLE(table), PANEL_SPACING); /* default measurement is bonds */ gtk_widget_set_sensitive(GTK_WIDGET(match3), FALSE); gtk_widget_set_sensitive(GTK_WIDGET(spin3), FALSE); gtk_widget_set_sensitive(GTK_WIDGET(spin4), FALSE); gtk_widget_set_sensitive(GTK_WIDGET(spin5), FALSE); gtk_widget_set_sensitive(GTK_WIDGET(spin6), FALSE); } /***********************************/ /* select a particular measurement */ /***********************************/ void measure_select(gpointer measurement, struct model_pak *model) { GSList *list; for (list=model->measure_list ; list ; list=g_slist_next(list)) { if (measurement == list->data) { /* selected measurement */ measure_colour_set(1.0, 1.0, 0.0, list->data); } else { /* reset other measurements */ measure_colour_set(0.8, 0.8, 0.8, list->data); } } redraw_canvas(SINGLE); } /**********************************/ /* measurement selection callback */ /**********************************/ void measure_select_changed(GtkTreeSelection *selection, gpointer data) { GtkTreeModel *treemodel; GtkTreeIter iter; gpointer measure; struct model_pak *model; gtk_entry_set_text(GTK_ENTRY(entry), ""); if (gtk_tree_selection_get_selected(selection, &treemodel, &iter)) { gtk_tree_model_get(treemodel, &iter, MEAS_MODEL, &model, -1); gtk_tree_model_get(treemodel, &iter, MEAS_POINTER, &measure, -1); if (measure && model) { measure_select(measure, model); gtk_entry_set_text(GTK_ENTRY(entry), measure_value_get(measure)); /* CURRENT - only allow adjustments to supported editables */ switch (measure_type_get(measure)) { case MEASURE_BOND: case MEASURE_ANGLE: case MEASURE_TORSION: gtk_widget_set_sensitive(GTK_WIDGET(entry), TRUE); break; default: gtk_widget_set_sensitive(GTK_WIDGET(entry), FALSE); break; } } } } /**************************/ /* geometric measurements */ /**************************/ void gui_measure_dialog(void) { gint i; gpointer dialog; gchar *titles[] = {" Name ", " Constituent atoms ", " Value "}; GSList *list; GtkWidget *window, *swin, *notebook; GtkWidget *frame, *vbox, *hbox, *label; GtkTreeSelection *select; GtkCellRenderer *renderer; GtkTreeViewColumn *column; struct model_pak *model; /* request dialog */ dialog = dialog_request(GEOMETRY, "Measurements", NULL, meas_cleanup, NULL); if (!dialog) return; window = dialog_window(dialog); gtk_window_set_default_size(GTK_WINDOW(window), 250, 550); /* notebook */ notebook = gtk_notebook_new(); gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_TOP); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox),notebook,FALSE,FALSE,0); gtk_notebook_set_show_border(GTK_NOTEBOOK(notebook), TRUE); /* manual measurement */ vbox = gtk_vbox_new(FALSE, PANEL_SPACING); label = gtk_label_new(" Manual "); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, label); meas_manual_page(vbox); /* searching */ vbox = gtk_vbox_new(FALSE, PANEL_SPACING); label = gtk_label_new(" Search "); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, label); meas_search_page(vbox); /* measurements viewing pane */ frame = gtk_frame_new ("Label list"); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(window)->vbox),frame,TRUE,TRUE,0); gtk_container_set_border_width(GTK_CONTAINER(frame), PANEL_SPACING/2); vbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(frame), vbox); gtk_container_set_border_width(GTK_CONTAINER(GTK_BOX(vbox)), PANEL_SPACING); swin = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_box_pack_start(GTK_BOX(vbox), swin, TRUE, TRUE, 0); /* NEW - tree view */ /* underlying data storage */ /* 3 strings - data, 1 pointer - the measurement itself */ /* NB: model assoc. is passed with the selection callback */ if (!meas_ts) meas_ts = gtk_tree_store_new(MEAS_NCOLS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_POINTER); meas_tv = gtk_tree_view_new_with_model(GTK_TREE_MODEL(meas_ts)); gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(swin), meas_tv); select = gtk_tree_view_get_selection(GTK_TREE_VIEW(meas_tv)); g_signal_connect(G_OBJECT(select), "changed", G_CALLBACK(measure_select_changed), NULL); /* set up column renderers */ for (i=0 ; i<=MEAS_VALUE ; i++) { renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes (titles[i], renderer, "text", i, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(meas_tv), column); } /* list modification buttons */ hbox = gtk_hbox_new(TRUE, 10); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, PANEL_SPACING); gui_button(" Select ", measure_select_all, NULL, hbox, TT); gui_button(" Delete ", meas_prune_selected, NULL, hbox, TT); gui_button(" Dump ", meas_dump_all, NULL, hbox, TT); /* terminating button */ gui_stock_button(GTK_STOCK_CLOSE, dialog_destroy, dialog, GTK_DIALOG(window)->action_area); /* done */ gtk_widget_show_all(window); /* refresh all measurements */ for (list=sysenv.mal ; list ; list=g_slist_next(list)) { model = list->data; meas_graft_model(model); } gtk_tree_view_expand_all(GTK_TREE_VIEW(meas_tv)); }