/** Utilities for drawing GTK windows.
 *  Transcripted from Motif to GTK+ by P. Vincent to
 *  Replace  motifutils.c   
 *  See also the gw_* files
 */

#include <stdlib.h>
#include <string.h>
#include <gtk/gtk.h>

#include "defines.h"
#include "globals.h"
#include "parser.h"
#include "noxprotos.h"
#include "utils.h"
#include "nn_cmd.h"

#include "t1fonts.h"

#include "gw_choice.h"
#include "gw_list.h"
#include "gw_pannel.h"

#include "gg_gtkinc.h"
#include "gg_events.h"
#include "gg_gtkdrv.h"
#include "gg_protos.h"
#include "ng_objects.h"

#include "ge_tree.h"
#include "ge_protos.h"
 
extern GtkWidget *gg_main_window;

extern GdkColor  bleuciel ,wheat;

#define LIST_WIDTH  250
#define GRAPH_LIST_HEIGHT 100
#define SET_LIST_HEIGHT   250


/* main window parameters */
static gint gg_wx ,gg_wy ,gg_ww ,gg_wh ,gg_depth;


void gg_to_be_done_CB ( GtkWidget *w ,gpointer p)
{
  printf(" Callback A FAIRE \n");
}


/* lookup table to determine if character is a floating point digit 
 * only allowable char's [0-9.eE]
 */
static unsigned char fpdigit[256] = {  
			      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
			      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
			      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
			      1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
			      0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
			      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
			      0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
			      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
			      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
			      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
			      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
			      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
			      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
			      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
			      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
			      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };


extern GdkColor gg_colors[MAXCOLORS];  

static GtkWidget *w_box ,*w_label;

/**
 * Unified set/get for gint and gdouble  variables 
 */
void gg_set_int (GtkWidget *w ,gint val) 
{
  gint old_val;
  if (w == NULL) {
    fprintf (stderr ,"gg_set_int called with NULL widget\n");
    return;
  }

  /* To avoid emiting signals not needed */
  old_val = gg_get_int (w);
  if (val == old_val) return;

  if (GTK_IS_COMBO_BOX (w)) {
    gtk_combo_box_set_active (GTK_COMBO_BOX (w) ,val);
  } else if (GTK_IS_TOGGLE_BUTTON (w)) {
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w) ,val);    
  } else if (GTK_IS_SPIN_BUTTON (w)) {
    gtk_spin_button_set_value (GTK_SPIN_BUTTON (w) ,(gdouble) val);
  } else if (GTK_IS_RANGE (w)) {
    gtk_range_set_value (GTK_RANGE(w) ,(gdouble) val);
  } else if (GTK_IS_CHECK_MENU_ITEM (w)) {
    gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w) ,val);
  } else if (IS_GW_LIST (w)) {
    gw_list_select (GW_LIST (w) ,val);
  } else if (IS_GW_CHOICE(w)) {
    gw_choice_set (GW_CHOICE (w) ,val);
  } else {
    fprintf (stderr ,"gg_set_int: unknown type GtkWidget\n");
  }
}

int gg_get_int (GtkWidget *w) 
{
  gint retval = 0;
  if (w == NULL) {
    fprintf (stderr ,"gg_get_int called with NULL widget\n");
    return 0;
  }

  if (GTK_IS_COMBO_BOX (w)) {
    retval = gtk_combo_box_get_active (GTK_COMBO_BOX(w));
  } else if (GTK_IS_TOGGLE_BUTTON (w)) {
    retval = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(w));  
  } else if (GTK_IS_SPIN_BUTTON (w)) {
    retval =  gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (w));
  } else if (GTK_IS_RANGE (w)) {
    retval = (gint) gtk_range_get_value (GTK_RANGE(w));
  } else if (GTK_IS_CHECK_MENU_ITEM (w)) {
    retval =  gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (w));
  } else if (GTK_IS_ENTRY (w)) {
    if (gg_evalexpri (w ,&retval) == RETURN_FAILURE) {
      fprintf (stderr ,"gg_get_int: internal error in gg_evalexpri \n");
      retval = 0;
    }
  } else if (IS_GW_LIST (w)) {
    if (gw_list_count_rows_selected (GW_LIST (w)) != 1) {
      fprintf (stderr ,"gg_get_int: with gw_list works only with one selected row\n");
    } else {
      retval = gw_list_get_first_selected_row_num (GW_LIST (w));
    }
  } else if (IS_GW_CHOICE(w)) {
    retval = gw_choice_get (GW_CHOICE (w));
  } else {
    fprintf (stderr ,"gg_get_int: unknown type GtkWidget\n");
  }
  return (retval);
}

void gg_set_dble (GtkWidget *w ,gdouble d)
{
  gdouble x1 ,x2 ,x12 ,old_d;
  gint ra;
  if (w == NULL) {
    fprintf (stderr ,"gg_set_dble called with NULL widget\n");
    return;
  }

  /* To avoid emiting signals not needed */
  old_d = gg_get_dble (w);
  if (d == old_d) return;

  if (GTK_IS_SPIN_BUTTON (w)) {
    gtk_spin_button_get_range (GTK_SPIN_BUTTON (w) ,&x1 ,&x2);
    x12 = fabs (x2 - x1);
    if (d < x1) {
      ra = (int) ((x1 - d) / x12) + 1;
      x1 = x1 - ra * x12;
      gtk_spin_button_set_range (GTK_SPIN_BUTTON (w) ,x1 ,x2);
    } else if (d > x2) {
      ra = (int) ((d - x2) / x12) + 1;
      x2 = x2 + ra * x12;
      gtk_spin_button_set_range (GTK_SPIN_BUTTON (w) ,x1 ,x2);
    }
    gtk_spin_button_set_value (GTK_SPIN_BUTTON (w) ,d);
  } else if (GTK_IS_RANGE (w)) {
    gtk_range_set_value (GTK_RANGE(w) ,d);
  } else {
    fprintf (stderr ,"gg_set_dble: unknown type GtkWidget\n");
  }
}

gdouble gg_get_dble (GtkWidget *w)
{
  gdouble retval;

  if (w == NULL) {
    fprintf (stderr ,"gg_get_dble called with NULL widget\n");
    return 0.0;
  }
  if (GTK_IS_SPIN_BUTTON (w)) {
    retval = gtk_spin_button_get_value (GTK_SPIN_BUTTON (w));
  } else if (GTK_IS_RANGE (w)) {
    retval = gtk_range_get_value (GTK_RANGE(w));

  } else if (GTK_IS_ENTRY (w)) {
    if (gg_evalexpr (w ,&retval) == RETURN_FAILURE) {
      fprintf (stderr ,"gg_get_dble: internal error in gg_evalexpr \n");
      retval = 0.0;
    }
  } else {
      fprintf (stderr ,"gg_get_dble: unknown type GtkWidget \n");
    retval = 0.0;
  }
  return (retval);
}

/**
 * Replace  SetSensitive      motifutils.c  169
 */
void gg_SetSensitive (GtkWidget *w, gboolean onoff)
{
  gtk_widget_set_sensitive (GTK_WIDGET(w) ,onoff);
}


void gg_spin_time_store_CB (GtkSpinButton *w ,gpointer p)
{
  GTimeVal temps;
  g_get_current_time (&temps);   /* like gettimeofday() */
  nn_cmd_set_time ((void*) w ,temps.tv_sec ,temps.tv_usec);
}


/**********   create a  S E L E C T  L I S T  ***************/

/**
 *  Replace  GetSingleListChoice   motifutils.c 607
 */
gboolean gg_GetSingleListChoice (Gw_list *list ,int *value)
{
  GtkTreeSelection *sel;
  int n;

  sel = gtk_tree_view_get_selection (GTK_TREE_VIEW  (list->view));
  n = gtk_tree_selection_count_selected_rows (sel);
  *value = gw_list_get_first_selected_row_num (list);
  return (n == 1);
}


/************ C R E A T E   F I L E   P O P U P ***************  
 *  Replace  CreateFileSelectionBox  motifutils.c 1048
 * A FAIRE:  rajouter un bouton pour définir le répertoire
 *           comme répertoire courant pour grace
 */
