/*
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 <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#ifndef __WIN32
#include <sys/times.h>
#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));
}
syntax highlighted by Code2HTML, v. 0.9.1