>From 824fd7ee07a685159e42ae022440722aa7506f9a Mon Sep 17 00:00:00 2001
From: Bernhard R. Link <brlink@debian.org>
Date: Sat, 19 Feb 2011 13:36:40 +0100
Subject: use non-deprecated combo boxes

---
 src/Makefile.am      |    2 +-
 src/callbacks.c      |  795 +++++---------------------------------------------
 src/callbacks.h      |    4 -
 src/interface.c      |   10 -
 src/interface.h      |    8 +-
 src/optionstore.c    |  213 ++++++++++++++
 src/optionstore.h    |   27 ++
 src/printeroptions.c |  310 ++++++++++++++++++++
 src/printeroptions.h |   42 +++
 9 files changed, 673 insertions(+), 738 deletions(-)
 create mode 100644 src/optionstore.c
 create mode 100644 src/optionstore.h
 create mode 100644 src/printeroptions.c
 create mode 100644 src/printeroptions.h

diff --git a/src/Makefile.am b/src/Makefile.am
index 8d433fb..d57f5d0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -11,7 +11,7 @@ else
 x = 
 endif
 
-gpr_SOURCES = main.c support.c support.h callbacks.c callbacks.h interface.c interface.h $(x)
+gpr_SOURCES = optionstore.c optionstore.h printeroptions.c printeroptions.h main.c support.c support.h callbacks.c callbacks.h interface.c interface.h $(x)
 
 gpr_CFLAGS = $(GNOME_CFLAGS)
 
diff --git a/src/callbacks.c b/src/callbacks.c
index d2f7058..0f48558 100644
--- a/src/callbacks.c
+++ b/src/callbacks.c
@@ -45,49 +45,17 @@
 #include <string.h>
 #include <stdlib.h>
 
+#include "optionstore.h"
 #include "callbacks.h"
 #include "interface.h"
 #include "support.h"
 #include "tab_categories.h"
 
-void choice_process(PpdOption * option, PpdChoice * choice, GtkWidget * combo,
-		    int interator, gboolean inst_opt, GtkWidget * combo_item,
-		    int count, gpointer ppd);
-
 #define MASTER_PPDDIR "/usr/share/postscript/ppd"
 
-/* keywords used to assign data to widgets with gtk_object_set_data */
-#define OT "option text"
-#define OK "option keyword"
-#define CT "choice text"
-#define CK "choice keyword"
-#define IN "index"
-/* keyword to assigne a pointer to */
-#define OC "option combo"
-
-
 #define MAX_TABS 25
 #define NUM_STANDARD_TABS 3
 