GtkWidget *gg_create_file_popup (char *title ,char *filter
				 ,gint popup_width ,gint popup_height
				 ,GtkWidget *extra_widget
				 ,gboolean save)
{
  GtkWidget *file_select;
  GtkFileFilter *filtre_cmd ,*filtre_tout ,*filtre_hidden;  

  if (save) {
    file_select = gtk_file_chooser_dialog_new (title,
					       NULL,
					       GTK_FILE_CHOOSER_ACTION_SAVE,
					       GTK_STOCK_SAVE  , GTK_RESPONSE_ACCEPT,
					       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
					       NULL);
  } else {
    file_select = gtk_file_chooser_dialog_new (title,
					       NULL,
					       GTK_FILE_CHOOSER_ACTION_OPEN,
					       GTK_STOCK_OPEN  , GTK_RESPONSE_ACCEPT,
					       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
					       NULL);
  }
  if (popup_width > 0)
    gtk_widget_set_size_request (file_select ,popup_width ,popup_height);
  gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (file_select)
						  ,TRUE);
  /* Filtres */ 
  if (filter != NULL) {
    filtre_cmd = gtk_file_filter_new (); 
    gtk_file_filter_set_name (filtre_cmd ,filter);
    gtk_file_filter_add_pattern (filtre_cmd ,filter);
    gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(file_select) ,filtre_cmd);
  }

  filtre_tout = gtk_file_filter_new (); 
  gtk_file_filter_set_name (filtre_tout ,"All files");
  gtk_file_filter_add_pattern (filtre_tout ,"*");
  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(file_select) ,filtre_tout);

  filtre_hidden = gtk_file_filter_new (); 
  gtk_file_filter_set_name (filtre_hidden ,"Don't show hidden");
  gtk_file_filter_add_pattern (filtre_hidden ,"[^.]*");
  gtk_file_chooser_add_filter (GTK_FILE_CHOOSER(file_select) ,filtre_hidden);

  if (extra_widget) {
    gtk_file_chooser_set_extra_widget ( GTK_FILE_CHOOSER(file_select) ,extra_widget);
    gtk_widget_show_all (extra_widget);
  }

  gtk_widget_show_all (file_select);
  return (file_select);
}
    
/**
 *  Replace  init_option_menus    motifutils.c 1197
 */

gint gg_get_depth (void)
{
  gdk_window_get_geometry (gg_main_window->window
			   ,&gg_wx ,&gg_wy ,&gg_ww ,&gg_wh ,&gg_depth);
  return (gg_depth);
}

/*************** R E S T R I C T   T O   R E G I O N ****************/

/**
 *  Replace CreateRestrictionChoice  motifutils.c 1329
 */
gg_RestrictionStructure *gg_CreateRestrictionChoice (GtkWidget *parent ,char *s)
{
  gg_RestrictionStructure *retval;

  retval = xmalloc (sizeof (gg_RestrictionStructure));
  retval->frame = gg_frame (parent ,s ,&wheat ,1 ,0);
  retval->r_sel = gg_combo_new (retval->frame ,"Restriction:" ,7
					,"None"            /* 0=RESTRICT_NONE +1 */
					,"Region 0"    	   /* 1=RESTRICT_REG0 +1 */
					,"Region 1"    	   /* 2=RESTRICT_REG1 +1 */
					,"Region 2"    	   /* 3=RESTRICT_REG2 +1 */
					,"Region 3"    	   /* 4=RESTRICT_REG3 +1 */
					,"Region 4"    	   /* 5=RESTRICT_REG4 +1 */
					,"Inside graph");  /* 6=RESTRICT_WORLD+8 */
  retval->negate = gg_check_new (retval->frame ,"Negated");
  gtk_widget_show_all (retval->frame);
  return retval;
}


/************ M A N A G E   G R A P H    L I S T S ***************/

/* A graph_selector is a Gw_list or the Ge_tree of the explorer popup
 * 
 *  Is it possible to fix this ?
 *   menu3 are used  for menus raised by mouse Button3 in graph_list or sets_list.
 *   When I try to use a unique GraphMenu3Struct in gg_gtkutils.c 
 *   the menu3 is poped out as wanted but the actions are performed on
 *   the items selected with the first list, not the list clicked,
 *   and I don't know why. Thus I define a GraphMenu3Struct for
 *   each list: this is dumb.
 *   Idem for sets and SetsMenu3Struct
 */
static int ngraph_selectors = 0;
static Gw_list *graph_selectors[20];

void gg_graph_add_selector (Gw_list *w)
{
  graph_selectors [ngraph_selectors] = w;
  ngraph_selectors++;
}

void gg_graph_update_list (Gw_list *wl)
{
  char buf[64];  
  GtkTreeModel *model;
  GtkTreeIter iter;
  gint i ,j;

  if (wl != NULL) {
    model = GTK_TREE_MODEL(wl->model);
    if (gtk_tree_model_get_iter_first (model ,&iter)) {
      do {
	gtk_tree_model_get (model, &iter ,GW_LIST_NUM_COL  ,&i ,-1);
	if (i < number_of_graphs ()) {
	  sprintf(buf, "(%c) G%d (%d sets)",
		  is_graph_hidden(i) ? '-':'+', i, number_of_sets(i));
	  gw_list_set (wl ,i ,buf ,i ,is_graph_hidden(i) );
	} else {
	  while (gtk_list_store_remove (wl->store ,&iter));
	  break;
	}
      } while (gtk_tree_model_iter_next (model ,&iter));
    }
    if (i+1 <  number_of_graphs ()) {
      for (j=i+1; j<number_of_graphs(); j++) {
	sprintf(buf, "(%c) G%d (%d sets)",
		is_graph_hidden(j) ? '-':'+', j ,number_of_sets (j));
	gw_list_append (wl ,buf ,j ,is_graph_hidden(j) );
      }
    }
  }
}

void gg_graph_update_all_lists (void)
{
  int i;
  for (i=0; i<ngraph_selectors; i++) {
    gg_graph_update_list (graph_selectors[i]);
  }
}

/**
 *  Replace graph_menu_cb           motifutils.c 1442
 */

static int iprec = -1;
void gg_graph_menu3_actions (Menu3action action ,int n ,int i ,int j)
{
  char buf[64];
  char *title;
  switch (action) {
  case MenuFocusCB:
    if (n != 1) return;
    gg_switch_current_graph (i);
    break;
  case MenuHideCB:
    set_graph_hidden (i ,TRUE);
    break;
  case MenuShowCB:
    set_graph_hidden (i ,FALSE);
    break;
  case MenuDuplicateCB:
    duplicate_graph (i);
    break;
  case MenuKillCB:
    if (yesno_i (buf ,"Kill graph" ,i)) kill_graph (i); 
    break;
  case MenuUseFontTool:
    title = NULL;
    get_graph_title (i ,&title);
    gg_fontwin_create (NULL ,title);
    break;
  case MenuNewCB:
    set_graph_active (number_of_graphs());
    break;
  case MenuCopy12CB:
  case MenuCopy21CB:
  case MenuMove12CB:
  case MenuMove21CB:
  case MenuSwapCB:
    if (n != 2) return;
    if (j == 0) iprec = i;
    if (j != 1) break;
    switch (action) {
    case MenuCopy12CB:
      if (yesno_i (buf ,"Overwrite G" ,i)) copy_graph (iprec ,i);
      break;
    case MenuCopy21CB:
      if (yesno_i (buf ,"Overwrite G" ,iprec)) copy_graph (i ,iprec);
      break;
    case MenuMove12CB:
      if (yesno_i (buf ,"Replace G" ,i))  move_graph (iprec ,i);
      break;
    case MenuMove21CB:
      if (yesno_i (buf ,"Replace G" ,iprec)) move_graph (i ,iprec);
      break;
    case MenuSwapCB:
      swap_graph (iprec, i);
      break;
    default:
      break;
    }
    break;
  case MenuGraphsUpdate:
    
    break;
  default:
    sprintf (buf ,"gg_graph_menu3_actions internal error: i=%d \n" ,i);
    errmsg (buf);
    break;
  }
  ge_update_explorer    ();
}

/**
 *  A callback called by the contextual menu for graphs
 *  Compare to ge_graph_menu3_CB
 */
void gg_graph_menu3_CB  (GtkWidget *graph_list ,gpointer pact)
{
  gint action = GPOINTER_TO_INT (pact);
  Gw_list *liste;
  GtkTreeModel *model;
  GtkTreeIter iter;
  GtkTreeSelection *select;
  GList *lpath = NULL;
  gint id ,j ,n;
  if (IS_GW_LIST(graph_list)) {
    liste = GW_LIST(graph_list);
    model = GTK_TREE_MODEL(liste->model);
    select = gtk_tree_view_get_selection (GTK_TREE_VIEW (GW_LIST(graph_list)->view));
    n = gtk_tree_selection_count_selected_rows (select);
    lpath  = gtk_tree_selection_get_selected_rows (select ,&model);
    /* the list must be traversed un reverse order */
    lpath = g_list_last (lpath);
    j = 0;
    while (lpath != NULL) {
      if (gtk_tree_model_get_iter (model ,&iter ,lpath->data)) {
	gtk_tree_model_get (model ,&iter ,GW_LIST_NUM_COL ,&id ,-1);
	gg_graph_menu3_actions (action ,n ,id ,j);
	lpath = g_list_previous (lpath);
	j++;
      }
    }
  }
  gg_update_all ();
  gg_drawgraph();
}

/**
 *  Called with CB = ge_graph_menu3_CB
 *   Replace  CreateGraphPopupEntries    motifutils.c 1616
 */  
GraphMenu3Struct *gg_CreateGraphPopupEntries (GraphMenu3Struct *p ,GtkWidget *list
					       ,void (*CB) (GtkWidget *graph_list ,gpointer p))
{
  GtkWidget *menu;

  if (p  == NULL) {
    p = xmalloc (sizeof (GraphMenu3Struct));
    menu = gtk_menu_new ();
    p->popup 	 = menu;
    p->focus 	 = gg_CreateMenuButton (menu ,"Focus to"      ,'F'  ,list  ,CB ,MenuFocusCB);
    p->hide  	 = gg_CreateMenuButton (menu ,"Hide"          ,'H'  ,list  ,CB ,MenuHideCB);
    p->show  	 = gg_CreateMenuButton (menu ,"Show"          ,'S'  ,list  ,CB ,MenuShowCB);
    p->duplicate = gg_CreateMenuButton (menu ,"Duplicate"     ,'D'  ,list  ,CB ,MenuDuplicateCB);
    p->kill      = gg_CreateMenuButton (menu ,"Kill"          ,'K'  ,list  ,CB ,MenuKillCB);
    gg_CreateMenuSeparator (menu);
    p->copy12 	 = gg_CreateMenuButton (menu ,"Copy 1 to 2"   ,'\0' ,list ,CB ,MenuCopy12CB);
    p->copy21 	 = gg_CreateMenuButton (menu ,"Copy 2 to 1"   ,'\0' ,list ,CB ,MenuCopy21CB);
    p->move12 	 = gg_CreateMenuButton (menu ,"Move 1 to 2"   ,'\0' ,list ,CB ,MenuMove12CB);
    p->move21 	 = gg_CreateMenuButton (menu ,"Move 2 to 1"   ,'\0' ,list ,CB ,MenuMove21CB);
//09    p->swap   	 = gg_CreateMenuButton (menu ,"Swap"          ,'w'  ,list ,CB ,MenuSwapCB);
    gg_CreateMenuSeparator (menu);
    p->update 	 = gg_CreateMenuButton (menu ,"Update graphs" ,'\0' ,list ,CB ,MenuGraphsUpdate);
    p->font_tool = gg_CreateMenuButton (menu ,"Use font tool" ,'\0' ,list ,CB ,MenuUseFontTool);
    gg_CreateMenuSeparator (menu);
    gg_CreateMenuButton (menu ,"Create new"  ,'C'  ,list ,CB ,MenuNewCB);
    gtk_widget_show_all (menu);
  }
  return (p);
}

/**
 *   Replace  graph_popup    motifutils.c  1657
 */  
void gg_graph_popup_menu3 (GraphMenu3Struct *p ,int n)
{
  gtk_widget_set_sensitive (p->focus     ,n == 1);
  gtk_widget_set_sensitive (p->show      ,n != 0);
  gtk_widget_set_sensitive (p->duplicate ,n != 0);
  gtk_widget_set_sensitive (p->kill      ,n != 0);
  gtk_widget_set_sensitive (p->copy12    ,n == 2);
  gtk_widget_set_sensitive (p->copy21    ,n == 2);
  gtk_widget_set_sensitive (p->move12    ,n == 2);
  gtk_widget_set_sensitive (p->move21    ,n == 2);
//09   gtk_widget_set_sensitive (p->swap      ,n == 2);
  gtk_widget_set_sensitive (p->font_tool ,n == 1);
  gtk_menu_popup (GTK_MENU(p->popup) ,NULL ,NULL ,NULL ,NULL ,3
		  ,gtk_get_current_event_time () );
}

void gg_graph_popup_menu3_CB (GtkWidget *list ,GdkEvent *event ,GraphMenu3Struct *p)
{
  GtkTreeSelection *sel;
  int n;
  if (event->type == GDK_BUTTON_PRESS) {
    if (event->button.button == 3) {
      sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (GW_LIST(list)->view));
      n  = gtk_tree_selection_count_selected_rows (sel);
      gg_graph_popup_menu3 (p ,n);
    }
  }
}


/**
 *   gg_CreateGraphChoice creates a graph_selector for Gw_list
 *     For the explorer, use ge_explorer_popup
 *     Replace CreateGraphChoice  motifutils.c  1826
 *     Use two hidden columns to store the graph number and hidden flag.
 *     *graph_menu3 MUST be NULLIFIED before call
 */
GtkWidget *gg_CreateGraphChoice (char *labelstr, int type_select
				  ,int width ,int height
				  ,void (* cb)(GtkTreeSelection *sel ,Gw_list *list)
				  ,GraphMenu3Struct **graph_menu3
			       )
{
  char *buf; 
  int gno ,i;
  GtkWidget *w;
  Gw_list *wl;
  GtkTreeSelection *sel;
  buf = malloc (128 * sizeof (char));
  gno = get_cg();
  w = gw_list_new ("Graphs" ,labelstr ,type_select);
  wl = GW_LIST(w);
  for (i=0; i<number_of_graphs(); i++) {
    sprintf(buf, "(%c) G%d (%d sets)",
	    is_graph_hidden(i) ? '-':'+', i ,number_of_sets (i));
    gw_list_append (wl ,buf ,i ,is_graph_hidden(i) );
  }
  if (width > 0)
    gtk_widget_set_size_request (w ,width ,height);
  if (cb != NULL) {
    sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (wl->view));
    g_signal_connect (sel ,"changed" ,G_CALLBACK (cb) ,w);
  }
  gg_graph_add_selector (wl);
  if (*graph_menu3 == NULL) {
    *graph_menu3 = gg_CreateGraphPopupEntries (*graph_menu3 ,w ,gg_graph_menu3_CB);
  }
  g_signal_connect (w ,"gw_list_event" ,G_CALLBACK (gg_graph_popup_menu3_CB) ,*graph_menu3);
  return (w);
}


 // update_set_selectors  motifutils.c 1912


/************ M A N A G E   S E T   L I S T ***************/

typedef struct _set_selector set_selector;
struct _set_selector {
  Gw_list *sets;
  gg_GraphSetStructure *gss;
};

static int nset_selectors = 0;
static set_selector set_selectors[40];

static gboolean sets_list_view_legend = TRUE;
static gboolean sets_list_view_comments = FALSE;
static gboolean show_hidden = TRUE;
static gboolean show_nodata = FALSE;


void gg_set_selector_add (Gw_list *sets ,gg_GraphSetStructure *gss)
{
  set_selectors[nset_selectors].sets   = sets;
  set_selectors[nset_selectors].gss = gss;
  nset_selectors++;
}


/**
 *  Replace  UpdateSetChoice   motifutils.c  1867
 */
static gchar *gg_update_sets_string (int gno ,int setno ,gchar *buf)
{
  if (sets_list_view_legend) {
    sprintf (buf, "(%c) G%d.S%d[%d][%d] \"%s\" ",  
	     is_set_hidden(gno, setno) ? '-':'+',
	     gno, setno, dataset_cols(gno, setno), getsetlength(gno, setno)
	     ,get_legend_string (gno ,setno));
  } else if (sets_list_view_comments) {
    sprintf (buf, "(%c) G%d.S%d[%d][%d] \"%s\" ",  
	     is_set_hidden(gno, setno) ? '-':'+',
	     gno, setno, dataset_cols(gno, setno), getsetlength(gno, setno)
	     ,getcomment (gno ,setno));
  } else {
    sprintf (buf, "(%c) G%d.S%d[%d][%d] ",  
	     is_set_hidden(gno, setno) ? '-':'+',
	     gno, setno, dataset_cols(gno, setno), getsetlength(gno, setno));
  }
  return (buf);
}

/**
 * Returns the gno if "sets" is in a GraphSetSelector, -1 if not
 */
static int gno_to_update_sets_list (Gw_list *sets)
{
  int i ,gno;
  gg_GraphSetStructure *gss;
  for (i = 0; i < nset_selectors; i++) {
    if (sets == set_selectors[i].sets) {
      gss = set_selectors[i].gss;
      if (gss == NULL) {
	/* sets list is not in GraphSetSelector */
	return get_cg ();
      } else {
	/* try to get the correct gno */
	gno = gw_list_get_first_selected_row_num (GW_LIST (gss->graphs)); 
	return gno;
      }
    }
  }
  /* Can't determine a valid graph */
  return -1;
}