-
-
-/* puts data exactly in position 
-   (basilarly, the original authors should have used an array) */
-static GSList* my_g_slist_replace(GSList *list,
-				 gpointer data,
-				 gint position)
-{
-  GSList* obj=g_slist_nth(list,position);
-  g_assert( position <= g_slist_length(list)  );
-  if(obj) {
-    if(obj->data) g_free(obj->data);
-    obj->data=data;    
-  } else
-    list=g_slist_insert(list,data,position);
-  return list;
-}
-
-
 void revalidate_utf8(gchar *str)
 {
   extern int debug_on;
@@ -122,15 +90,7 @@ extern ptrTab *globalTabList;
 static GtkWidget *gpr_hbox_make(GtkWidget * opt_vbox);
 static GtkWidget *gpr_label_make(gchar * str, GtkWidget * hbox);
 static GtkWidget *gpr_optcombo_make(GtkWidget * hbox);
-static GtkWidget *gpr_combo_make(ppd_struct * local_ppd, int option_count,
-				PpdOption * option_ptr, GtkWidget * option_combo,
-				 gboolean inst_opt, GtkSignalFunc signal_func_ptr);
-static void gpr_combo_choices_make(int idx, ppd_struct * local_ppd,
-				  int option_count, PpdOption * option_ptr,
-				   GtkWidget * choice_combo, gboolean inst_opt);//GtkSignalFunc signal_func_ptr);
 static void gpr_set_ppd_saved_options(ppd_struct * local_ppd);
-static gboolean ensure_remove_hash_item(gpointer key, gpointer value,
-					gpointer user_data);
 static int in_path(gchar * filename);
 static int is_file(gchar * filename);
 
@@ -392,11 +352,8 @@ void grab_default_ppd(GtkWidget * menu_item, gpointer ppd)
 void init_ppd(GtkButton * button, gpointer ppd)
 {
   ppd_struct *local_ppd;	/* local copy of ppd struct */
-  PpdFile *local_ppd_handle = NULL;	/* local pointer to a ppd file
-					   structure */
+  PrinterOptions *options;
   char *local_file;		/* name of a local ppd file */
-  gchar *temp_string;		/* temporary string */
-  PpdConstraint *constr_ptr;	/* pointer to a UI constraint structure */
   FILE *settlist;		/* file containing list of saved settings */
   char curr_line[80];		/* a line of input from a file */
   gchar *current_setting;	/* current saved setting in the list of saved
@@ -405,9 +362,6 @@ void init_ppd(GtkButton * button, gpointer ppd)
   GtkWidget *savesett_menu_item[20];	/* space for 20 saved settings */
   int savesett_count = 0;	/* count of saved settings */
 
-  /* List iterators */
-  GSList *list;
-
   /* redirect stderr to dialog */
   g_set_printerr_handler((GPrintFunc) error_box);
 
@@ -420,18 +374,8 @@ void init_ppd(GtkButton * button, gpointer ppd)
   }
 
   /* 
-   * Initialize lists and hash tables
+   * Initialize lists
    */
-  if (local_ppd->choice_list != NULL) {
-    g_slist_free(local_ppd->choice_list);
-    local_ppd->choice_list = NULL;
-  }
-
-  if (local_ppd->inst_opt_list != NULL) {
-    g_slist_free(local_ppd->inst_opt_list);
-    local_ppd->inst_opt_list = NULL;
-  }
-
   /* create a new menu of saved settings */
   gtk_option_menu_remove_menu(GTK_OPTION_MENU(local_ppd->savesett_optionmenu));
   local_ppd->savesett_optionmenu_menu = gtk_menu_new();
@@ -492,10 +436,7 @@ void init_ppd(GtkButton * button, gpointer ppd)
 
   gtk_widget_show_all(local_ppd->savesett_optionmenu_menu);
 
-  local_ppd_handle = malloc(sizeof(PpdFile));
-  memset(local_ppd_handle, 0, sizeof(PpdFile));
-
-  /* 
+  /*
    * Check to see if the name is a file or a directory
    */
   if (!is_file((gchar *) local_file)) {
@@ -506,66 +447,18 @@ void init_ppd(GtkButton * button, gpointer ppd)
 
     return;
   }
-  local_ppd_handle = ppd_file_new(local_file);
-
+  options = printer_options_new(local_file);
 
   // check handle here, output error message if null
-  if (local_ppd_handle == NULL) {
+  if (options == NULL) {
     g_printerr
       (_("Invalid PPD file.\nPlease select a PPD file with the file browser or\nedit the printcap file section for this printer,\nadding an entry under \"ppdfile=\".\n"));
     return;
   }
 
-  if (local_ppd->constr_hash != NULL) {
-    g_hash_table_foreach_remove(local_ppd->constr_hash, (GHRFunc)
-				ensure_remove_hash_item, NULL);
-    g_hash_table_destroy(local_ppd->constr_hash);
-  }
-
-  if (local_ppd->cr_to_hr != NULL) {
-    g_hash_table_foreach_remove(local_ppd->cr_to_hr, (GHRFunc)
-				ensure_remove_hash_item, NULL);
-    g_hash_table_destroy(local_ppd->cr_to_hr);
-  }
-
-
-  local_ppd->ppd_handle = local_ppd_handle;
-
-  /* 
-   * Now devise a hash table for constraints
-   * The key is really important, not the data
-   * We just want a quick lookup to see if it's in the list
-   */
-  local_ppd->constr_hash = g_hash_table_new(g_str_hash, g_str_equal);
-
-  /* 
-   * Hash table to quickly convert computer readable text to 
-   * human readable text
-   */
-  local_ppd->cr_to_hr = g_hash_table_new(g_str_hash, g_str_equal);
-
-  list = (GSList *) local_ppd_handle->consts;
-
-  while (list) {
-    constr_ptr = list->data;
-    list = g_slist_next(list);
-    /* MLP: modified from "option1" to "option1->str" */
-    if (constr_ptr->option1 && constr_ptr->choice1 && 
-	constr_ptr->option2 && constr_ptr->choice2) {
-    temp_string =
-      g_strjoin(":", constr_ptr->option1->str, constr_ptr->choice1->str,
-		constr_ptr->option2->str, constr_ptr->choice2->str, NULL);
-
-    /* this is somewhat silly, but we need quick lookup. we don't care about
-       the 1 */
-    g_hash_table_insert(local_ppd->constr_hash, temp_string,
-			GINT_TO_POINTER(1));
-    }
-  }
-
-
-  free(local_ppd_handle);
-  local_ppd_handle = NULL;
+  if (local_ppd->options)
+	  g_object_unref(local_ppd->options);
+  local_ppd->options = options;
 }
 
 
@@ -773,9 +666,7 @@ void send_print(GtkWidget * button, gpointer ppd)
     I have created a subblock to reorganize the code a bit...
   */
   {
-    gchar *current_option;	/* the string for the current option */
     FILE *last_used;		/* file containing list of last used settings */
-    GSList *olist, *clist;
  
     last_used = fopen((char *)
 		      g_strconcat(local_ppd->config_dir, "/.lastused.sett", NULL),
@@ -817,34 +708,25 @@ void send_print(GtkWidget * button, gpointer ppd)
 
     /* end non-ppd options */
 
-    /* move through the linked list of installable options */
-    olist = local_ppd->inst_opt_list;
-    while (olist) {
-      current_option = olist->data;
-      olist = g_slist_next(olist);
-      if (last_used) fprintf (last_used, "IO:%s\n", current_option);
-    }
+    if (last_used) {
+      struct PrinterOption *o;
 
-    /* move through the linked list of options */
-    clist = local_ppd->choice_list;
-    while (clist) {
-      current_option = clist->data;
-      clist = g_slist_next(clist);
-
-      /* Will support more spoolers in future */
-      switch (local_ppd->spooler) {
-      case SPOOLER_LPRNG:			/* LPRng */
-	command_line =
-	  g_strconcat(command_line, "-Z ", current_option, " ", NULL);
-	break;
-      default:
-	command_line =
-	  g_strconcat(command_line, "-o ", current_option, " ", NULL);
-	break;
+      for (o = local_ppd->options->installable ; o != NULL ; o = o->next) {
+	if (o->selected_index < 0)
+		continue;
+	fprintf(last_used, "IO:%s:%s\n", o->option->keyword->str,
+			o->choices[o->selected_index].choice->choice->str);
+      }
+      for (o = local_ppd->options->options ; o != NULL ; o = o->next) {
+	if (o->selected_index < 0)
+		continue;
+	fprintf(last_used, "AO:%s:%s\n", o->option->keyword->str,
+			o->choices[o->selected_index].choice->choice->str);
       }
-
-      if (last_used) fprintf (last_used, "AO:%s\n", current_option);
     }
+    command_line = printer_options_concat(local_ppd->options,
+		   command_line,
+		   (local_ppd->spooler == SPOOLER_LPRNG)?"-Z ":"-o ", " ");
 
     if (is_psfile)
       command_line =
@@ -894,33 +776,14 @@ void clean_up(GtkWidget * button, gpointer ppd)
   local_ppd = ppd;
 
   /* 
-   * Check lists and hash tables and clear them
+   * Check lists and clear them
    */
-  if (local_ppd->choice_list != NULL) {
-    g_slist_free(local_ppd->choice_list);
-    local_ppd->choice_list = NULL;
-  }
-
-  if (local_ppd->inst_opt_list != NULL) {
-    g_slist_free(local_ppd->inst_opt_list);
-    local_ppd->inst_opt_list = NULL;
-  }
-
-  if (local_ppd->constr_hash != NULL) {
-    g_hash_table_foreach_remove(local_ppd->constr_hash, (GHRFunc)
-				ensure_remove_hash_item, NULL);
-    // g_hash_table_destroy(local_ppd->constr_hash);
-  }
-
-  if (local_ppd->cr_to_hr != NULL) {
-    g_hash_table_foreach_remove(local_ppd->cr_to_hr, (GHRFunc)
-				ensure_remove_hash_item, NULL);
-    // g_hash_table_destroy(local_ppd->cr_to_hr);
-  }
 
   /* free ppd structure */
-  if (local_ppd->ppd_handle != NULL)
-    free(local_ppd->ppd_handle);
+  if (local_ppd->options != NULL) {
+	g_object_unref(local_ppd->options);
+	local_ppd->options = NULL;
+  }
 
   /* free any temporary files created */
   if (local_ppd->file_to_print != NULL) {
@@ -1003,6 +866,15 @@ void remove_extra_notebook_tabs (GtkWidget *widget) {
 
 /*=================================================================*/
 
+static void warn_constaints(char *constr_message) {
+	/* redirect stderr to dialog */
+	g_set_printerr_handler((GPrintFunc) error_box);
+	constr_message = g_strdup_printf(_("CONFLICTING CHOICES!\n\n%s\n"
+				"Please change options to eliminate conflicts."),
+				constr_message);
+	g_printerr(constr_message);
+	g_free(constr_message);
+}
 
 /*
  * Grabs the advanced options from the ppd file and fills 
@@ -1015,21 +887,9 @@ void fill_option_menus(GtkWidget * button, gpointer ppd)
   ppd_struct *local_ppd;	/* local copy of ppd struct */
   char *local_file;		/* local ppd file name string */
 
-  int i, l, m;			/* looping vars */
-
-  int option_count;		/* count of non-install options */
-  int inst_option_count;	/* count of installable options */
-
+  struct PrinterOption *option;
   char *match_title = NULL;	/* title of matched tab */
 
-  GtkWidget *pagesize_combo_list;	/* page size option combo */
-
-  /* pointers to mark our position in the ppd struct */
-  PpdGroup *group_ptr;		/* pointer to current group */
-  PpdGroup *subgroup_ptr;	/* pointer to current subgroup */
-  PpdOption *option_ptr;	/* pointer to current option */
-  PpdOption *sub_option_ptr;	/* pointer to current option under a subgroup */
-
   /* 
    * set up an array of widget pointers for the 
    * printer specific items
@@ -1044,10 +904,6 @@ void fill_option_menus(GtkWidget * button, gpointer ppd)
   GtkWidget *hbox2;
   GtkWidget *iopt_combo;
 
-
-  /* List iterators */
-  GSList *glist, *olist, *sglist;
-
   local_ppd = ppd;
   local_file = local_ppd->ppd_file;
 
@@ -1062,9 +918,11 @@ void fill_option_menus(GtkWidget * button, gpointer ppd)
     /* No error message here, shoud have caught this above */
     return;
   }
-  local_ppd->ppd_handle = ppd_file_new(local_file);
+  if (local_ppd->options)
+	  g_object_unref(local_ppd->options);
+  local_ppd->options = printer_options_new(local_file);
 
-  if (local_ppd->ppd_handle == NULL) {
+  if (local_ppd->options == NULL) {
     // no error message, done in previous function
     return;
   }
@@ -1101,27 +959,8 @@ void fill_option_menus(GtkWidget * button, gpointer ppd)
   /* Remove any extra tabs that were previously created. */
   remove_extra_notebook_tabs (local_ppd->opt_vbox);
 
-  option_count = 0;
-  inst_option_count = 0;
-
-  /* Loop through groups */
-  glist = local_ppd->ppd_handle->groups;
-  while (glist) {
-    group_ptr = glist->data;
-    glist = g_slist_next(glist);
-    /* we want everything but the installable options */
-    if (!((g_strcasecmp((group_ptr->text->str), "Options Installed") == 0)
-	  || (g_strcasecmp((group_ptr->text->str), "InstallableOptions")
-	      == 0)
-	  || (g_strcasecmp((group_ptr->text->str), "Installable Options") == 0)
-	  || (g_strcasecmp((group_ptr->text->str), "Installed Options")
-	      == 0))) {
-
-      /* Loop through options */
-      olist = group_ptr->options;
-      while (olist) {
-	option_ptr = olist->data;
-	olist = g_slist_next(olist);
+  for (option = local_ppd->options->options ; option != NULL ; option = option->next) {
+	PpdOption *option_ptr = option->option;
 
 	if (g_strcasecmp((option_ptr->keyword->str), "PageRegion") == 0) {
 	  continue;
@@ -1130,14 +969,11 @@ void fill_option_menus(GtkWidget * button, gpointer ppd)
 	/* need to handle the PageSize case separately */
 	if (g_strcasecmp((option_ptr->keyword->str), "PageSize") == 0) {
 
-	  pagesize_combo_list =
-	    gpr_combo_make(local_ppd, option_count, option_ptr,
-			   local_ppd->pagesize_optionmenu, 0, select_option_choice);
-
+  	  combo_box_mode_set(local_ppd->pagesize_optionmenu,
+			  local_ppd->options, option, warn_constaints);
 	  gtk_widget_show_all(local_ppd->pagesize_optionmenu);
-	  ++option_count;
-
-	} else {		/* if this is not the PageSize option */
+	  continue;
+	}		/* if this is not the PageSize option */
 #ifdef HAVE_XML
 	  match_title = tab_match (option_ptr->text->str, globalTabList);
 #else
@@ -1172,81 +1008,26 @@ void fill_option_menus(GtkWidget * button, gpointer ppd)
 	    gpr_label_make(option_ptr->text->str, hbox);
 
 	    opt_combo = gpr_optcombo_make(hbox);
-	    opt_combo =
-	      gpr_combo_make(local_ppd, option_count, 
-			    option_ptr, opt_combo, 0,
-			    select_option_choice);
+	    combo_box_mode_set(opt_combo, local_ppd->options, option,
+			    warn_constaints);
 
 	    gtk_widget_show_all (extra_tabs[tab_pos].vbox);
-	    ++option_count;
 	  }
 	  else {
 	    hbox = gpr_hbox_make(local_ppd->opt_vbox);
 	    gpr_label_make(option_ptr->text->str, hbox);
 
 	    opt_combo = gpr_optcombo_make(hbox);
-	    opt_combo =
-	      gpr_combo_make(local_ppd, option_count, option_ptr, opt_combo, 0,
-			    select_option_choice);
-
-	    ++option_count;
+	    combo_box_mode_set(opt_combo, local_ppd->options, option,
+			    warn_constaints);
 	  }
-	}
-      }
-
-      /* now go into subgroups  */
-      /* Loop through subgroups */
-      sglist = group_ptr->subgroups;
-      l = -1;
-      while (sglist) {
-	l++;
-	subgroup_ptr = sglist->data;
-	sglist = g_slist_next(sglist);
-
-	/* Loop through options */
-	olist = subgroup_ptr->options;
-	m = -1;
-	while (olist) {
-	  m++;
-	  sub_option_ptr = olist->data;
-	  olist = g_slist_next(olist);
-
-	  hbox = gpr_hbox_make(local_ppd->opt_vbox);
-	  gpr_label_make(sub_option_ptr->text->str, hbox);
-	  opt_combo = gpr_optcombo_make(hbox);
-
-	  opt_combo =
-	    gpr_combo_make(local_ppd, option_count, sub_option_ptr,opt_combo,0,
-			   select_option_choice);
-
-	  ++option_count;
-
-	}
-      }
-    }
-
-    /* now, we want only the installable option (assume no subgroups) */
-    else {
-      /* Loop through options */
-      olist = group_ptr->options;
-      i = -1;
-      while (olist) {
-	option_ptr = olist->data;
-	i++;
-	olist = g_slist_next(olist);
-
+  }
+  for (option = local_ppd->options->installable ; option != NULL ; option = option->next) {
 	hbox2 = gpr_hbox_make(local_ppd->inst_opt_vbox);
-	gpr_label_make(option_ptr->text->str, hbox2);
+	gpr_label_make(option->option->text->str, hbox2);
 	iopt_combo = gpr_optcombo_make(hbox2);
-
-	iopt_combo = 
-	  gpr_combo_make(local_ppd, inst_option_count, option_ptr,iopt_combo,1,
-			 select_inst_option_choice);
-
-	++inst_option_count;
-
-      }
-    }
+	combo_box_mode_set(iopt_combo, local_ppd->options, option,
+			    warn_constaints);
   }
 
   gtk_widget_show_all(local_ppd->opt_vbox);
@@ -1254,275 +1035,6 @@ void fill_option_menus(GtkWidget * button, gpointer ppd)
 
 }
 
-/*=================================================================*/
-gchar * conflict_check 
-(
- ppd_struct *local_ppd,
- GSList *oplist, 
- gchar *combined_string,
- gchar *question_string
-) {
-/*-------------------------------------------------------------------
-  Check for conflicts based on "constraints" between options found
-  in the PPD file.
--------------------------------------------------------------------*/
-  gchar *temp_string;		/* a temporary string */
-  gchar *constr_message = NULL;	/* the message to appear in the pop-up
-				   constraint box */
-  gchar *astr = NULL;
-  gchar *bstr = NULL;
-  GSList *top = oplist;
-  int times = 0;
-
-  astr = question_string;
-  bstr = combined_string;
-
-  while ((times <= 1) && (constr_message == NULL)) {
-    oplist = top;
-
-    while (oplist) {
-      astr = oplist->data;
-      oplist = g_slist_next(oplist);
-
-      if (times == 0) 
-	temp_string = g_strjoin(":", astr, bstr, NULL);
-      else
-	temp_string = g_strjoin(":", bstr, astr, NULL);
-
-      if (g_hash_table_lookup(local_ppd->constr_hash, temp_string)) {
-
-	/* stick something in the string so there is no error */
-	if (constr_message == NULL)
-	  constr_message = g_strdup("\0");
-
-	constr_message =
-	  g_strconcat(constr_message,
-		      "Option \"", 
-		      g_hash_table_lookup(local_ppd->cr_to_hr, bstr),
-		      "\"\n conflicts with option\n\"", 
-		      g_hash_table_lookup(local_ppd->cr_to_hr, astr),
-		      "\"\n", NULL);
-      }
-    }
-
-    times++;
-
-  }
-
-  return constr_message;
-}
-
-/*
- * Adds the choice from the advanced options tab to the
- * list of selected options and checks for any constraints
- */
-void select_option_choice(GtkWidget * option_entry, gpointer ppd)
-{
-  gchar *object_keyword;	/* object keyword string */
-  gchar *choice_keyword;	/* choice keyword string */
-  gchar *combined_string;	/* object keyword:choice keyword */
-  int index;			/* the index number associated with the item */
-  ppd_struct *local_ppd;	/* local copy of ppd struct */
-  gchar *question_string = NULL;/* value to hash on */
-  gchar *constr_message = NULL;	/* the message to appear in the pop-up
-				   constraint box */
-
-  GSList *oplist, *ioplist;
-  GList *combo_item_list;
-  GtkWidget *option_combo, *combo_item = NULL;
-  local_ppd = ppd;
-
-  /* redirect stderr to dialog */
-  g_set_printerr_handler((GPrintFunc) error_box);
-
-  /* Search the chosen item in the list */
-  option_combo = gtk_object_get_data(GTK_OBJECT(option_entry), OC);
-
-#if USE_COMBO_BOX
-  choice_keyword = gtk_combo_box_get_active_text(GTK_COMBO_BOX(option_combo)) ;
-#else
-  combo_item_list = gtk_container_children(GTK_CONTAINER(GTK_COMBO(option_combo)->list));
-  while (combo_item_list) {
-    combo_item = combo_item_list->data;
-    if (g_strcasecmp(gtk_object_get_data(GTK_OBJECT(combo_item), CT),
-		     gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(option_combo)->entry))) == 0) {
-      break;
-    } else {
-      combo_item_list = g_list_next(combo_item_list);
-    } 
-  }
-  object_keyword = g_strdup(gtk_object_get_data(GTK_OBJECT(combo_item), OK));
-  choice_keyword = g_strdup(gtk_object_get_data(GTK_OBJECT(combo_item), CK));
-#endif
-
-
-  combined_string = g_strjoin(":", object_keyword, choice_keyword, NULL);
-
-  index = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(combo_item), IN));
-
-  /* 
-   * need to add this string to the linked list of choices
-   * need to associate an index with the choice to insert at the proper
-   * spot in the linked list
-   */
-  local_ppd->choice_list =
-    my_g_slist_replace(local_ppd->choice_list, combined_string, index);
-
-  /* 
-   * To check for conflicts, we will build a hash table of conflicts
-   * in 'init_ppd'. Each choice will consult the hash table for a conflict
-   * and display a pop-up dialog with all conflicting choices
-   */
-
-  /* conflict check option list */
-  oplist = local_ppd->choice_list;
-  constr_message = conflict_check (local_ppd, oplist, 
-				   combined_string, question_string);
-
-  /* now conflict check installable option list */
-  if (constr_message == NULL) {
-    ioplist = local_ppd->inst_opt_list;
-    constr_message = conflict_check (local_ppd, ioplist, 
-				     combined_string, question_string);
-  }
-
-  /* show constraint dialog if there are any conflicts */
-  if (constr_message != NULL) {
-    constr_message =
-      g_strdup_printf(_("CONFLICTING CHOICES!\n\n%s\n"
-			"Please change options to eliminate conflicts."),
-		        constr_message);
-    g_printerr(constr_message);
-  }
-}
-
-
-/*
- * Adds the choice from the installable options tab to the
- * list of installable options and checks for any constraints
- */
-void select_inst_option_choice(GtkWidget * option_entry, gpointer ppd)
-{
-  gchar *object_keyword;	/* object keyword string */
-  gchar *choice_keyword;	/* choice keyword string */
-  gchar *combined_string;	/* object keyword:choice keyword */
-  int index;			/* the index number associated with the item */
-  ppd_struct *local_ppd;	/* local copy of ppd struct */
-  gchar *question_string = NULL;/* value to hash on */
-  gchar *constr_message = NULL;	/* the message to appear in the pop-up
-				   constraint box */
-
-  GSList *oplist, *ioplist;
-  GList *combo_item_list;
-  GtkWidget *option_combo, *combo_item = NULL;
-  local_ppd = ppd;
-
-  /* redirect stderr to dialog */
-  g_set_printerr_handler((GPrintFunc) error_box);
-
-#if USE_COMBO_BOX
-  choice_keyword = gtk_combo_box_get_active_text(GTK_COMBO_BOX(option_combo)) ;
-#else
-  /* Search the chosen item in the list */
-  option_combo = gtk_object_get_data(GTK_OBJECT(option_entry), OC);
-  combo_item_list = gtk_container_children(GTK_CONTAINER(GTK_COMBO(option_combo)->list));
-  while (combo_item_list) {
-    combo_item = combo_item_list->data;
-    if (g_strcasecmp(gtk_object_get_data(GTK_OBJECT(combo_item), CT),
-		     gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(option_combo)->entry))) == 0) {
-      break;
-    } else {
-      combo_item_list = g_list_next(combo_item_list);
-    } 
-  }
-  object_keyword = g_strdup(gtk_object_get_data(GTK_OBJECT(combo_item), OK));
-  choice_keyword = g_strdup(gtk_object_get_data(GTK_OBJECT(combo_item), CK));
-#endif
-
-  combined_string = g_strjoin(":", object_keyword, choice_keyword, NULL);
-
-  index = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(combo_item), IN));
-
-  /* 
-   * need to add this string to the linked list of choices
-   * need to associate an index with the choice to insert at the proper
-   * spot in the linked list
-   */
-  local_ppd->inst_opt_list =
-    my_g_slist_replace(local_ppd->inst_opt_list, combined_string, index);
-
-  /* 
-   * To check for conflicts, we will build a hash table(?) of conflicts
-   * in 'init_ppd'. Each choice will consult the hash table for a conflict
-   * and display a pop-up dialog with all conflicting choices
-   */
-
-  /* check printer-specific option list for constraints */
-  oplist = local_ppd->choice_list;
-  constr_message = conflict_check (local_ppd, oplist, 
-				   combined_string, question_string);
-
-  /* now conflict-check installable option list */
-  if (constr_message == NULL) {
-    ioplist = local_ppd->inst_opt_list;
-    constr_message = conflict_check (local_ppd, ioplist, 
-				     combined_string, question_string);
-  }
-
-  /* if there are conflicts, show the constraint dialog */
-  if (constr_message != NULL) {
-    constr_message =
-	g_strdup_printf(_("CONFLICTING CHOICES!\n\n%s\n"
-		      "Please change options to eliminate conflicts."),
-		      constr_message);
-    g_printerr(constr_message);
-  }
-}
-
-
-/*
- * Assigns some data to each PPD choice that is appended to a combo
- */
-void choice_process(PpdOption * option, PpdChoice * choice, GtkWidget * combo,
-		    int interator, gboolean inst_opt, GtkWidget * combo_item,
-		    int count, gpointer ppd)
-{
-  gchar *key_string;		/* key value to serach in a hash table */
-  gchar *value_string;		/* return value from hash */
-  gchar *temp_string;		/* temporary string */
-  ppd_struct *local_ppd;
-
-  local_ppd = ppd;
-  gtk_object_set_data(GTK_OBJECT(combo_item), OT, option->text->str);
-  gtk_object_set_data(GTK_OBJECT(combo_item), OK, option->keyword->str);
-  gtk_object_set_data(GTK_OBJECT(combo_item), CT, choice->text->str);
-  gtk_object_set_data(GTK_OBJECT(combo_item), CK, choice->choice->str);
-  gtk_object_set_data(GTK_OBJECT(combo_item), IN, GINT_TO_POINTER(count));
-  //printf("IN %d OK %s OT %s CK %s CT %s \n" ,count,
-  //	 option->keyword->str,option->text->str,
-  //	 choice->choice->str,choice->text->str);
-
-  key_string = g_strjoin(":", option->keyword->str, choice->choice->str, NULL);
-  value_string = g_strjoin(":", option->text->str, choice->text->str, NULL);
-
-  g_hash_table_insert(local_ppd->cr_to_hr, key_string, value_string);
-
-  if (choice->marked) {
-    revalidate_utf8(choice->text->str);
-    gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo)->entry), choice->text->str);
-    
-    temp_string =
-      g_strjoin(":", option->keyword->str, choice->choice->str, NULL);
-    if (inst_opt)
-      local_ppd->inst_opt_list=
-	my_g_slist_replace(local_ppd->inst_opt_list,temp_string,count);
-    else
-      local_ppd->choice_list=
-	my_g_slist_replace(local_ppd->choice_list,temp_string,count);
-  }
-
-  return;
-}
 
 /*
  * END ADVANCED/PPD OPTION CALLBACKS
@@ -1537,12 +1049,11 @@ void choice_process(PpdOption * option, PpdChoice * choice, GtkWidget * combo,
  */
 void save_sett(GtkWidget * button, gpointer ppd)
 {
+  struct PrinterOption *o;
   ppd_struct *local_ppd;	/* local copy of ppd struct */
   FILE *settlist;		/* file containing a list of settings for this
 				   printer */
   FILE *setting;		/* the actual file list all of the settings */
-  gchar *current_option;	/* the string for the current option */
-  gchar *current_choice;	/* the string for the current choice */
   gchar *sett_name;		/* the name of the saved settings file */
   char curr_line[80];		/* a line of file input */
   gchar *curr_sett_name;	/* the name of the current setting in the file */
@@ -1551,8 +1062,6 @@ void save_sett(GtkWidget * button, gpointer ppd)
 				   or one needing updating */
   GtkWidget *menu_item;		/* a menu item */
 
-  GSList *olist, *clist;
-
   local_ppd = ppd;
 
   /* redirect stderr to dialog */
@@ -1627,22 +1136,18 @@ void save_sett(GtkWidget * button, gpointer ppd)
       fprintf(setting, "CO:%s\n", local_ppd->output_order);
     /* end non-ppd options */
 
-    /* move through the linked list of installable options */
-    olist = local_ppd->inst_opt_list;
-    while (olist) {
-      current_option = olist->data;
-      olist = g_slist_next(olist);
-      fprintf(setting, "IO:%s\n", current_option);
+    for (o = local_ppd->options->installable ; o != NULL ; o = o->next) {
+	if (o->selected_index < 0)
+		continue;
+	fprintf(setting, "IO:%s:%s\n", o->option->keyword->str,
+			o->choices[o->selected_index].choice->choice->str);
     }
-
-    /* move through the linked list of choices */
-    clist = local_ppd->choice_list;
-    while (clist) {
-      current_choice = clist->data;
-      clist = g_slist_next(clist);
-      fprintf(setting, "AO:%s\n", current_choice);
+    for (o = local_ppd->options->options ; o != NULL ; o = o->next) {
+	if (o->selected_index < 0)
+		continue;
+	fprintf(setting, "AO:%s:%s\n", o->option->keyword->str,
+			o->choices[o->selected_index].choice->choice->str);
     }
-
     fclose(setting);
   }
 
@@ -2066,104 +1571,17 @@ static GtkWidget *gpr_label_make(gchar * str, GtkWidget * hbox)
 static GtkWidget *gpr_optcombo_make(GtkWidget * hbox)
 {
   GtkWidget *opt_combo;
+  GtkCellRenderer *cell;
 
-#if USE_COMBO_BOX
-  opt_combo = gtk_combo_box_new_text();
-#else
-  opt_combo = gtk_combo_new();
-#endif
-  gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(opt_combo)->entry), FALSE);
-  //USELESS?? gtk_widget_ref(opt_combo);
-  gtk_box_pack_start(GTK_BOX(hbox), opt_combo, FALSE, FALSE, 0);
-  //gtk_widget_set_usize(opt_combo, 220, -2);
-  //USELESS?? gtk_widget_realize(opt_combo);
-
+  opt_combo = gtk_combo_box_new();
+  cell = gtk_cell_renderer_text_new();
+  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(opt_combo), cell, TRUE);
+  gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(opt_combo), cell, "text", 0, NULL);
+  gtk_box_pack_start(GTK_BOX(hbox), opt_combo, FALSE, TRUE, 0);
   return opt_combo;
 }
 
 /*-------------------------------------------------------------------
-  Used by fill_option_menus() to build a combo and its list items.
--------------------------------------------------------------------*/
-static GtkWidget *gpr_combo_make(ppd_struct * local_ppd, int option_count,
-				PpdOption * option_ptr,	/* pointer to current
-							   option */
-				GtkWidget * option_combo, gboolean inst_opt,
-				GtkSignalFunc signal_func_ptr)
-{
-  int idx;
-  GSList *clist;
-  PpdChoice *choice_ptr;	/* pointer to current choice */
-
-  clist = option_ptr->choices;
-  idx = -1;
-  while (clist) {
-    choice_ptr = clist->data;
-    clist = g_slist_next(clist);
-    idx++;
-    /* glist = g_list_append(glist, choice_ptr->text->str); */
-    gpr_combo_choices_make(idx, local_ppd, option_count, option_ptr, option_combo,inst_opt);
-    //signal_func_ptr);
-  }
-
-  gtk_object_set_data(GTK_OBJECT(GTK_COMBO(option_combo)->entry), OC, option_combo);
-
-  /* handle selection of a combo item */
-  gtk_signal_connect(GTK_OBJECT(GTK_COMBO(option_combo)->entry), "changed",
-		     GTK_SIGNAL_FUNC(signal_func_ptr), local_ppd);
-
-  return option_combo;
-}
-
-/*-------------------------------------------------------------------
-  Used by gpr_combo_make() to fill in a combo item and its 
-  callback function.
--------------------------------------------------------------------*/
-
-static void gpr_combo_choices_make(int idx, ppd_struct * local_ppd,
-				  int option_count, PpdOption * option_ptr,
-				   /* pointer to current option */
-				   GtkWidget * option_combo, gboolean inst_opt)
-//GtkSignalFunc signal_func_ptr)
-{
-#if USE_COMBO_BOX
-  gtk_combo_box_append(GTK_COMBO_BOX(option_combo), g_strdup(g_slist_nth_data(option_ptr->choices, idx));
-#else
-
-  PpdChoice *choice_ptr;	/* pointer to current choice */
-  gchar *choice_label;
-  
-
-  choice_ptr = g_slist_nth_data(option_ptr->choices, idx);
-
-  choice_label = g_strdup(choice_ptr->text->str);
-
-
-  GtkWidget *item, *label;
-  item = gtk_list_item_new();
-  gtk_widget_show (item);
-  revalidate_utf8(choice_label);
-  label = gtk_label_new(choice_label);
-  gtk_container_add (GTK_CONTAINER (item), label);
-  gtk_widget_show (label);
-  gtk_combo_set_item_string (GTK_COMBO (option_combo), GTK_ITEM (item),
-			     choice_label);
-  gtk_container_add (GTK_CONTAINER (GTK_COMBO (option_combo)->list), item);
-  /* associate strings with each object for retrieval later */
-  /* add to hash table */
-  /* deal with defaults or presets */
-  choice_process(option_ptr, choice_ptr, option_combo, idx, inst_opt,
-		 item, option_count, (gpointer) local_ppd);
-
-  //USELESS?? gtk_widget_realize(item);
-
-  /* handle selection of a combo item */
-  /*gtk_signal_connect(item, "activate",
-    GTK_SIGNAL_FUNC(signal_func_ptr), local_ppd);*/
-#endif
-}
-
-
-/*-------------------------------------------------------------------
 -------------------------------------------------------------------*/
 void set_common_option_defaults (ppd_struct * local_ppd) {
   GtkWidget *target = NULL;
@@ -2244,51 +1662,6 @@ void set_common_option (char *line, ppd_struct * local_ppd) {
 }
 
 /*-------------------------------------------------------------------
--------------------------------------------------------------------*/
-void print_ppd_data (ppd_struct *local_ppd) {
-  /* List iterators */
-  GSList *glist, *olist, *clist;
-  /* pointers to mark our position in the ppd struct */
-  PpdGroup *group_ptr;		/* pointer to current group */
-  PpdOption *option_ptr;	/* pointer to current option */
-  PpdChoice *choice_ptr;	/* pointer to current choice */
-  char marked;
-
-  /* Loop through groups */
-  glist = local_ppd->ppd_handle->groups;
-  while (glist) {
-    group_ptr = glist->data;
-    glist = g_slist_next(glist);
-
-    printf ("glist (%s)\n", group_ptr->text->str);
-    /* we want everything but the installable options */
-
-    /* Loop through options */
-    olist = group_ptr->options;
-    while (olist) {
-      option_ptr = olist->data;
-      olist = g_slist_next(olist);
-
-      printf ("\tolist (%s)\n", option_ptr->keyword->str);
-
-      clist = option_ptr->choices;
-      printf ("\tdefchoice (%s)\n", option_ptr->defchoice->str);
-
-      while (clist) {
-	choice_ptr = clist->data;
-	clist = g_slist_next(clist);
-	marked = ' ';
-	if (choice_ptr->marked)
-	  marked = '*';
-
-	printf ("\t\tclist %c (%s)(%s)\n", marked, choice_ptr->choice->str, choice_ptr->text->str);
-      }
-    }
-  }
-
-}
-
-/*-------------------------------------------------------------------
   Used by fill_option_menus() to set any saved PPD options.
 -------------------------------------------------------------------*/
 static void gpr_set_ppd_saved_options(ppd_struct * local_ppd	/* local copy
@@ -2313,8 +1686,6 @@ static void gpr_set_ppd_saved_options(ppd_struct * local_ppd	/* local copy
 		      g_strconcat(local_ppd->config_dir, "/savesetts/",
 				  local_ppd->setting_name, NULL), "r");
 
-  /* go ahead and mark defaults options in the ppd */
-  ppd_mark_defaults(local_ppd->ppd_handle);
 
   set_common_option_defaults (local_ppd);
 
@@ -2329,7 +1700,7 @@ static void gpr_set_ppd_saved_options(ppd_struct * local_ppd	/* local copy
 	end = strchr(curr_line, '\n');
 	if (end)
 	  *end = '\0';
-	ppd_mark_option(local_ppd->ppd_handle, "PageSize", curr_line);
+	printer_options_select(local_ppd->options, "PageSize", curr_line);
       }
       fclose(f);
     }
@@ -2364,7 +1735,7 @@ static void gpr_set_ppd_saved_options(ppd_struct * local_ppd	/* local copy
 	if (choice)
 	  *choice = '\0';
 	++choice;
-	ppd_mark_option(local_ppd->ppd_handle, option, choice);
+	printer_options_select(local_ppd->options, option, choice);
 	continue;
       } else
 	continue;
@@ -2374,16 +1745,6 @@ static void gpr_set_ppd_saved_options(ppd_struct * local_ppd	/* local copy
 }
 
 /*
- * Returns TRUE to force the removal of a hash entry
- */
-static gboolean ensure_remove_hash_item(gpointer key, gpointer value,
-					gpointer user_data)
-{
-  return (TRUE);
-}
-
-
-/*
  * Function to determine whether an executable file is in the user's
  * path
  * Greatly Inspired by Craig Tanis <crt@weblar.org>
diff --git a/src/callbacks.h b/src/callbacks.h
index bf196a3..406263b 100644
--- a/src/callbacks.h
+++ b/src/callbacks.h
@@ -83,7 +83,3 @@ void update_nup_4up(GtkWidget * radiobutton, gpointer ppd);
 /* UI Modification callbacks */
 void ungrey_entrybox(GtkWidget * button, gpointer entrybox);
 void regrey_entrybox(GtkWidget * button, gpointer entrybox);
-
-/* this is a preliminary work to substitute gtk_combo with gtk_combo_box 
-  it does not yet work */
-#define USE_COMBO_BOX 0
diff --git a/src/interface.c b/src/interface.c
index 039de48..adc0d65 100644
--- a/src/interface.c
+++ b/src/interface.c
@@ -61,11 +61,6 @@
 #define PN "printer name"
 #define SD "spool directory"
 #define PF "PPD file"
-#define OT "option text"
-#define OK "option keyword"
-#define CT "choice text"
-#define CK "choice keyword"
-#define IN "index"
 #define SS "saved settings"
 
 #define MASTER_PPDDIR "/usr/share/postscript/ppd"
@@ -602,12 +597,7 @@ GtkWidget *create_gpr_main_window(ppd_struct * ppd,
   gtk_box_pack_start(GTK_BOX(hbox8), pagesize_label, FALSE, FALSE, 5);
 
   /* page size option menu */ 
-#if USE_COMBO_BOX
   local_ppd->pagesize_optionmenu = gtk_combo_box_new_text();
-#else
-  local_ppd->pagesize_optionmenu = gtk_combo_new();
-  gtk_entry_set_editable(GTK_ENTRY(GTK_COMBO(local_ppd->pagesize_optionmenu)->entry), FALSE);
-#endif
   gtk_widget_show(local_ppd->pagesize_optionmenu);
   gtk_box_pack_start(GTK_BOX(hbox8), local_ppd->pagesize_optionmenu, 
 		     FALSE, FALSE, 0);
diff --git a/src/interface.h b/src/interface.h
index 2ac7d18..48b5e68 100644
--- a/src/interface.h
+++ b/src/interface.h
@@ -34,6 +34,7 @@
 
 #include <gtk/gtk.h>
 #include <ppd.h>
+#include <printeroptions.h>
 
 #define PRINTCAP "/etc/printcap"
 #define PF "PPD file"
@@ -65,7 +66,7 @@ typedef struct {
   char *ppd_file;		/* file name of the ppd (plus the path) */
   char *default_ppd_file;	/* file name of the system default ppd (plus
 				   the path) */
-  PpdFile *ppd_handle;		/* ppd file structure */
+  PrinterOptions *options;
   gchar *lpr_opts;		/* options to be passed directly to lpr */
   gchar *printer_name;		/* printer name */
   gchar *spool_dir;		/* spool directory */
@@ -81,14 +82,9 @@ typedef struct {
   GtkWidget *inst_opt_window;	/* may not need this here */
   GtkWidget *inst_opt_vbox;	/* installable options container */
   GtkWidget *inst_opt_viewport;	/* installable options viewport */
-  GSList *choice_list;		/* linked list of job-specific choices */
-  GSList *inst_opt_list;	/* linked list of installable options */
   GtkWidget *savesett_optionmenu;	/* saved settings option menu */
   GtkWidget *savesett_optionmenu_menu;	/* saved settings optionmenu menu */
   GtkWidget *savesett_textentry;	/* text box for entering settings name */
-  GHashTable *constr_hash;	/* hash table of constraints */
-  GHashTable *cr_to_hr;		/* hash table to translate computer readable to 
-				   human readable */
   char *num_copies;		/* Non-PPD: number of copies */
   char *n_up;			/* Non-PPD: n-up option */
   char *page_range;		/* Non-PPD: the range of pages to print */
diff --git a/src/optionstore.c b/src/optionstore.c
new file mode 100644
index 0000000..b3010ac
--- /dev/null
+++ b/src/optionstore.c
@@ -0,0 +1,213 @@
+/* Copyright 2011 Bernhard R. Link
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ */
+
+#include "config.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <gtk/gtktreemodel.h>
+#include <gtk/gtkcombobox.h>
+#include <gtk/gtksignal.h>
+#include "printeroptions.h"
+#include "optionstore.h"
+
+struct _OptionStore {
+	GObject parent;
+	PrinterOptions *options;
+	struct PrinterOption *option;
+	void (*constraint_callback)(char *);
+};
+struct _OptionStoreClass {
+	GObjectClass parent_class;
+};
+
+static void option_store_finalize(GObject *);
+static void option_store_class_init(OptionStoreClass *);
+
+static GtkTreeModelFlags option_store_get_flags(GtkTreeModel *m) {
+	return GTK_TREE_MODEL_LIST_ONLY | GTK_TREE_MODEL_ITERS_PERSIST;
+}
+
+static int option_store_get_n_columns(GtkTreeModel *m) {
+	return 1;
+}
+static GType option_store_get_column_type(GtkTreeModel *m, int index) {
+	return G_TYPE_STRING;
+}
+static gboolean option_store_get_iter(GtkTreeModel *tm, GtkTreeIter *iter, GtkTreePath *path) {
+	OptionStore *os = (OptionStore*)tm;
+	int i = gtk_tree_path_get_indices (path)[0];
+
+	iter->stamp = 0;
+	iter->user_data = NULL;
+	if (os->option == NULL)
+		return FALSE;
+	iter->stamp = 17;
+	iter->user_data = GINT_TO_POINTER(i+1);
+	return TRUE;
+}
+
+static GtkTreePath *option_store_get_path(GtkTreeModel *tm, GtkTreeIter *iter) {
+	OptionStore *os = (OptionStore*)tm;
+	int i;
+
+	if (iter->stamp != 17 || os->option == NULL)
+		return NULL;
+	i = GPOINTER_TO_INT(iter->user_data);
+	if (i <= 0)
+		return NULL;
+	return gtk_tree_path_new_from_indices (i-1, -1);
+}
+
+static void option_store_get_value(GtkTreeModel *tm, GtkTreeIter *iter, int column, GValue *v) {
+	OptionStore *os = (OptionStore*)tm;
+	PpdChoice *choice_ptr;
+	int i;
+
+	g_value_init(v, G_TYPE_STRING);
+	if (column != 0)
+		return;
+	if (iter->stamp != 17 || os->option == NULL)
+		return;
+	i = GPOINTER_TO_INT(iter->user_data) - 1;
+	if (i < 0 || i >= os->option->count)
+		return;
+	choice_ptr = g_slist_nth_data(os->option->option->choices, i);
+	g_value_set_string(v, choice_ptr->text->str);
+}
+
+static gboolean option_store_iter_next(GtkTreeModel *tm, GtkTreeIter *iter) {
+	OptionStore *os = (OptionStore*)tm;
+	int i;
+
+	if (os->option == NULL || iter->stamp != 17)
+		return FALSE;
+	i = GPOINTER_TO_INT(iter->user_data);
+	if (i <= 0 || i > os->option->count) {
+		iter->stamp = 0;
+		iter->user_data = GINT_TO_POINTER(0);
+		return FALSE;
+	}
+	iter->user_data = GINT_TO_POINTER(i+1);
+	return TRUE;
+}
+
+static gboolean option_store_iter_children(GtkTreeModel *tm, GtkTreeIter *iter, GtkTreeIter *parent) {
+	OptionStore *os = (OptionStore*)tm;
+
+	if (parent != NULL)
+		return FALSE;
+	if (os->option == NULL)
+		return FALSE;
+	iter->stamp = 17;
+	iter->user_data = GINT_TO_POINTER(1);
+	return FALSE;
+}
+
+static gboolean option_store_iter_has_child(GtkTreeModel *tm, GtkTreeIter *iter) {
+	return FALSE;
+}
+static int option_store_iter_n_children(GtkTreeModel *tm, GtkTreeIter *iter) {
+	OptionStore *os = (OptionStore*)tm;
+
+	if (iter != NULL || os->option == NULL )
+		return 0;
+	else
+		return os->option->count;
+}
+static int option_store_iter_nth_child(GtkTreeModel *tm, GtkTreeIter *iter, GtkTreeIter *parent, int n) {
+	OptionStore *os = (OptionStore*)tm;
+
+	if (parent != NULL || os->option == NULL)
+		return FALSE;
+	if (n < 0 || n >= os->option->count)
+		return FALSE;
+	iter->user_data = GINT_TO_POINTER(n+1);
+	iter->stamp = 17;
+	return TRUE;
+}
+static int option_store_iter_parent(GtkTreeModel *tm, GtkTreeIter *iter, GtkTreeIter *child) {
+	return FALSE;
+}
+
+static void option_store_tree_model_init(GtkTreeModelIface *iface) {
+	iface->get_flags = option_store_get_flags;
+	iface->get_n_columns = option_store_get_n_columns;
+	iface->get_column_type = option_store_get_column_type;
+	iface->get_iter = option_store_get_iter;
+	iface->get_path = option_store_get_path;
+	iface->get_value = option_store_get_value;
+	iface->iter_next = option_store_iter_next;
+	iface->iter_children = option_store_iter_children;
+	iface->iter_has_child = option_store_iter_has_child;
+	iface->iter_n_children = option_store_iter_n_children;
+	iface->iter_nth_child = option_store_iter_nth_child;
+	iface->iter_parent = option_store_iter_parent;
+}
+
+static void option_store_init(OptionStore *store) {
+}
+
+G_DEFINE_TYPE_WITH_CODE(OptionStore, option_store, G_TYPE_OBJECT,
+			G_IMPLEMENT_INTERFACE(GTK_TYPE_TREE_MODEL,
+				option_store_tree_model_init))
+
+static void option_store_finalize(GObject *o) {
+	g_object_unref(((OptionStore*)o)->options);
+	G_OBJECT_CLASS (option_store_parent_class)->finalize (o);
+}
+
+static void option_store_class_init(OptionStoreClass *class) {
+	GObjectClass *o = (GObjectClass*) class;
+	o->finalize = option_store_finalize;
+}
+
+GtkTreeModel * option_store_new(PrinterOptions *po, struct PrinterOption *o, void (*callback)(char *)) {
+	OptionStore *os = g_object_new(TYPE_OPTION_STORE, NULL);
+
+	os->options = g_object_ref(po);
+	os->option = o;
+	os->constraint_callback = callback;
+	return GTK_TREE_MODEL(os);
+}
+
+int option_store_get_default(OptionStore *os) {
+	return 0;
+}
+
+static void change_selection(GtkWidget *widget, void *privdata) {
+	GtkComboBox *combobox = GTK_COMBO_BOX(widget);
+	OptionStore *os = OPTION_STORE(gtk_combo_box_get_model(combobox));
+	int i = gtk_combo_box_get_active(combobox);
+	struct PrinterOption *o = os->option;
+	char *message;
+
+	message = printer_option_select(o, i);
+	if (message != NULL) {
+		if (os->constraint_callback != NULL)
+			os->constraint_callback(message);
+		else
+			g_free(message);
+	}
+}
+
+void combo_box_mode_set(GtkWidget *combobox, PrinterOptions *options, struct PrinterOption *option, void (*callback)(char *)) {
+	GtkComboBox *cb = GTK_COMBO_BOX(combobox);
+  	GtkTreeModel *m = option_store_new(options, option, callback);
+
+	gtk_combo_box_set_model(cb, m);
+	gtk_combo_box_set_active(cb,
+		  OPTION_STORE(m)->option->selected_index);
+	gtk_signal_connect(GTK_OBJECT(combobox), "changed",
+		GTK_SIGNAL_FUNC(change_selection), NULL);
+}
diff --git a/src/optionstore.h b/src/optionstore.h
new file mode 100644
index 0000000..576aa09
--- /dev/null
+++ b/src/optionstore.h
@@ -0,0 +1,27 @@
+#ifndef OPTION_STORE_H
+#define OPTION_STORE_H
+#include <stdbool.h>
+#include <gdkconfig.h>
+#include <gtk/gtktreemodel.h>
+#include <gtk/gtkwidget.h>
+#include <ppd.h>
+#include "printeroptions.h"
+
+G_BEGIN_DECLS
+
+#define TYPE_OPTION_STORE (option_store_get_type())
+#define OPTION_STORE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_OPTION_STORE, OptionStore))
+#define OPTION_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS(CAST((klass),TYPE_OPTION_STORE, OptionStoreClass))
+
+typedef struct _OptionStore OptionStore;
+typedef struct _OptionStoreClass OptionStoreClass;
+
+
+GType option_store_get_type (void) G_GNUC_CONST;
+GtkTreeModel * option_store_new(PrinterOptions *po, struct PrinterOption *o, void (*contraint_callback)(char *));
+
+void combo_box_mode_set(GtkWidget *, PrinterOptions *, struct PrinterOption *, void (*callback)(char *));
+
+G_END_DECLS
+
+#endif
diff --git a/src/printeroptions.c b/src/printeroptions.c
new file mode 100644
index 0000000..2b054d7
--- /dev/null
+++ b/src/printeroptions.c
@@ -0,0 +1,310 @@
+/* Copyright 2011 Bernhard R. Link
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ */
+
+#include "config.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <glib-object.h>
+#include <ppd.h>
+#include "printeroptions.h"
+
+struct PrinterConstraint {
+	struct PrinterConstraint *next;
+	struct PrinterOption *option;
+	struct PrinterOptionChoice *choice;
+};
+
+struct _PrinterOptionsClass {
+	GObjectClass parent_class;
+};
+
+static void printer_options_finalize(GObject *);
+static void printer_options_class_init(PrinterOptionsClass *);
+
+static void printer_options_init(PrinterOptions *i) {
+}
+
+G_DEFINE_TYPE_WITH_CODE(PrinterOptions, printer_options, G_TYPE_OBJECT,)
+
+static void option_free(struct PrinterOption *o) {
+	int i;
+
+	if (o == NULL)
+		return;
+	for (i = 0 ; i < o->count ; i++) {
+		struct PrinterOptionChoice *h = &o->choices[i];
+
+		while (h->constraints != NULL) {
+			struct PrinterConstraint *c = h->constraints;
+			h->constraints = c->next;
+
+			free(c);
+		}
+	}
+	free(o->choices);
+	free(o);
+}
+
+static void printer_options_finalize(GObject *o) {
+	PrinterOptions *po = (PrinterOptions*)o;
+	struct PrinterOption *option;
+
+	option = po->options;
+	while (option != NULL) {
+		struct PrinterOption *h = option;
+		option = option->next;
+		option_free(h);
+	}
+	option = po->installable;
+	while (option != NULL) {
+		struct PrinterOption *h = option;
+		option = option->next;
+		option_free(h);
+	}
+	ppd_file_free(po->ppd);
+
+
+	G_OBJECT_CLASS (printer_options_parent_class)->finalize (o);
+}
+
+static void printer_options_class_init(PrinterOptionsClass *class) {
+	GObjectClass *o = (GObjectClass*) class;
+	o->finalize = printer_options_finalize;
+}
+
+static struct PrinterOption *option_new(PpdOption *option) {
+	struct PrinterOption *r;
+	GSList *sl;
+	size_t i, count = g_slist_length(option->choices);
+
+	r = g_malloc0(sizeof(struct PrinterOption) +
+			(count + 1) * sizeof(struct PrinterOptionChoice));
+	r->count = count;
+	r->option = option;
+	r->selected_index = -1;
+	i = 0;
+	for (sl = option->choices ; sl != NULL ; sl = g_slist_next(sl)) {
+		PpdChoice *choice = sl->data;
+
+		r->choices[i].choice = choice;
+		if (choice->marked)
+			r->selected_index = i;
+		i++;
+	}
+	r->choices[count].choice = NULL;
+	g_assert (count == i);
+	return r;
+}
+
+static void group_add(struct PrinterOption ***next_p, PpdGroup *group) {
+	GSList *gl, *ol;
+
+	for (ol = group->options ; ol != NULL ; ol = g_slist_next(ol)) {
+		PpdOption *option = ol->data;
+		(**next_p) = option_new(option);
+		*next_p = &(**next_p)->next;
+	}
+	for (gl = group->subgroups ; gl != NULL ; gl = g_slist_next(gl)) {
+		PpdGroup *subgroup = gl->data;
+		group_add(next_p, subgroup);
+	}
+}
+
+static struct PrinterOption *option_find(PrinterOptions *po, const char *key) {
+	struct PrinterOption *p;
+
+	for (p = po->options ; p != NULL ; p = p->next) {
+	       if (strcmp(p->option->keyword->str, key) == 0)
+		       return p;
+	}
+	for (p = po->installable ; p != NULL ; p = p->next) {
+	       if (strcmp(p->option->keyword->str, key) == 0)
+		       return p;
+	}
+	return NULL;
+}
+
+static struct PrinterOptionChoice *option_find_choice(struct PrinterOption *o, const char *choice) {
+	struct PrinterOptionChoice *c;
+
+	for (c = o->choices ; c->choice != NULL ; c++) {
+		if (strcmp(c->choice->choice->str, choice) == 0)
+			return c;
+	}
+	return NULL;
+}
+
+static void addconstraint(PrinterOptions *po, const char *key1, const char *choice1, const char *key2,  const char *choice2) {
+	struct PrinterOption *o1, *o2;
+	struct PrinterOptionChoice *c1, *c2;
+	struct PrinterConstraint *t;
+
+	o1 = option_find(po, key1);
+	o2 = option_find(po, key2);
+	if (o1 == NULL || o2 == NULL || o1 == o2)
+		return;
+	c1 = option_find_choice(o1, choice1);
+	c2 = option_find_choice(o2, choice2);
+	if (c1 == NULL || c2 == NULL)
+		return;
+	t = g_malloc0(sizeof(struct PrinterConstraint));
+	t->next = c1->constraints;
+	t->option = o2;
+	t->choice = c2;
+	c1->constraints = t;
+	t = g_malloc0(sizeof(struct PrinterConstraint));
+	t->next = c2->constraints;
+	t->option = o1;
+	t->choice = c1;
+	c2->constraints = t;
+	if (o1->selected_index == c1 - o1->choices)
+		c2->forbidden++;
+	if (o2->selected_index == c2 - o2->choices)
+		c1->forbidden++;
+}
+
+PrinterOptions* printer_options_new(const char *filename) {
+	PpdFile *ppd;
+	PrinterOptions *r;
+	GSList *gl, *cl;
+	struct PrinterOption **next_option, **next_installable;
+
+  	ppd = ppd_file_new(filename);
+	if (ppd == NULL)
+		return NULL;
+	r = g_object_new(TYPE_PRINTER_OPTIONS, NULL);
+	r->ppd = ppd;
+	r->options = NULL;
+	r->installable = NULL;
+	ppd_mark_defaults(r->ppd);
+	g_object_ref(G_OBJECT(r));
+
+	next_option = &r->options;
+	next_installable = &r->installable;
+
+	for (gl = r->ppd->groups ; gl != NULL ; gl = g_slist_next(gl)) {
+		PpdGroup *group = gl->data;
+		const char *name = group->text->str;
+
+		if (name != NULL) {
+			if (strcasecmp(name, "Options Installed") == 0)
+				group_add(&next_installable, group);
+			else if (strcasecmp(name, "InstallableOptions") == 0)
+				group_add(&next_installable, group);
+			else if (strcasecmp(name, "Installable Options") == 0)
+				group_add(&next_installable, group);
+			else if (strcasecmp(name, "Installed Options") == 0)
+				group_add(&next_installable, group);
+			else
+				group_add(&next_option, group);
+		} else
+				group_add(&next_installable, group);
+	}
+	for (cl = r->ppd->consts ; cl != NULL ; cl = g_slist_next(cl)) {
+		PpdConstraint *c = cl->data;
+
+		if (c->option1 == NULL || c->choice1 == NULL ||
+				c->option2 == NULL || c->choice2 == NULL)
+			continue;
+		addconstraint(r, c->option1->str, c->choice1->str,
+				c->option2->str, c->choice2->str);
+	}
+	return r;
+}
+
+PpdFile *printer_options_get_handle(PrinterOptions *po) {
+	return po->ppd;
+}
+
+void printer_options_select(PrinterOptions *po, const char *key, const char *value) {
+	PpdOption *o;
+	PpdChoice *c;
+	struct PrinterOption *p;
+
+	o = ppd_find_option_by_keyword(po->ppd, key);
+	if (o == NULL)
+		return;
+	p = po->options;
+	while (p != NULL && p->option != o)
+		p = p->next;
+	if (p == NULL) {
+		p = po->installable;
+		while (p != NULL && p->option != o)
+			p = p->next;
+		if (p == NULL)
+			return;
+	}
+	c = ppd_find_choice(o, value);
+	if (c == NULL)
+		return;
+	p->selected_index = g_slist_index(o->choices, c);
+}
+
+char *printer_options_concat(PrinterOptions *po, char *s, const char *prefix, const char *postfix) {
+	struct PrinterOption *o;
+
+	for (o = po->options ; o != NULL ; o = o->next) {
+		if (o->selected_index < 0)
+			continue;
+
+		s = g_strconcat(s, prefix,
+				o->option->keyword->str,
+				":",
+				o->choices[o->selected_index].choice->choice->str,
+				postfix,
+				(char*)NULL);
+	}
+	return s;
+}
+
+char *printer_option_select(struct PrinterOption *o, int s) {
+	struct PrinterConstraint *t;
+	struct PrinterOptionChoice *c;
+	char *message = NULL;
+
+	if (o->selected_index >= 0) {
+		c = &o->choices[o->selected_index];
+		for (t = c->constraints ; t != NULL ; t = t->next ) {
+			t->choice->forbidden--;
+		}
+	}
+
+	if (s < 0 || s >= o->count)
+		o->selected_index = -1;
+	else {
+		o->selected_index = s;
+		c = &o->choices[o->selected_index];
+		for (t = c->constraints ; t != NULL ; t = t->next ) {
+			struct PrinterOption *co = t->option;
+			struct PrinterOptionChoice *cc = t->choice;
+			cc->forbidden++;
+			if (co->selected_index == cc - co->choices) {
+				char *n;
+				n = g_strconcat(message?message:"",
+						"Option \"",
+						o->option->text->str,
+						"\":\"",
+						c->choice->text->str,
+						"\"\n conflicts with option\n\"",
+						co->option->text->str,
+						"\":\"",
+						cc->choice->text->str,
+						"\"\n", NULL);
+				g_free(message);
+				message = n;
+			}
+		}
+	}
+	return message;
+}
diff --git a/src/printeroptions.h b/src/printeroptions.h
new file mode 100644
index 0000000..97e698d
--- /dev/null
+++ b/src/printeroptions.h
@@ -0,0 +1,42 @@
+#ifndef PRINTER_OPTIONS_H
+#define PRINTER_OPTIONS_H
+#include <glib.h>
+#include <glib-object.h>
+#include <ppd.h>
+
+G_BEGIN_DECLS
+
+struct PrinterOption {
+	struct PrinterOption *next;
+	PpdOption *option;
+	int selected_index;
+	int count;
+	struct PrinterOptionChoice {
+		const PpdChoice *choice;
+		struct PrinterConstraint *constraints;
+		int forbidden;
+	} choices[]; // needs C99
+};
+struct _PrinterOptions {
+	GObject parent;
+	PpdFile *ppd;
+	struct PrinterOption *options, *installable;
+};
+
+#define TYPE_PRINTER_OPTIONS (printer_options_get_type())
+#define PRINTER_OPTIONS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_PRINTER_OPTIONS, PrinterOptions))
+#define PRINTER_OPTIONS_CLASS(klass) (G_TYPE_CHECK_CLASS(CAST((klass),TYPE_PRINTER_OPTIONS, PrinterOptionsClass))
+
+typedef struct _PrinterOptions PrinterOptions;
+typedef struct _PrinterOptionsClass PrinterOptionsClass;
+
+GType printer_options_get_type (void) G_GNUC_CONST;
+PrinterOptions *printer_options_new(const char*);
+PpdFile *printer_options_get_handle(PrinterOptions *);
+void printer_options_select(PrinterOptions *, const char *, const char *);
+char *printer_options_concat(PrinterOptions *, char *, const char *, const char *);
+char *printer_option_select(struct PrinterOption *, int);
+
+G_END_DECLS
+
+#endif
-- 
1.7.2.3