/**
 *   Replace update_set_list   motifutils.c  3832
 *   and     UpdateSetChoice   motifutils.c  1867
 */
void gg_update_sets_list (Gw_list *wl)
{
  gint i ,gno;
  char *buf; 
  if (wl != NULL) {
    /* list is updated only if we can determine a valid gno */
    gno = gno_to_update_sets_list (wl);
    if (is_valid_gno (gno)) {
      gw_list_clear (wl);
      buf = malloc (256 * SIZEOF_CHAR);
      for (i = 0; i < number_of_sets (gno); i++) {
	if ((show_nodata == TRUE || is_set_active (gno, i) == TRUE) &&
	    (show_hidden == TRUE || is_set_hidden (gno, i) != TRUE )) {
	  gw_list_append (wl ,gg_update_sets_string (gno ,i ,buf)
			  ,i ,is_set_hidden(gno,i) );
	  ge_hidden_update (Q_Set ,i ,Q_Graph ,gno ,is_set_hidden(gno,i) );
	}
      }
    }
  }
}

void gg_update_all_sets_lists (void)
{
  int i;
  for (i = 0; i < nset_selectors; i++) {
    gg_update_sets_list (set_selectors[i].sets);
  }
}




void gg_view_legend (gboolean val)
{
  sets_list_view_legend   = val;
  sets_list_view_comments = !val;
}

void gg_view_comment (gboolean val)
{
 sets_list_view_comments = val;
 sets_list_view_legend   = !val;
}

/**
 * Acts on set (gno ,setno)
 */
static void gg_sets_menu_actions_1 (Menu3action action ,int gno ,int setno)
{
  char buf[64];
  char *title;
  int new_set;
  switch (action) {
  case MenuHideCB:
    set_set_hidden (gno ,setno ,TRUE);
    break;
  case MenuShowCB:
    set_set_hidden (gno ,setno ,FALSE);
    break;
  case MenuDuplicateCB:
    new_set = nextset (gno);
    do_copyset (gno ,setno ,gno ,new_set);
    sprintf (buf, "Copy of G%d.s%d" ,gno ,setno);
    comment_and_legend_set (gno ,new_set ,buf);
    break;
  case MenuKillCB:
    if (yesno_set (buf ,"Kill set "         ,gno ,setno)) ge_kill_set (gno ,setno);
    break;
  case MenuKillDataCB:
    if (yesno_set (buf ,"Kill data in set " ,gno ,setno)) killsetdata (gno ,setno);
    break;
    case MenuEditECB:
      gg_do_ext_editor (gno, setno);
      break;
  case MenuUseFontTool:
    title = get_legend_string (gno ,setno);
    gg_fontwin_create (NULL ,title);
    break;
  default:
    break;
  }
}

/**
 * Acts on two sets
 */
static void gg_sets_menu_actions_2 (Menu3action action ,int gno1 ,int setno1 ,int gno2 ,int setno2)
{
  char buf[64];
  switch (action) {
  case MenuCopy12CB:
    if (yesno_set (buf ,"Overwrite ",gno2 ,setno2 )) do_copyset (gno1 ,setno1 ,gno2 ,setno2);
    break;
  case MenuCopy21CB:
    if (yesno_set (buf ,"Overwrite ",gno1 ,setno1 )) do_copyset (gno2 ,setno2 ,gno1 ,setno1);
    break;
  case MenuMove12CB:
    if (yesno_set (buf ,"Replace ",gno2 ,setno2 ))   moveset    (gno1 ,setno1 ,gno2 ,setno2);
    break;
  case MenuMove21CB:
    if (yesno_set (buf ,"Replace ",gno1 ,setno1 ))   moveset    (gno2 ,setno2 ,gno1 ,setno1);
    break;
  case MenuSwapCB:
    swapset (gno1 ,setno1 ,gno2 ,setno2);
    break;
  default:
    break;
  }
}

/**
 *  "n" is the number of selected sets in the list,
 *  "j" the counter for selected lines
 */
void gg_sets_menu3_actions (Menu3action action ,int n ,int gno ,int setno ,int j)
{
  static int gno1 ,setno1;
  gg_sets_menu_actions_1 (action ,gno ,setno);

  if (action == MenuEditGE) {     /* Edit in Explorer */
    ge_explorer_popup     ();
    ge_block_tree_CB      (TRUE);
    ge_unselect_all       ();
    obj_tid_make_current  (Q_Set ,setno ,Q_Graph ,gno);
    gg_switch_current_set (gno ,setno);
    ge_rpane_set_type     (Q_Set);
    ge_set_update         (gno ,setno);
    ge_rpane_update       ();
    ge_block_tree_CB      (FALSE);
  } else if (n == 2) {
    if (j == 0) {
      gno1   = gno;
      setno1 = setno;
    } else {
      gg_sets_menu_actions_2 (action ,gno1 ,setno1 ,gno ,setno);
    }
  }
}

/**
 * Returns the gno if "sets" is in a GraphSetSelector, -1 if not
 */
static int gno_for_menu3 (Gw_list *sets)
{
  int i ,gno;
  gg_GraphSetStructure *gss;
  gboolean graph_list_visible;
  for (i = 0; i < nset_selectors; i++) {
    if (sets == set_selectors[i].sets) {
      gss = set_selectors[i].gss;
      if (gss != NULL) {
	graph_list_visible = gdk_window_is_viewable (GDK_WINDOW (gss->frame->window));
	gno = gw_list_get_first_selected_row_num (GW_LIST (gss->graphs)); 
	return gno;
      } else {
	return get_cg ();
      }
    }
  }
  return -1;
}

/**
 *  A callback for the contextual menu for sets
 *  Compare to ge_sets_menu3_CB  and ge_axis_menu3_CB
 */
void gg_sets_menu3_CB  (GtkWidget *sets_list ,gpointer pact)
{
  Menu3action action = GPOINTER_TO_INT (pact);  
  Gw_list *liste;
  GtkTreeModel *model;
  GtkTreeIter iter;
  GtkTreeSelection *select;
  GList *lpath = NULL;
  int n ,setno ,j ,gno;

  liste = GW_LIST (sets_list);
  model = GTK_TREE_MODEL (liste->model);
  switch (action) {
  case MenuSelectAll:
    gw_list_select_all (liste);
    break;
  case MenuUSelectAll:
    gw_list_unselect_all (liste);
    break;
  case MenuInvSelect:
    gw_list_inverse_selection (liste);
    break;
  default:
    select = gtk_tree_view_get_selection (GTK_TREE_VIEW (GW_LIST (sets_list)->view));
    n = gtk_tree_selection_count_selected_rows (select);
    if (n > 0) {
      if (action == MenuEditGE && n != 1) {
	errmsg ("Please select single set");
	return;
      }
      gno = gno_for_menu3 (GW_LIST (sets_list));
      if (!is_valid_gno (gno)) {
	errmsg("Please select a valid graph");
	return;
      }
      lpath  = gtk_tree_selection_get_selected_rows (select ,&model);
      /* the list must be traversed un reverse order */
      lpath = g_list_last (lpath);
      j = 0;
      while (lpath != NULL) {
	if (gtk_tree_model_get_iter (model ,&iter ,lpath->data)) {
	  gtk_tree_model_get (model ,&iter ,GW_LIST_NUM_COL ,&setno ,-1);
	  gg_sets_menu3_actions (action ,n ,gno ,setno ,j);
	  lpath = g_list_previous (lpath);
	  j++;
	}
      }
    }
    gg_update_all_sets_lists ();
    gg_drawgraph();
  }
}


/**
 *  gg_CreateSetPopupEntries  creates a contextual menu for sets
 *                 replace  CreateSetPopupEntries   motifutils.c  2240
 *  Comparer a gg_CreateGraphPopupEntries
 */
static void gg_shownd_set_CB (GtkWidget *w ,gint action)
{
  show_nodata = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (w));  
  gg_update_all_sets_lists ();
}

static void gg_showh_set_CB (GtkWidget *w ,gint action)
{
  show_hidden = gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (w));  
  gg_update_all_sets_lists ();
}

/**
 * Called by ge_tree_CB with CB = gg_sets_popup_menu3
 */
SetsMenu3Struct *gg_CreateSetPopupEntries (SetsMenu3Struct *p ,GtkWidget *list
					   ,void (*CB) (GtkWidget *set_list ,gpointer p))
{
  GtkWidget *menu ,*submenu ,*aux;
  if (p  == NULL) {
    p = xmalloc (sizeof (SetsMenu3Struct));
    menu = gtk_menu_new ();
    p->popup 	 = menu;
    p->hide  	 = gg_CreateMenuButton (menu ,"Hide"           ,'H'  ,list ,CB ,MenuHideCB);
    p->show  	 = gg_CreateMenuButton (menu ,"Show"           ,'S'  ,list ,CB ,MenuShowCB);
    p->editge  	 = gg_CreateMenuButton (menu ,"Edit in Explorer",'E' ,list ,CB ,MenuEditGE);
    gg_CreateMenuSeparator (menu);
    p->duplicate = gg_CreateMenuButton (menu ,"Duplicate"      ,'\0' ,list ,CB ,MenuDuplicateCB);
    p->kill  	 = gg_CreateMenuButton (menu ,"Kill"           ,'\0' ,list ,CB ,MenuKillCB);
    p->killd  	 = gg_CreateMenuButton (menu ,"Kill data"      ,'\0' ,list ,CB ,MenuKillDataCB);
    gg_CreateMenuSeparator (menu);
    p->copy12 	 = gg_CreateMenuButton (menu ,"Copy 1 to 2"    ,'\0' ,list ,CB ,MenuCopy12CB);
    p->copy21 	 = gg_CreateMenuButton (menu ,"Copy 2 to 1"    ,'\0' ,list ,CB ,MenuCopy21CB);
    p->move12 	 = gg_CreateMenuButton (menu ,"Move 1 to 2"    ,'\0' ,list ,CB ,MenuMove12CB);
    p->move21 	 = gg_CreateMenuButton (menu ,"Move 2 to 1"    ,'\0' ,list ,CB ,MenuMove21CB);
    p->swap   	 = gg_CreateMenuButton (menu ,"Swap"           ,'w'  ,list ,CB ,MenuSwapCB);
    gg_CreateMenuSeparator (menu);
    p->update 	 = gg_CreateMenuButton (menu ,"Update sets"    ,' '  ,list ,CB ,MenuSetsUpdate);
    p->font_tool = gg_CreateMenuButton (menu ,"Use font tool"  ,'\0' ,list ,CB ,MenuUseFontTool);
    gg_CreateMenuSeparator (menu);
    p->edit = gg_CreateMenu (menu ,"Edit data" ,'E'  ,FALSE);
    aux = gg_CreateMenuButton (p->edit ,"In spreadsheet" , '\0'  ,list ,CB ,MenuEditSCB);
    gtk_widget_set_sensitive (aux ,FALSE);
    gg_CreateMenuButton       (p->edit ,"In  text editor" ,'\0'  ,list ,CB ,MenuEditECB);

    submenu = gg_CreateMenu (menu ,"Create new" ,'\0' ,FALSE);
    aux = gg_CreateMenuButton (submenu ,"By formula"     ,'\0' ,list ,CB ,MenuNewFCB);
    aux = gg_CreateMenuButton (submenu ,"In spreadsheet" ,'\0' ,list ,CB ,MenuNewSCB);
    gtk_widget_set_sensitive (aux ,FALSE);
    aux = gg_CreateMenuButton (submenu ,"In text editor" ,'\0' ,list ,CB ,MenuNewECB);
    gg_CreateMenuSeparator (menu);

    aux = gg_CreateMenuButton (menu ,"Pack all sets" ,'\0' ,list ,CB ,MenuPackCB);
    gg_CreateMenuSeparator (menu);

    submenu = gg_CreateMenu (menu ,"Selector operations" ,'\0' ,FALSE);
    p->shownd = gg_CreateMenuToggle (submenu ,"Show data-less"    ,'\0' ,list ,gg_shownd_set_CB ,FALSE);
    p->showh  = gg_CreateMenuToggle (submenu ,"Show hidden"       ,'\0' ,list ,gg_showh_set_CB  ,TRUE);
    aux       = gg_CreateMenuButton (submenu ,"Select all"        ,'\0' ,list ,CB ,MenuSelectAll);
    aux       = gg_CreateMenuButton (submenu ,"Unselect all"      ,'\0' ,list ,CB ,MenuUSelectAll);
    aux       = gg_CreateMenuButton (submenu ,"Invert selection"  ,'\0' ,list ,CB ,MenuInvSelect);
    aux       = gg_CreateMenuButton (submenu ,"Update"            ,'\0' ,list ,CB ,MenuUpdate);
  }
  return (p);
}

/**
 *  Replace  set_popup   motifutils.c  2324
 */
void gg_sets_popup_menu3 (SetsMenu3Struct *p ,int n ,gboolean edit_in_tree)
{
  gtk_widget_set_sensitive (p->editge    ,edit_in_tree);
  gtk_widget_set_sensitive (p->show      ,n != 0);
  gtk_widget_set_sensitive (p->duplicate ,n != 0);
  gtk_widget_set_sensitive (p->kill      ,n != 0);
  gtk_widget_set_sensitive (p->copy12    ,n == 2);
  gtk_widget_set_sensitive (p->copy21    ,n == 2);
  gtk_widget_set_sensitive (p->move12    ,n == 2);
  gtk_widget_set_sensitive (p->move21    ,n == 2);
  gtk_widget_set_sensitive (p->swap      ,n == 2);
  gtk_widget_set_sensitive (p->font_tool ,n == 1);
  gtk_menu_popup (GTK_MENU(p->popup) ,NULL ,NULL ,NULL ,NULL ,3
		  ,gtk_get_current_event_time () );
}

void gg_sets_popup_menu3_CB (GtkWidget *list ,GdkEvent *event ,SetsMenu3Struct *p)
{
  GtkTreeSelection *sel;
  int n;
  if (event->type == GDK_BUTTON_PRESS) {
    if (event->button.button == 3) {
      sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (GW_LIST(list)->view));
      n   = gtk_tree_selection_count_selected_rows (sel);
      gg_sets_popup_menu3 (p ,n ,TRUE);
    }
  }
}


/**
 *   Replace CreateSetChoice     motifutils.c 2424
 */  
GtkWidget *gg_CreateSetChoice (char *labelstr, int type_select
			       ,int width ,int height
			       ,void (* cb)(GtkTreeSelection *sel ,Gw_list *list)
			       ,SetsMenu3Struct **sets_menu3
			       ,gg_GraphSetStructure *gss
			       )
{
  char *buf; 
  int gno ,i;
  GtkWidget *w;
  Gw_list *sets;
  GtkTreeSelection *sel;
  buf = malloc (128 * sizeof (char));
  gno = get_cg();
  w = gw_list_new ("Sets" ,labelstr ,type_select);
  sets = GW_LIST (w);
  for (i = 0; i < number_of_sets(gno); i++) {
    buf = gg_update_sets_string (gno ,i ,buf);
    gw_list_append (sets ,buf ,i ,is_set_hidden(gno,i) );
  }
  if (width > 0) gtk_widget_set_size_request (w ,width ,height);
  if (cb != NULL) {
    sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (sets->view));
    g_signal_connect (sel ,"changed" ,G_CALLBACK (cb) ,sets);
  }
  if (*sets_menu3 == NULL) {
    *sets_menu3 = gg_CreateSetPopupEntries (*sets_menu3 ,w ,gg_sets_menu3_CB);
  }
  gg_set_selector_add (GW_LIST (w) ,gss);
  g_signal_connect (sets ,"gw_list_event" ,G_CALLBACK (gg_sets_popup_menu3_CB) ,*sets_menu3);
  return (w);
}

/**
 *  Replace update_sets_cb  motifutils.c   2468
 */
static void gg_update_sets_CB (GtkWidget *graphs ,gg_GraphSetStructure *gss)
{
  int gno;

  if (!gg_GetSingleListChoice (GW_LIST (graphs) ,&gno)) return;
  gg_update_sets_list (GW_LIST (gss->sets));
}


/**
 *  Replace CreateGraphSetSelector  motifutils.c   2481
 */
gg_GraphSetStructure *gg_CreateGraphSetSelector (GtkWidget *parent ,char *s
						 ,int sel_type
						 ,GdkColor *couleur)
{
  gg_GraphSetStructure *gss;

  gss = xmalloc (sizeof (gg_GraphSetStructure));
  gss->graph_menu3 = NULL;
  gss->frame  = gg_frame (parent, s ,couleur ,0 ,1);
  gss->graphs = gg_CreateGraphChoice ("Graph:"  ,GTK_SELECTION_BROWSE
				      ,LIST_WIDTH ,GRAPH_LIST_HEIGHT
				      ,NULL
				      ,&(gss->graph_menu3));
  gss->sets_menu3 = NULL;
  gss->sets   = gg_CreateSetChoice ("Set:", sel_type
				    ,LIST_WIDTH ,SET_LIST_HEIGHT
				    ,NULL
				    ,&(gss->sets_menu3)
				    ,gss); //12 (gpointer)gss->graphs);

  gg_frame_attach (gss->frame ,gss->graphs);
  gg_frame_attach (gss->frame ,gss->sets);
  g_signal_connect (gss->graphs ,"gw_list"
		    ,G_CALLBACK (gg_update_sets_CB) ,gss);   // gw_list_event
  return gss;
}

/********** Source / destination  graph / sets selectors **********/
/**
 *  Replace CreateSrcDestSelector  motifutils.c   2499
 */
gg_SrcDestStructure *gg_CreateSrcDestSelector(GtkWidget *parent
					      ,int sel_type)
{
    gg_SrcDestStructure *sds;

    sds = xmalloc (sizeof (gg_SrcDestStructure));
    sds->box = gg_CreateHContainer (parent);
    sds->src  = gg_CreateGraphSetSelector (sds->box, "Source"
					   ,sel_type ,&wheat);
    sds->dest = gg_CreateGraphSetSelector (sds->box, "Destination"
					   ,sel_type ,&bleuciel);
    gtk_widget_show_all (sds->box);
    return sds;
}

/**
 * sd_mask values are  (see gg_gtkinc.h):
 * SD_SET1_UNIQUE      the number of selected source sets must be one
 * SD_NSET1_EQ_NSET2   *nset1 must be == *nset2.
 *                     If (*nset2 < *nset1), missing sets are created in gno2
 *                     
 * SD_NSET2_GIVEN      The arg *nset2 gives the number of sets to use in gno2
 *                     If the numbers of selected sets in gno2 is < *nset2,
 *                     missing sets are created in gno2
 *                    
 * SD_EXCLUSIVE        check for mutually exclusive selections
 */
int gg_GetSrcDestSettings (gg_SrcDestStructure *sds ,int sd_mask
			   ,int *gno1  	     ,int *gno2
			   ,int *nset1 	     ,int *nset2
			   ,int **setlist1   ,int **setlist2)
{
  Gw_list *slist1 ,*slist2;
  int i ,setno2 ,*sl2 ,nset2_in ,wanted_nset2;

  if (!gg_GetSingleListChoice (GW_LIST (sds->src->graphs)  ,gno1)) {
    errmsg("Please select single source graph");    
    return (RETURN_FAILURE);
  }
  if (!gg_GetSingleListChoice (GW_LIST (sds->dest->graphs) ,gno2)) {
    errmsg("Please select single destination  graph");    
    return (RETURN_FAILURE);
  }
  if (nset2 != NULL) {
    nset2_in = *nset2;
  } else {
    nset2_in = 0;
  }
  slist1 = GW_LIST (sds->src->sets);
  slist2 = GW_LIST (sds->dest->sets);
  *setlist1 = gw_list_selected_list (slist1 ,nset1);
  sl2       = gw_list_selected_list (slist2 ,nset2);

  if (sd_mask & SD_NSET2_GIVEN) {
    wanted_nset2 = nset2_in;
  } else {
    wanted_nset2 = *nset1;
  }

  /* check for mutually exclusive selections */
  if ( (sd_mask & SD_EXCLUSIVE) && (*gno1 == *gno2) ) {
    for (i = 0; i < *nset1; i++) {
      if (i < *nset2) {
	if ((*setlist1)[i] == sl2[i]) {
	  errmsg("Source and destination set(s) are not mutually exclusive");
	  xfree (*setlist1);
	  xfree (*setlist2);
	  xfree (sl2);
	  *setlist1 = NULL;
	  *setlist2 = NULL;
	  return RETURN_FAILURE;
	}
      }
    }
  }

  /* check *nset1 */
  if (*nset1 < 1) {
    errmsg ("No source sets selected");
    return RETURN_FAILURE;
  }
  if ( (sd_mask & SD_SET1_UNIQUE) && (*nset1 != 1) ) {
    errmsg ("Please select single source");
    return RETURN_FAILURE;
  }

  /* check nset2 */
  if (sd_mask & (SD_NSET1_EQ_NSET2 | SD_NSET2_GIVEN) ) {
    if (*nset2 < wanted_nset2) {
      /* We have to create new sets in *gno2 */
      if (*nset2 == 0) {
	*setlist2 = malloc (wanted_nset2 * sizeof (int));
      } else {
	*setlist2 = xrealloc (sl2 ,wanted_nset2 * sizeof (int));
      }
      /* We assume that the first sets selected in gno1 correspond to
	 the sets already selected  in gno2 and create the missing ones */
      for (i = *nset2; i < wanted_nset2; i++) {
	setno2 = nextset (*gno2);
	do_copyset (*gno1 ,(*setlist1)[0] ,*gno2 ,setno2);
	(*setlist2)[i] = setno2;
      }
      *nset2    = wanted_nset2;
      return RETURN_SUCCESS;
    } else if (*nset2 == wanted_nset2) {
      *setlist2 = sl2;
      return RETURN_SUCCESS;
    } else {
      errmsg ("Too much sets selected in gno2");
      return RETURN_FAILURE;
    }
  }
  *setlist2 = sl2;
  *nset2    = wanted_nset2;
  return RETURN_SUCCESS;
}


/******************* D I A L O G AAC  B O X ******************
 *  Replace CreateAACDialog   motifutils.c  2999
 */
/*  Apply , Accept , Close */

GtkWidget *gg_CreateAACDialog (gchar *title
			       ,void (*cbproc) (GtkWidget *w ,gint reponse)
			       ,gint width ,gint height
			       ,GtkWidget **vb)
{
  GtkWidget *w;

  w = gtk_dialog_new_with_buttons (title ,NULL
				   ,GTK_DIALOG_DESTROY_WITH_PARENT
				   ,GTK_STOCK_APPLY   ,GTK_RESPONSE_APPLY
				   ,"Accept"          ,GTK_RESPONSE_ACCEPT
				   ,GTK_STOCK_CLOSE   ,GTK_RESPONSE_CLOSE
				   ,NULL);
  g_signal_connect (w ,"delete-event" ,G_CALLBACK (gtk_widget_hide) ,NULL);
  if (cbproc != NULL) 
    g_signal_connect (w ,"response"   ,G_CALLBACK (cbproc)          ,NULL);
  if (width > 0) gtk_widget_set_size_request (GTK_WIDGET(w) ,width ,height);    
  *vb = GTK_DIALOG (w)->vbox;
  return w;
}

/**
 * Dialog to ask for an expr: return a double in *d
 * If gg_evalexpr fails or CLOSE is clicked, return FALSE.
 */
gint gg_ask_expr (char *msg ,char *title ,int len ,double *d)
{
  GtkWidget *w ,*vb ,*en;
  gint      response;
  gboolean  eval_ok;
  w = gtk_dialog_new_with_buttons (title ,NULL
				   ,GTK_DIALOG_DESTROY_WITH_PARENT
				   ,"Accept"          ,GTK_RESPONSE_ACCEPT
				   ,GTK_STOCK_CLOSE   ,GTK_RESPONSE_CLOSE
				   ,NULL); 
  vb = GTK_DIALOG (w)->vbox;
  en = gg_entry_new (vb ,msg ,len);
  gtk_entry_set_text (GTK_ENTRY (en) ,"");
  gtk_widget_show_all (w);
  response = gtk_dialog_run (GTK_DIALOG (w));
  if (response == GTK_RESPONSE_ACCEPT) {
    eval_ok = gg_evalexpr (en ,d);
    gtk_widget_destroy (w);
    return eval_ok;
  } else {
    gtk_widget_destroy (w);
    return GTK_RESPONSE_CLOSE;  /* i.e. -7 */
  }
}


/******************* B O X  in  a box ******************
 * Replace CreateVContainer  3105
 */
GtkWidget *gg_CreateVContainer (GtkWidget *parent)
{
    GtkWidget *rc;

    rc = gtk_vbox_new (FALSE, 2);
    gtk_box_pack_start (GTK_BOX (parent) ,rc , FALSE, TRUE, 1);    
    return (rc);
}

GtkWidget *gg_CreateHContainer (GtkWidget *parent)
{
    GtkWidget *rc;

    rc = gtk_hbox_new (FALSE, 3);
    gtk_box_pack_start (GTK_BOX (parent) ,rc ,FALSE , TRUE, 1);    
    return (rc);
}

/**************** N O T E B O O K  ************
 * Replace CreateTabPage   motifutils.c  3226
 */
GtkWidget *gg_CreateTabPage (GtkWidget *parent, char *s)
{
  GtkWidget *lab ,*vbox;

  lab = gtk_label_new (s);
  vbox = gtk_vbox_new (FALSE ,1);
  gtk_notebook_append_page (GTK_NOTEBOOK (parent) ,vbox ,lab);
  return (vbox);
}

/**
 *  Replace CreateScrollTextItem2   3285
 *  Width and height are in  pixels
 *  Return a GtkTextBuffer
 */
GtkTextBuffer *gg_CreateScrollTextItem2 (GtkWidget *parent
				     ,int width , int height
				     ,char *s)
{
  GtkWidget *sw ,*w_text;
  GtkTextBuffer *w_buff;
  
  w_box = gtk_vbox_new (FALSE, 2);
  w_label = gtk_label_new_with_mnemonic (s);
  gtk_box_pack_start (GTK_BOX (w_box) ,w_label, FALSE, FALSE, 1);
    
  sw = gtk_scrolled_window_new (NULL,NULL);
  gtk_scrolled_window_set_policy  (GTK_SCROLLED_WINDOW(sw)
				   ,GTK_POLICY_AUTOMATIC
				   ,GTK_POLICY_AUTOMATIC);
  if (width > 0) {
    gtk_widget_set_size_request (sw ,width ,height);
    /* Remark : a gtk_scrolled_window is not a GTK_WINDOW */
  }
  w_text = gtk_text_view_new ();
  w_buff = gtk_text_view_get_buffer (GTK_TEXT_VIEW (w_text));
  gtk_widget_show (w_text);
  gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW(sw) ,w_text);
  gtk_box_pack_start (GTK_BOX (w_box) ,sw ,TRUE , TRUE, 1);    
  if (parent != NULL)
    gtk_box_pack_start (GTK_BOX (parent) ,w_box , TRUE, TRUE, 1);

  gtk_widget_show_all (w_box);
  return (w_buff);
}

/**
 *  Replace xv_getstr     motifutils.c 3226
 *  whitout the conversion newlines -> spaces
 */
const char *gg_getstr (GtkWidget *w)
{
  return (gtk_entry_get_text (GTK_ENTRY(w)));
}

/**
 * Replace xv_evalexpr    motifutils.c 3356
 * take a text field and pass it to the parser if it needs to
 * evaluated, else use atof().
 * place the double result in answer
 * if an error, return False, else True
 */
gboolean gg_evalexpr (GtkWidget *w ,double *answer)
{
    const gchar *s;
    static char *buf = NULL;
    int i, len, ier = 0;
    double result;
	
    s = gtk_entry_get_text (GTK_ENTRY (w));
    buf = copy_string (buf, s);

    if (!(len = strlen( buf ) )) { /* check for zero length */
      *answer = 0;
      return RETURN_FAILURE;
    }
    

    /* first character may be a sign */
    if (!fpdigit[(int) buf[0]] && buf[0] != '-' && buf[0] != '+') {
        i = len +1;
    } else {
        i = 1;
    }

    for (; i<len; i++) {
        if (!fpdigit[(int) buf[i]]) {
            break;
        }
    }

    if (i == len) {         /* only floating point digits */
        *answer = atof( buf );
        return RETURN_SUCCESS;
    } else {                /* must evaluate an expression */
        ier = s_scanner(buf, &result);
        if( !ier ) {
            *answer = result;
            return RETURN_SUCCESS;
        } else {
            *answer = 0;
            return RETURN_FAILURE;
        }
    }
}


/** gg_evalexpri - take a text field and pass it to the parser if it needs to
 * evaluated, else use atoi().
 * place the integer result in answer
 * if an error, return FALSE, else TRUE
 *   Replace  xv_evalexpri                        !motifutils.c  3404
 */
gboolean gg_evalexpri (GtkWidget *w, int *answer )
{
    const gchar *s;
    static char *buf = NULL;
    int i, len, ier = 0;
    double result;
	
    s = gtk_entry_get_text (GTK_ENTRY (w));
    buf = copy_string (buf, s);

    if (!(len = strlen( buf ) )) { /* check for zero length */
        *answer = 0;
        return RETURN_FAILURE;
    }
    /* first character may be a sign */
    if (!fpdigit[(int) buf[0]] && buf[0] != '-' && buf[0] != '+') {
        i = len +1;
    } else {
        i = 1;
    }
    
    for (; i<len; i++) {
        if (!fpdigit[(int) buf[i]]) {
            break;
        }
    }

    if (i == len) {             /* only floating point digits */
        *answer = atoi(buf);
        return RETURN_SUCCESS;
    } else {                    /* must evaluate an expression */
        ier = s_scanner(buf, &result);
        if( !ier ) {
            *answer = (int)result;
            return RETURN_SUCCESS;
        } else {
            *answer = 0;
            return RETURN_FAILURE;
        }
    }
}



/**
 *   Replace xv_setstr  motifutils.c 3447
 */
void gg_setstr (GtkWidget *w, char *s)
{
    if (w != NULL) {
      gtk_entry_set_text (GTK_ENTRY (w) ,s ? s : "");        
    }
}

void gg_set_utf8 (GtkWidget *w, char *s)
{
  gchar *utf8_text;
  
  if (w != NULL) {
    if (s == NULL) {
      gtk_entry_set_text (GTK_ENTRY (w) , "");        
    } else {
      utf8_text = g_convert (s, -1, "UTF-8", "ISO-8859-1" ,NULL, NULL, NULL);
      gtk_entry_set_text (GTK_ENTRY (w) ,utf8_text);
    }
  }
}

/** Formatted variants of gg_setstr
 */
void gg_setstr_d (GtkWidget *w, char *fmt ,double d ,char *buf)
{
  if (w != NULL) {
    sprintf (buf ,fmt ,d);
    gtk_entry_set_text (GTK_ENTRY (w) ,buf);
  }   
}

/**
 *  Smart formatting of d1 and d2
 */
void gg_setstr_2d (GtkWidget *w1 ,GtkWidget *w2 ,double d1 ,double d2)
{
  char fmt[10] ,buf1[32] ,buf2[32];
  int prec ,n1 ,n2;
  double x1 ,x2 ,dx ,rdx;
  if (w1 != NULL && w2 != NULL) {
    dx = fabs (d2-d1);
    sprintf (fmt ,"%%g");
    prec = 6;
    do {          /* sometime "%g" does not print enough digits */
      do {
	sprintf (buf1 ,fmt ,d1);
	sprintf (buf2 ,fmt ,d2);
	prec++;
	sprintf (fmt ," %%.%de " ,prec+1);
	if (prec > 14) {
	  errmsg ("gg_setstr_2d: unable to distinguish start and stop");
	  return;
	}
      } while (strcmp (buf1 ,buf2) == 0);
      n1 = sscanf (buf1 ,"%lf" ,&x1);
      n2 = sscanf (buf2 ,"%lf" ,&x2);
      rdx = fabs (x2-x1) / dx;
    } while  (fabs (rdx - 1.0) > 0.01) ;
    /* sometime zero is not "0" */
    if (fabs(x1) / dx < 1e-12) sprintf (buf1 ," 0 ");
    if (fabs(x2) / dx < 1e-12) sprintf (buf2 ," 0 ");
    gtk_entry_set_text (GTK_ENTRY (w1) ,buf1);
    gtk_entry_set_text (GTK_ENTRY (w2) ,buf2);
  }
}

/************ M A N A G E   S E T   L I S T ***************/

/* Same selection problem for GraphMenu3Struct and SetsMenu3Struct */

/**
 *  Replace  CreateSeparator(Widget parent)   motifutils.c 3855
 */
void gg_CreateMenuSeparator(GtkWidget *parent)
{
  GtkWidget *sep;
  sep = gtk_separator_menu_item_new ();
  gtk_menu_shell_append        (GTK_MENU_SHELL (parent) ,sep);
  gtk_widget_show (sep);
}

/**
 *   Replace CreateMenuBar     motifutils.c 3864
 */
GtkWidget *gg_CreateMenuBar (GtkWidget *parent)
{
    GtkWidget *menubar;
    
    menubar = gtk_menu_bar_new ();
    gtk_box_pack_start (GTK_BOX (parent) ,menubar ,FALSE, FALSE, 2);
    gtk_widget_show (menubar);
    return menubar;
}



/**
 *   Replace CreateMenu        motifutils.c 3872
 */
GtkWidget *gg_CreateMenu (GtkWidget *parent, char *label, char mnemonic, gboolean help)
{
  GtkWidget *titre ,*menupane;

  titre = gtk_menu_item_new_with_label (label);
  gtk_widget_show (titre);
  gtk_menu_shell_append (GTK_MENU_SHELL (parent) ,titre);

  menupane = gtk_menu_new ();
  gtk_menu_item_set_submenu (GTK_MENU_ITEM (titre), menupane);

  if (help) {
    gtk_menu_item_set_right_justified ( GTK_MENU_ITEM(titre) ,TRUE);
    
  }
  return menupane;
}

/**
 *  Create a gtk_button  and connect signal "activate" (?)  func to mbut in list
 */
GtkWidget *gg_CreateListButton (GtkWidget *parent, char *label, char mnemonic
				 ,GtkWidget *list
				 ,void (*func) (GtkWidget *list ,gpointer p)
				 ,gint action)
{
  GtkWidget *but;   

  but = gtk_button_new_with_label (label);
  if (list != NULL) {
    gw_list_connect (but ,NULL ,func ,list ,action);
  }
  gg_frame_attach (parent ,but);
  // A FAIRE : mnemonic
  return but;
}

/**
 *  Create a gtk_menu_item and connect signal "activate" func to mbut in list
 *  Replace CreateMenuButton    motifutils.c 3905
 */
GtkWidget *gg_CreateMenuButton (GtkWidget *parent, char *label, char mnemonic
				 ,GtkWidget *list
				 ,void (*func) (GtkWidget *list ,gpointer p)
				 ,gint action)
{
  GtkWidget *mbut;   

  mbut = gtk_menu_item_new_with_label (label);
  gtk_menu_shell_append (GTK_MENU_SHELL (parent) ,mbut);
  gtk_widget_show (mbut);
  if (list != NULL) {
    gw_list_connect (mbut ,NULL ,func ,list ,action);
  } else  if (func != NULL) {
    if (action < 0) {
      g_signal_connect_swapped (G_OBJECT (mbut), "activate"
				,G_CALLBACK (func)
				,G_OBJECT (parent));
    } else {
      g_signal_connect (mbut ,"activate" ,G_CALLBACK (func) ,GINT_TO_POINTER (action));
    }
  }
  // A FAIRE : mnemonic
  gtk_widget_show (mbut);
  return mbut;
}

/**
 *  Replace CreateMenuCloseButton   motifutils.c 3930
 */
GtkWidget *gg_CreateMenuCloseButton (GtkWidget *parent, GtkWidget *form)
{
  GtkWidget *mbut; 

  mbut = gg_CreateMenuButton (parent ,"Close" ,'C' ,NULL ,NULL ,-1);
  g_signal_connect_swapped (G_OBJECT (mbut) ,"activate", 
			    G_CALLBACK (gtk_widget_hide),
			    form);
  return mbut;
}

/**
 *  Replace CreateMenuToggle   motifutils.c 3956
 */
GtkWidget *gg_CreateMenuToggle (GtkWidget *parent, char *label, char mnemonic
				,GtkWidget *list
				,void (*func) (GtkWidget *w ,gint action)
				,gint state)
{
  GtkWidget *mbut;   

  mbut = gtk_check_menu_item_new_with_label (label);
  gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM(mbut) ,(gboolean) state);
  gtk_menu_shell_append (GTK_MENU_SHELL (parent) ,mbut);
  gtk_widget_show (mbut);
  if (func != NULL) g_signal_connect (mbut ,"toggled" ,G_CALLBACK (func) ,list);

  // A FAIRE : mnemonic 
  return mbut;
}




/** Update all the menus
 *  Replace update_all   motifutils.c 4162
 */
void gg_update_all (void)
{
  int gno = get_cg();
    
  if (!inwin) return;
  gg_graph_update_all_lists ();
  gg_update_all_sets_lists ();
  gg_update_props_items ();
//     update_hotlinks();
//     update_prune_frame();
  gg_update_locator_items (gno);
  gg_set_stack_message();
  gg_status_message (NULL);

  //  gg_update_explorer ();
  gg_update_bt_graph ();
  gg_update_layers_hbox ();
}

/** 
 *   Replace yesnowin   motifutils.c 4079
 */

int gg_yesnowin (char *msg)
{
  static GtkWidget *yesno_popup = NULL;
  static GtkWidget *label;
  gint response;

  yesno_popup = gtk_dialog_new_with_buttons ("Warning" ,NULL
					     ,GTK_DIALOG_DESTROY_WITH_PARENT
					     ,GTK_STOCK_OK    ,GTK_RESPONSE_OK
					     ,GTK_STOCK_CANCEL,GTK_RESPONSE_CANCEL
					     ,NULL);
  label = gtk_label_new (msg);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (yesno_popup)->vbox),
                        label, TRUE, TRUE, 0);
  gtk_widget_show_all (yesno_popup);
  response = gtk_dialog_run (GTK_DIALOG(yesno_popup));
  gtk_widget_destroy (yesno_popup);

  return (response ==GTK_RESPONSE_OK) ;
}

    
/*********** C O M B O s ************/


GtkWidget *gg_Create2VBox (GtkWidget *parent ,gchar *label ,GdkColor *couleur
			   ,GtkWidget **lbox ,GtkWidget **rbox)
{
  GtkWidget *fr ,*hb;

  fr = gg_frame (parent ,label ,couleur ,1 ,0);
  hb = gg_CreateHContainer (fr);
  *lbox = gg_CreateVContainer (hb);
  *rbox = gg_CreateVContainer (hb);
  return fr;
}



void gg_void_cell (GtkWidget *parent)
{
  GtkWidget *w;

  w = gtk_label_new (NULL);
  gg_frame_attach (parent ,w);
}


GtkWidget *vpg_frame (GtkWidget *parent ,gchar *label ,GdkColor *couleur) 
{
  GtkWidget *w_event ,*w_frame;

  /* un "event"  est necessaire pour pouvoir changer la couleur du fond */
  w_event = gtk_event_box_new ();  
  gtk_box_pack_start (GTK_BOX (parent) ,w_event , FALSE , TRUE, 5);
  gtk_widget_modify_bg (GTK_WIDGET(w_event), GTK_STATE_NORMAL ,couleur);
  w_frame = gtk_frame_new(label);
  gtk_container_add (GTK_CONTAINER (w_event), w_frame);
  return (w_frame);
}


/************** D O U B L E ****************/

/* Lire un nombre reel (double) dans une entry */ 
void gg_double_scan (GtkWidget *widget, double *d)
{
  sscanf (gtk_entry_get_text(GTK_ENTRY (widget)) ,"%lf" ,d);
} 

/* Afficher le double d dans w_entry */
void gg_double_set (GtkWidget *w_entry ,int nb_car ,double *d)
{
  char *sval;

  sval = malloc (nb_car * sizeof(char));
  sprintf(sval ,"%g" ,*d);
  gtk_entry_set_text (GTK_ENTRY (w_entry) ,sval);
  free (sval);
}

/* Creer un widget pour lire/modifier le double d  */ 
GtkWidget *gg_double (GtkWidget *parent, int nb_car ,char *label ,GtkWidget **p ,double *d)
{
  GtkWidget *w_entry;

  w_box = gtk_hbox_new (FALSE, 5);
  w_label = gtk_label_new_with_mnemonic (label);
  w_entry = gtk_entry_new ();  
  gtk_entry_set_width_chars (GTK_ENTRY (w_entry) ,nb_car);
  gg_double_set (w_entry ,nb_car ,d);

  gtk_box_pack_start (GTK_BOX (w_box), w_label, FALSE, TRUE, 0); 
  gtk_box_pack_start (GTK_BOX (w_box), w_entry, FALSE, TRUE, 1);
  if (parent != NULL)
    gtk_box_pack_start (GTK_BOX (parent), w_box , FALSE , TRUE, 5);

  g_signal_connect (w_entry ,"changed" ,G_CALLBACK(gg_double_scan) ,d);

  *p = w_entry;
  return (w_box);
}

/*********** G R O U P  of  M E N U   R A D I O   B U T T O N S ***********/

/**  Create a group of radio buttons and set one of them active
 *  va_arg s must be const char*
 */
GSList *gg_menu_radio_buttons_group (GtkWidget *parent
				     ,void (*func) (GtkCheckMenuItem *item ,gpointer p)  
				     ,int active
				     ,int nchoices ,...)
{
  va_list var;
  GSList *group = NULL;
  GtkWidget *item;
  int i;
  char *s;

  va_start(var ,nchoices);
  for (i=0; i<nchoices; i++) {
    s = va_arg (var, char *);
    item = gtk_radio_menu_item_new_with_label (group, s);
    group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (item));
    gtk_menu_shell_append (GTK_MENU_SHELL (parent) ,item);
    gtk_widget_show (item);
    if (i == active) {
      gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
      func (GTK_CHECK_MENU_ITEM(item) ,GINT_TO_POINTER(i));
    }
    g_signal_connect (GTK_CHECK_MENU_ITEM(item) ,"toggled" ,G_CALLBACK (func) ,GINT_TO_POINTER(i));
  }
  va_end(var);
  return (group);

}
