Logo Search packages:      
Sourcecode: ledcontrol version File versions  Download package

callbacks.c

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <gtk/gtk.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>

#include "callbacks.h"
#include "interface.h"
#include "support.h"

#define COMMAND "ledcontrol"
#define BLANKCOMMAND COMMAND " set n9c9s9 off"
#define NUMOFFCMD COMMAND " set n9 off"
#define CAPSOFFCMD COMMAND " set c9 off"
#define SCROLLOFFCMD COMMAND " set s9 off"
#define NICECOMMAND COMMAND " set n9c9s9 normal"
#define EXITMSG "Note: If in X, some LEDs may have the wrong state.\n" \
                "Press some lock key twice to correct them.\n"

#define OLDLISTLENGTH 10
#define MAXPIECELENGTH 64

#define BLINK_PAGE 0
#define FREQUENCY_PAGE 1
#define DUTYCYCLE_PAGE 2
#define ANIMATION_PAGE 3


#define NUM_ON        (1<<0)
#define CAPS_ON       (1<<1)
#define SCROLL_ON     (1<<2)
#define NUM_OFF       (1<<3)
#define CAPS_OFF      (1<<4)
#define SCROLL_OFF    (1<<5)
#define NUM_NORMAL    (1<<6)
#define CAPS_NORMAL   (1<<7)
#define SCROLL_NORMAL (1<<8)

/* To remove all settings */
#define NUM_AND (~(NUM_ON|NUM_OFF|NUM_NORMAL))
#define CAPS_AND (~(CAPS_ON|CAPS_OFF|CAPS_NORMAL))
#define SCROLL_AND (~(SCROLL_ON|SCROLL_OFF|SCROLL_NORMAL))

#define ANIM_OPT_NOTHING 0
#define ANIM_OPT_ON 1
#define ANIM_OPT_OFF 2
#define ANIM_OPT_NORMAL 3


/* A few global variables... */

static gboolean previous_command_modified=FALSE;
static guint internal=FALSE;


/* Helper functions: */
static gchar *strappend(gchar *a,gchar *b);
static gchar *parse_jumpspace(gchar *str);
static gchar *parse_getpiece(gchar *str,gchar *buf);
static gchar *parse_getnumber(gchar *str,gint *n);
static gchar *parse_getfloat(gchar *str,gfloat *n);
static gint option_menu_get_active(GtkWidget *opt,gchar *name);
static void option_menu_set_active(GtkWidget *opt,gchar *name,gint n);


/* Main functions: */

static void set_command(GtkWidget *top);

static gchar *make_command_set(GtkWidget *top);
static gchar *make_command_blink(GtkWidget *top);
static gchar *make_command_frequency(GtkWidget *top);
static gchar *make_command_dutycycle(GtkWidget *top);
static gchar *make_command_animation(GtkWidget *top);


static gboolean parse_command(gchar *string, GtkWidget *top, gboolean set);
static gboolean parse_command_main(gchar *string, GtkWidget *top,gboolean set);
static gboolean parse_command_set(gchar *string, GtkWidget *top,gboolean set);
static gboolean parse_command_blink(gchar *string, GtkWidget *top,
                            gboolean set);
static gboolean parse_command_frequency(gchar *string, GtkWidget *top,
                              gboolean set);
static gboolean parse_command_dutycycle(gchar *string, GtkWidget *top,
                              gboolean set);
static gboolean parse_command_anim(gchar *string, GtkWidget *top,gboolean set);
static gchar *parse_command_float(gchar *string,GtkWidget *top,gboolean set,
                          gfloat *f,gchar *widget);
static gchar *parse_command_number(gchar *string,GtkWidget *top,gboolean set,
                           gint *n,gchar *widget);

/* A few internal ones too... */
static void list_add(GtkWidget *list,gchar **str,gpointer data);

static gchar *anim_make_change_command(gint change);

static void do_exit(void);
static int my_system(char *command);


/********* HELPER FUNCTION CODE: **********/

/*
 * Appends new to str and returns pointer to new string.
 * a is freed, but b is not!!!
 */
static gchar *strappend(gchar *a,gchar *b) {
      gchar *str;

      str=g_strconcat(a,b,NULL);
      g_free(a);
      return str;
}


/*
 * Returns pointer to next character in str that is not a space.
 */
static gchar *parse_jumpspace(gchar *str) {
        gint i;
        
        for (i=0; str[i] && isspace(str[i]); i++)
                ;
        return str+i;
}


/*
 * Gets a space-separated piece from str and stores it into buf.
 * Returns pointer to str directly after the piece.
 * If piece is longer that MAXPIECELENGTH-1, returns NULL.
 * If no piece can be found, returns NULL.
 */
static gchar *parse_getpiece(gchar *str,gchar *buf) {
        gint i;
        
        str=parse_jumpspace(str);
        if (str[0]==0)
                return NULL;

        for (i=0; i<(MAXPIECELENGTH-1) && str[i] && !isspace(str[i]); i++)
                buf[i]=str[i];
        if (i>=(MAXPIECELENGTH-1))
                return NULL;
        buf[i]=0;
        return str+i;
}


/*
 * Gets a space-separated non-negative number from str and stores it in *n.
 * Returns pointer to str directly after the piece.
 * If string is not a number returns NULL.
 */
static gchar *parse_getnumber(gchar *str,gint *n) {

        *n=0;
        str=parse_jumpspace(str);
        if (str[0]=='+')
                str++;
        if (!str[0])
                return NULL;
        
        for (; str[0] && !isspace(str[0]); str++) {
                if (!isdigit(str[0]))
                        return NULL;
                *n=*n*10+str[0]-'0';
        }
        return str;
}


/*
 * Gets a space-separated floating point number from str and stores it
 * in *n. If string is not a number returns NULL.
 */
static gchar *parse_getfloat(gchar *str,gfloat *n) {
        gfloat d;
        gint sign=1;

        *n=0;

        /* Check for negative number */
        str=parse_jumpspace(str);
        if (str[0]==0)
                return NULL;
        if (str[0]=='-') {
                sign=-1;
                str++;
        }

        for (; str[0] && str[0]!='.' && str[0]!=',' &&
                     !isspace(str[0]); str++) {
                if (!isdigit(str[0]))
                        return NULL;
                *n=*n*10+str[0]-'0';
        }
        if (isspace(str[0]) || str[0]==0) {
                if (sign<0)
                        *n=-*n;
                return str;
        }
        /* Contains '.' or ',' */
        str++;
        d=10;
        for (; str[0] && !isspace(str[0]); str++) {
                if (!isdigit(str[0]))
                        return NULL;
                *n=*n+((gfloat)(str[0]-'0')/d);
                d*=10;
        }
        if (sign<0)
                *n=-*n;
        return str;
}


/*
 * Returns number of selected item in option menu. If name==NULL uses
 * opt directly, otherwise looks up widget name.
 * This is a rather tedious process... Why hasn't this been automated?!?
 */
static gint option_menu_get_active(GtkWidget *opt,gchar *name) {
      GtkWidget *selected;
      GList *list;
      gint i;

      if (name)
            opt=lookup_widget(opt,name);
      opt=gtk_option_menu_get_menu(GTK_OPTION_MENU(opt));
      selected=gtk_menu_get_active(GTK_MENU(opt));
      for (i=0, list=GTK_MENU_SHELL(opt)->children; list;
           i++, list=g_list_next(list))
            if (list->data == selected)
                  return i;
      return -1;
}

/*
 * Sets the n:th option in option menu opt. If name==NULL uses opt directly,
 * otherwise looks up widget name.
 */
static void option_menu_set_active(GtkWidget *opt,gchar *name,gint n) {

      if (name)
            opt=lookup_widget(opt,name);
      gtk_option_menu_set_history(GTK_OPTION_MENU(opt),n);

      return;
}


/*********** MAIN STUFF ***********/


/* Command making: */

/*
 * Sets the command entry according to data in widgets. If command
 * has been user-edited, save it to the list.
 */
static void set_command(GtkWidget *top) {
      GtkWidget *notebook;
      GtkWidget *entry;
      GtkWidget *combo;
      GtkWidget *autob;
      gint page;
      static GList *oldlist=NULL;
      GList *givelist,*list;
      gchar *str,*new;

      notebook=lookup_widget(top,"main_notebook");
      page=gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));

      entry=lookup_widget(top,"command_entry");
      combo=lookup_widget(top,"command_combo");


      switch (page) {
      case BLINK_PAGE:
            str=make_command_blink(top);
            break;
      case FREQUENCY_PAGE:
            str=make_command_frequency(top);
            break;
      case DUTYCYCLE_PAGE:
            str=make_command_dutycycle(top);
            break;
      case ANIMATION_PAGE:
            str=make_command_animation(top);
            break;
      default:
            g_warning("Internal error: set_command gets page %d\n",page);
            return;
      }

      /* Don't want on_command_entry_changed to change flag... */
      internal++;

      if (previous_command_modified) {
            new=gtk_entry_get_text(GTK_ENTRY(entry));

            /* Check whether it already is in the list */
            for (list=oldlist; list; list=g_list_next(list)) {
                  if (list->data==NULL)
                        continue;
                  if (g_strcasecmp(list->data,new)==0) {
                        oldlist=g_list_remove_link(oldlist,list);
                        break;
                  }
            }

            oldlist=g_list_prepend(oldlist,g_strdup(new));

            /* If too long... */
            if (g_list_length(oldlist)>OLDLISTLENGTH) {
                  if (g_list_last(oldlist)->data!=NULL)
                        g_free(g_list_last(oldlist)->data);
                  g_list_last(oldlist)->data=NULL;
                  oldlist=g_list_remove_link(oldlist,g_list_last(oldlist));
            }

            /* Duplicate the list */
            givelist=NULL;
            for (list=oldlist; list; list=g_list_next(list))
                  givelist=g_list_append(givelist,g_strdup(list->data));

            /* Set it. */
            gtk_combo_set_popdown_strings(GTK_COMBO(combo),givelist);
            previous_command_modified=FALSE;
      }

      gtk_entry_set_text(GTK_ENTRY(entry),str);
      /* Test it (don't set widgets). */
      if (parse_command(str,top,FALSE) && internal<=1) {
            /* It's an OK string. */
            autob=lookup_widget(top,"auto_test_button");
            if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(autob))) {
                  /* Set it. */
                  on_test_button_clicked(top,(gpointer)str);
            }
      }

      g_free(str);

      /* Release it. */
      internal--;

      return;
}



/*
 * Returns a newly-allocated string with a "set xxx " command of the
 * stuff in led_use_xxx1.
 */
static gchar *make_command_set(GtkWidget *top) {
      GtkWidget *n,*c,*s;
      gchar *str;

      n=lookup_widget(top,"led_use_num1");
      c=lookup_widget(top,"led_use_caps1");
      s=lookup_widget(top,"led_use_scroll1");

      str=g_strdup("set ");
      if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(n)))
            str=strappend(str,"n9");
      if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(c)))
            str=strappend(str,"c9");
      if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(s)))
            str=strappend(str,"s9");
      str=strappend(str," ");
      
      return str;
}



/*
 * The functions that make a command string of the stuff in the widgets.
 * They return a newly-allocated string.
 */
static gchar *make_command_blink(GtkWidget *top) {
      gchar *str;
      gint i;
      GtkWidget *list;
      gchar buf[10];
      
      str=make_command_set(top);
      str=strappend(str,"blink");

      list=lookup_widget(top,"blink_list");
      for (i=0; i<GTK_CLIST(list)->rows; i++) {
            snprintf(buf,10," %d",GPOINTER_TO_INT(gtk_clist_get_row_data(GTK_CLIST(list),i)));
            str=strappend(str,buf);
      }
      return str;
}
static gchar *make_command_frequency(GtkWidget *top) {
      gchar *str;
      GtkWidget *entry;
      GtkAdjustment *adj;
      gchar *n;

      str=make_command_set(top);

      str=strappend(str,"frequency ");

      entry=lookup_widget(top,"frequency_minimum_value");
      str=strappend(str,gtk_entry_get_text(GTK_ENTRY(entry)));
      str=strappend(str," ");

      entry=lookup_widget(top,"frequency_minimum_freq");
      str=strappend(str,gtk_entry_get_text(GTK_ENTRY(entry)));
      str=strappend(str," ");

      entry=lookup_widget(top,"frequency_maximum_value");
      str=strappend(str,gtk_entry_get_text(GTK_ENTRY(entry)));
      str=strappend(str," ");

      entry=lookup_widget(top,"frequency_maximum_freq");
      str=strappend(str,gtk_entry_get_text(GTK_ENTRY(entry)));
      str=strappend(str," ");

      entry=lookup_widget(top,"frequency_test_value");
      adj=gtk_range_get_adjustment(GTK_RANGE(entry));
      n=g_strdup_printf("%.2f",adj->value);
      str=strappend(str,n);
      g_free(n);

      return str;
}
static gchar *make_command_dutycycle(GtkWidget *top) {
      gchar *str;
      GtkWidget *entry;
      GtkAdjustment *adj;
      gchar *n;

      str=make_command_set(top);
      str=strappend(str,"dutycycle ");

      entry=lookup_widget(top,"dutycycle_total_time");
      str=strappend(str,gtk_entry_get_text(GTK_ENTRY(entry)));
      str=strappend(str," ");

      entry=lookup_widget(top,"dutycycle_minimum_value");
      str=strappend(str,gtk_entry_get_text(GTK_ENTRY(entry)));
      str=strappend(str," ");

      entry=lookup_widget(top,"dutycycle_maximum_value");
      str=strappend(str,gtk_entry_get_text(GTK_ENTRY(entry)));
      str=strappend(str," ");

      entry=lookup_widget(top,"dutycycle_test_value");
      adj=gtk_range_get_adjustment(GTK_RANGE(entry));
      n=g_strdup_printf("%.2f",adj->value);
      str=strappend(str,n);
      g_free(n);

      return str;
}
static gchar *make_command_animation(GtkWidget *top) {
      GtkWidget *list;
      gchar *str;
      gint i;
      gchar *text;
      
      str=g_strdup("anim");
      list=lookup_widget(top,"animation_list");
      /* The 'Option' column already contains all the required strings... ;)
       */
      for (i=0; i<GTK_CLIST(list)->rows; i++) {
            gtk_clist_get_text(GTK_CLIST(list),i,1,&text);
            str=strappend(str," ");
            str=strappend(str,text);
      }

      return str;
}














/* Parsing: */

/*
 * Parses the command given in string. If set is true, then it sets all
 * the stuff in the widgets according to the string. It temporarily sets
 * internal. Also hides/shows "Bad command" string whether set is true
 * or false.
 */
static gboolean parse_command(gchar *string, GtkWidget *top, gboolean set) {
      GtkWidget *text;

      /* Temporarily set internal */
      internal++;

      text=lookup_widget(top,"badcommand_label");
      if (parse_command_main(string,top,set)) {
            /* It's OK. */
            gtk_widget_hide(text);
            internal--;
            return TRUE;
      } else {
            /* It's invalid. */
            gtk_widget_show(text);
            internal--;
            return FALSE;
      }
}

/*
 * Directly called by parse_command. Does the same thing except it doesn't
 * set the "Bad command" text, instead returns TRUE/FALSE if command is
 * valid/invalid.
 */
static gboolean parse_command_main(gchar *string, GtkWidget *top,
                           gboolean set) {
      gchar buf[MAXPIECELENGTH];
      gchar *str;
      gboolean foundset=FALSE;

      str=parse_getpiece(string,buf);
      if (str==NULL)
            return FALSE;

      if (g_strcasecmp(buf,"set")==0) {
            if (!parse_command_set(str,top,set))
                  return FALSE;
            str=parse_getpiece(str,buf);   /* "n9c9s9" etc. */
            if (str==NULL)  /* Should not be. */
                  return FALSE;
            str=parse_getpiece(str,buf);   /* Command */
            if (str==NULL)
                  return FALSE;
            foundset=TRUE;
      }

      /* Things to accept but do nothing. */
      if (g_strcasecmp(buf,"nop")==0 && !foundset)
            return TRUE;
      if (g_strcasecmp(buf,"off")==0 ||
          g_strcasecmp(buf,"on")==0 ||
          g_strcasecmp(buf,"normal")==0) {
            str=parse_jumpspace(str);
            if (str[0]==0)
                  return TRUE;
            else
                  return FALSE;
      }

      if (g_strcasecmp(buf,"blink")==0) {
            /* BLINK */
            return parse_command_blink(str,top,set);
      } else if (g_strcasecmp(buf,"frequency")==0) {
            /* FREQUENCY */
            return parse_command_frequency(str,top,set);
      } else if (g_strcasecmp(buf,"dutycycle")==0 ||
               g_strcasecmp(buf,"duty_cycle")==0  ||
               g_strcasecmp(buf,"duty-cycle")==0) {
            /* DUTYCYCLE, DUTY_CYCLE, DUTY-CYCLE */
            return parse_command_dutycycle(str,top,set);
      } else if (g_strcasecmp(buf,"duty")==0) {
            /* "DUTY CYCLE" */
            str=parse_getpiece(str,buf);
            if (str==NULL || g_strcasecmp(buf,"cycle")!=0)
                  return FALSE;
            return parse_command_dutycycle(str,top,set);
      } else if (g_strcasecmp(buf,"anim")==0 && !foundset) {
            /* ANIM */
            return parse_command_anim(str,top,set);
      } else {
            return FALSE;
      }
}

/*
 * Parses the led info piece of a set command. Does NOT call anything
 * further.
 */
static gboolean parse_command_set(gchar *string, GtkWidget *top,
                          gboolean set) {
      gchar buf[MAXPIECELENGTH];
      gchar *str;
      gint i;
      GtkWidget *w;

      str=parse_getpiece(string,buf);
      if (str==NULL)
            return FALSE;

      if (set) {
            w=lookup_widget(top,"led_use_num1");
            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w),FALSE);
            w=lookup_widget(top,"led_use_caps1");
            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w),FALSE);
            w=lookup_widget(top,"led_use_scroll1");
            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w),FALSE);
      }

      for (i=0; buf[i]; i++) {
            if (set) {
                  switch (buf[i]) {
                  case 'n':
                  case 'N':
                        w=lookup_widget(top,"led_use_num1");
                        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w),TRUE);
                        break;
                  case 'c':
                  case 'C':
                        w=lookup_widget(top,"led_use_caps1");
                        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w),TRUE);
                        break;
                  case 's':
                  case 'S':
                        w=lookup_widget(top,"led_use_scroll1");
                        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w),TRUE);
                        break;
                  default:
                        return FALSE;
                  }
            } else {
                  if (!(buf[i]=='s' || buf[i]=='S' ||
                        buf[i]=='c' || buf[i]=='C' ||
                        buf[i]=='n' || buf[i]=='N'))
                        return FALSE;
            }
            if (isdigit(buf[i+1]))
                  i++;
      }
      return TRUE;
}



/*
 * Parse the individual types of commands.
 */
static gboolean parse_command_blink(gchar *string, GtkWidget *top,
                            gboolean set) {
      GtkWidget *notebook;
      GtkWidget *list;
      gchar *str;
      gint delay;
      gchar *cols[3];
      gchar buf[10];
      gint row;

      /* This is outside, otherwise
       * `list' might be used uninitialized in this function */
      list=lookup_widget(top,"blink_list");
      if (set) {
            notebook=lookup_widget(top,"main_notebook");
            gtk_notebook_set_page(GTK_NOTEBOOK(notebook),BLINK_PAGE);
            gtk_clist_freeze(GTK_CLIST(list));
            gtk_clist_clear(GTK_CLIST(list));
      }

      str=parse_jumpspace(string);
      if (str[0]==0) {
            if (set)
                  gtk_clist_thaw(GTK_CLIST(list));
            return FALSE;
      }
      while (str[0]) {
            str=parse_getnumber(str,&delay);
            if (str==NULL) {
                  if (set) {
                        blink_list_correct_on_off(top,NULL);
                        gtk_clist_thaw(GTK_CLIST(list));
                  }
                  return FALSE;
            }
            if (set) {
                  cols[0]="ON";   /* Will be corrected later. */
                  snprintf(buf,10,"%d",delay);
                  cols[1]=buf;
                  cols[2]=NULL;
                  row=gtk_clist_append(GTK_CLIST(list),cols);
                  gtk_clist_set_row_data(GTK_CLIST(list),row,
                                     GINT_TO_POINTER(delay));
            }
            str=parse_jumpspace(str);
      }
      if (set) {
            blink_list_correct_on_off(top,NULL);
            gtk_clist_thaw(GTK_CLIST(list));
      }
      return TRUE;
}
static gboolean parse_command_frequency(gchar *string, GtkWidget *top,
                              gboolean set) {
      gchar *str=string;
      gfloat f;
      gint n;
      gint maxf,minf;
      GtkWidget *notebook;

      /* Set notebook page */
      if (set) {
            notebook=lookup_widget(top,"main_notebook");
            gtk_notebook_set_page(GTK_NOTEBOOK(notebook),FREQUENCY_PAGE);
      }

      /* Parse pieces and check them */
      /* We don't change the scale, we just call frequency_test_reset() */
      if ((str=parse_command_float(str,top,set,&f,
                             "frequency_minimum_value"))==NULL ||
          (str=parse_command_number(str,top,set,&minf,
                              "frequency_minimum_freq"))==NULL ||
          (str=parse_command_float(str,top,set,&f,
                             "frequency_maximum_value"))==NULL ||
          (str=parse_command_number(str,top,set,&maxf,
                              "frequency_maximum_freq"))==NULL) {

            /* This is ugly: */
            n=internal;   internal=FALSE;
            frequency_test_reset(top,NULL);
            internal=n;
            return FALSE;
      }

      /* Check if value is left out and allow it. */
      /* From now on, always check that maxf<minf before returning TRUE. */
      str=parse_jumpspace(str);
      if (str[0]==0) {
            if (set) {
                  n=internal;   internal=FALSE;
                  frequency_test_reset(top,NULL);
                  internal=n;
            }

            if (maxf<minf)
                  return TRUE;
            else
                  return FALSE;
      }

      /* Check value. */
      str=parse_getfloat(str,&f);
      if (str==NULL) {
            if (set) {
                  n=internal;   internal=FALSE;
                  frequency_test_reset(top,NULL);
                  internal=n;
            }

            return FALSE;
      }

      if (set) {
            n=internal;   internal=FALSE;
            frequency_test_reset(top,&f);
            internal=n;
      }

      str=parse_jumpspace(str);
      if (str[0]==0 && maxf<minf)
            return TRUE;
      else
            return FALSE;
}
static gboolean parse_command_dutycycle(gchar *string, GtkWidget *top,
                              gboolean set) {
      gchar *str=string;
      gfloat f;
      gint n;
      GtkWidget *notebook;

      /* Set notebook page */
      if (set) {
            notebook=lookup_widget(top,"main_notebook");
            gtk_notebook_set_page(GTK_NOTEBOOK(notebook),DUTYCYCLE_PAGE);
      }

      /* Parse pieces and check them */
      if ((str=parse_command_number(str,top,set,&n,
                             "dutycycle_total_time"))==NULL ||
          (str=parse_command_float(str,top,set,&f,
                             "dutycycle_minimum_value"))==NULL ||
          (str=parse_command_float(str,top,set,&f,
                             "dutycycle_maximum_value"))==NULL) {
            n=internal;   internal=FALSE;
            dutycycle_test_reset(top,NULL);
            internal=n;
            return FALSE;
      }


      /* Check if value is left out and allow it. */
      str=parse_jumpspace(str);
      if (str[0]==0) {
            if (set) {
                  n=internal;   internal=FALSE;
                  dutycycle_test_reset(top,NULL);
                  internal=n;
            }
            return TRUE;
      }

      /* Check value. */
      str=parse_getfloat(str,&f);
      if (str==NULL) {
            if (set) {
                  n=internal;   internal=FALSE;
                  dutycycle_test_reset(top,NULL);
                  internal=n;
            }
            return FALSE;
      }

      if (set) {
            n=internal;   internal=FALSE;
            dutycycle_test_reset(top,&f);
            internal=n;
      }

      str=parse_jumpspace(str);
      if (str[0]==0)
            return TRUE;
      else
            return FALSE;
}
static gboolean parse_command_anim(gchar *string, GtkWidget *top,
                           gboolean set) {
      GtkWidget *notebook;
      GtkWidget *list;
      gchar *str;
      gint change;
      gchar *cols[4];
      gint row;
      gchar buf[MAXPIECELENGTH];
      gint i;

      /* This is outside, otherwise
       * `list' might be used uninitialized in this function */
      list=lookup_widget(top,"animation_list");
      if (set) {
            notebook=lookup_widget(top,"main_notebook");
            gtk_notebook_set_page(GTK_NOTEBOOK(notebook),ANIMATION_PAGE);
            gtk_clist_freeze(GTK_CLIST(list));
            gtk_clist_clear(GTK_CLIST(list));
      }

      str=parse_jumpspace(string);
      while (str[0]) {
            str=parse_getpiece(str,buf);
            if (isdigit(buf[0])) {
                  /* It's a delay. */
                  if (parse_getnumber(buf,&change)==NULL) {
                        if (set) {
                              anim_correct_on_off_flags(top,NULL);
                              gtk_clist_thaw(GTK_CLIST(list));
                        }
                        return FALSE;
                  }
                  if (set) {
                        cols[0]="delay";
                        snprintf(buf,10,"%d",change);
                        cols[1]=buf;
                        /* Will be corrected later: */
                        cols[2]="";
                        cols[3]=NULL;
                        row=gtk_clist_append(GTK_CLIST(list),cols);
                        gtk_clist_set_row_data(GTK_CLIST(list),row,
                                           GINT_TO_POINTER(-change));
                  }
            } else {
                  change=0;
                  for (i=0; buf[i]; i++) {
                                switch (buf[i]) {
                        case 'x':
                        case 'X':
                              i++;
                              switch (buf[i]) {
                              case 'n':
                              case 'N':
                                    change=(change&NUM_AND)|
                                          NUM_NORMAL;
                                    break;
                              case 'c':
                              case 'C':
                                    change=(change&CAPS_AND)|
                                          CAPS_NORMAL;
                                    break;
                              case 's':
                              case 'S':
                                    change=(change&SCROLL_AND)|
                                          SCROLL_NORMAL;
                                    break;
                              default:
                              if (set) {
                              anim_correct_on_off_flags(top,NULL);
                              gtk_clist_thaw(GTK_CLIST(list));
                              }
                              return FALSE;
                              }
                              break;
                                case 'n':
                              change=(change&NUM_AND)|NUM_OFF;
                              break;
                                case 'N':
                              change=(change&NUM_AND)|NUM_ON;
                              break;
                                case 'c':
                              change=(change&CAPS_AND)|CAPS_OFF;
                              break;
                                case 'C':
                              change=(change&CAPS_AND)|CAPS_ON;
                              break;
                                case 's':
                              change=(change&SCROLL_AND)|SCROLL_OFF;
                              break;
                                case 'S':
                              change=(change&SCROLL_AND)|SCROLL_ON;
                              break;
                                default:
                              if (set) {
                              anim_correct_on_off_flags(top,NULL);
                              gtk_clist_thaw(GTK_CLIST(list));
                              }
                                        return FALSE;
                                }
                  }
                  /* Whew... We've parsed it.
                   * It also shouldn't be able to have nothing in it. */

                  if (set) {
                        cols[0]="set";
                        cols[1]=anim_make_change_command(change);
                        cols[2]="";
                        cols[3]=NULL;
                        row=gtk_clist_append(GTK_CLIST(list),cols);
                        gtk_clist_set_row_data(GTK_CLIST(list),row,
                                           GINT_TO_POINTER(change));
                  }
            }
            str=parse_jumpspace(str);
      }
      if (set) {
            anim_correct_on_off_flags(top,NULL);
            gtk_clist_thaw(GTK_CLIST(list));
      }
      return TRUE;
}




/*
 * Parses a float/number from string, stores it in *f / *n, and if set is true,
 * then sets widget "widget" to the string. Returns pointer after float/number
 * or NULL on error.
 */
static gchar *parse_command_float(gchar *string,GtkWidget *top,gboolean set,
                          gfloat *f, gchar *widget) {
      gchar buf[MAXPIECELENGTH];
      gchar *str;
      GtkWidget *entry;
      
      str=parse_getpiece(string,buf);
      if (str==NULL)
            return NULL;
      if (parse_getfloat(buf,f)==NULL)
            return NULL;
      if (set) {
            entry=lookup_widget(top,widget);
            gtk_entry_set_text(GTK_ENTRY(entry),buf);
      }
      return str;
}
static gchar *parse_command_number(gchar *string,GtkWidget *top,gboolean set,
                           gint *n,gchar *widget) {
      gchar buf[MAXPIECELENGTH];
      gchar *str;
      GtkWidget *entry;
      
      str=parse_getpiece(string,buf);
      if (str==NULL)
            return NULL;
      if (parse_getnumber(buf,n)==NULL)
            return NULL;
      if (set) {
            entry=lookup_widget(top,widget);
            gtk_entry_set_text(GTK_ENTRY(entry),buf);
      }
      return str;
}





/*
 * Called both as an event handler and convenience function. Resets
 * the "Test value" slider according to data in widgets. If value==NULL,
 * then value is set to minimum value, otherwise the value pointed to
 * with (gfloat *)value.
 * If some entry is bad, it is assumed to be the default (0.00/1.00)
 * Not done if internal set.
 */
void frequency_test_reset(GtkWidget *top, gpointer value) {
      GtkWidget *entry;
      GtkAdjustment *adj;
      gfloat min,max;
      gfloat jump;
      gchar *str;

      if (internal)
            return;

      entry=lookup_widget(top,"frequency_minimum_value");
      str=gtk_entry_get_text(GTK_ENTRY(entry));
      if (parse_getfloat(str,&min)==NULL)
            min=0.00;

      entry=lookup_widget(top,"frequency_maximum_value");
      str=gtk_entry_get_text(GTK_ENTRY(entry));
      if (parse_getfloat(str,&max)==NULL)
            max=1.00;

      if (max<min) {
            jump=max;
            max=min;
            min=jump;
      }

      for (jump=1000000; jump>((max-min)*0.9) && jump>0.1; jump/=10)
            ;

      entry=lookup_widget(top,"frequency_test_value");
      adj=gtk_range_get_adjustment(GTK_RANGE(entry));
      if (value==NULL)
            adj->value=min;
      else
            adj->value=*((gfloat *)value);
      adj->lower=min-jump;
      adj->upper=max+jump;
      adj->step_increment=jump/10;
      adj->page_increment=jump;
      adj->page_size=0;

      gtk_signal_emit_by_name(GTK_OBJECT(adj),"changed");
      return;
}
void dutycycle_test_reset(GtkWidget *top, gpointer value) {
      GtkWidget *entry;
      GtkAdjustment *adj;
      gfloat min,max;
      gfloat jump;
      gchar *str;

      if (internal)
            return;

      entry=lookup_widget(top,"dutycycle_minimum_value");
      str=gtk_entry_get_text(GTK_ENTRY(entry));
      if (parse_getfloat(str,&min)==NULL)
            min=0.00;

      entry=lookup_widget(top,"dutycycle_maximum_value");
      str=gtk_entry_get_text(GTK_ENTRY(entry));
      if (parse_getfloat(str,&max)==NULL)
            max=1.00;

      if (max<min) {
            jump=max;
            max=min;
            min=jump;
      }

      for (jump=1000000; jump>((max-min)*0.9) && jump>0.1; jump/=10)
            ;

      entry=lookup_widget(top,"dutycycle_test_value");
      adj=gtk_range_get_adjustment(GTK_RANGE(entry));
      if (value==NULL)
            adj->value=min;
      else
            adj->value=*((gfloat *)value);
      adj->lower=min-jump;
      adj->upper=max+jump;
      adj->step_increment=jump/10;
      adj->page_increment=jump;
      adj->page_size=0;

      gtk_signal_emit_by_name(GTK_OBJECT(adj),"changed");
      return;
}










/********************** COMMON LIST HANDLING **********************/

/*
 * Moves current selection one up/down. If widget==NULL, it is assumed
 * that list is the correct list, if not, then the corresponding widget
 * is looked up (using list as a base widget).
 *
 * Yes, I know I shouldn't read the GtkCList structure directly, but no,
 * there is no other way to do it (apart from making a dedicated event
 * handler and a few global variables, which I definately will NOT).
 */
void list_move_up(GtkWidget *list,gchar *widget) {
      gint row;

      if (widget)
            list=lookup_widget(list,widget);

      if (GTK_CLIST(list)->selection==NULL) {
            return;
      }

      row=GPOINTER_TO_INT(GTK_CLIST(list)->selection->data);

      if (row <= 0) {
            return;
      }
      gtk_clist_freeze(GTK_CLIST(list));
      gtk_clist_swap_rows(GTK_CLIST(list),row,row-1);
      gtk_clist_unselect_all(GTK_CLIST(list));
      gtk_clist_select_row(GTK_CLIST(list),row-1,0);
      if (gtk_clist_row_is_visible(GTK_CLIST(list),row-1)
          !=GTK_VISIBILITY_FULL)
            gtk_clist_moveto(GTK_CLIST(list),row-1,0,0.0,0.0);
      gtk_clist_thaw(GTK_CLIST(list));

      return;
}
void list_move_down(GtkWidget *list,gchar *widget) {
      gint row;

      if (widget)
            list=lookup_widget(list,widget);

      if (GTK_CLIST(list)->selection==NULL) {
            return;
      }

      row=GPOINTER_TO_INT(GTK_CLIST(list)->selection->data);

      if (row < 0 || row>=(GTK_CLIST(list)->rows-1)) {
            return;
      }
      gtk_clist_freeze(GTK_CLIST(list));
      gtk_clist_swap_rows(GTK_CLIST(list),row,row+1);
      gtk_clist_unselect_all(GTK_CLIST(list));
      gtk_clist_select_row(GTK_CLIST(list),row+1,0);
      if (gtk_clist_row_is_visible(GTK_CLIST(list),row+1)
          !=GTK_VISIBILITY_FULL)
            gtk_clist_moveto(GTK_CLIST(list),row+1,-1,1.0,0.0);
      gtk_clist_thaw(GTK_CLIST(list));

      return;
}
      
/*
 * Delete a row. Does the same as above in respect to the options.
 */
void list_remove(GtkWidget *list,gchar *widget) {
      gint row;

      if (widget)
            list=lookup_widget(list,widget);

      if (GTK_CLIST(list)->selection==NULL) {
            return;
      }

      row=GPOINTER_TO_INT(GTK_CLIST(list)->selection->data);
      gtk_clist_freeze(GTK_CLIST(list));
      gtk_clist_remove(GTK_CLIST(list),row);
      if (row >= GTK_CLIST(list)->rows)
            row--;
      gtk_clist_unselect_all(GTK_CLIST(list));
      if (GTK_CLIST(list)->rows > 0)
            gtk_clist_select_row(GTK_CLIST(list),row,0);

      if (gtk_clist_row_is_visible(GTK_CLIST(list),row)
          ==GTK_VISIBILITY_NONE)
            gtk_clist_moveto(GTK_CLIST(list),row,-1,0.5,0.0);
      gtk_clist_thaw(GTK_CLIST(list));

      return;
}


/*
 * Adds an item into list before the current selection or at the end of the
 * list if no selection active. Sets strings from str and row data to data.
 */
static void list_add(GtkWidget *list,gchar **str,gpointer data) {
      gint row=-1;

      gtk_clist_freeze(GTK_CLIST(list));
      if (GTK_CLIST(list)->selection)
            row=GPOINTER_TO_INT(GTK_CLIST(list)->selection->data);
      if (row<0)
            row=gtk_clist_append(GTK_CLIST(list),str);
      else
            row=gtk_clist_insert(GTK_CLIST(list),row,str);
      gtk_clist_set_row_data(GTK_CLIST(list),row,data);
      gtk_clist_thaw(GTK_CLIST(list));

      return;
}


/*
 * Execute command. Returns when command exists.
 * Copied from system(1) with exception of changing execve() to execv().
 */
static int my_system(char *command) {
      int pid, status;
      
      if (command == 0)
            return 1;
      pid = fork();
      if (pid == -1)
            return -1;
      if (pid == 0) {
            char *argv[4];
            argv[0] = "sh";
            argv[1] = "-c";
            argv[2] = command;
            argv[3] = 0;
            execv("/bin/sh", argv);
            exit(127);
      }
      do {
            if (waitpid(pid, &status, 0) == -1) {
                  if (errno != EINTR)
                        return -1;
            } else
                  return status;
      } while(1);
}


/****************** EVENT HANDLERS ******************/


/*
 * Notebook switch, set the command if not internal.
 */
void on_main_notebook_switch_page(GtkNotebook *notebook,
                          GtkNotebookPage *page,
                          gint page_num,
                          gpointer user_data) {
      GtkWidget *top=GTK_WIDGET(notebook);

      if (!internal)
            set_command(top);

      return;
}


/*
 * Command entry edited. Parse it if not internal.
 */
void on_command_entry_changed(GtkEditable *editable,gpointer user_data) {
      if (!internal) {
            previous_command_modified=TRUE;
            internal++;
            parse_command(gtk_entry_get_text(GTK_ENTRY(editable)),
                        GTK_WIDGET(editable),TRUE);
            internal--;
      }
      return;
}


/*
 * Led settings. When one led is changed, it changes also all others
 * (even if internal). Sets command if not internal.
 * If set off, then set x9 to OFF
 */
void on_led_use_num_toggled(GtkToggleButton *togglebutton,
                      gpointer user_data) {
      GtkWidget *top=GTK_WIDGET(togglebutton);
      static gboolean doing=FALSE;
      GtkWidget *but;
      gboolean state;

      if (!doing) {
            doing=TRUE;
            state=gtk_toggle_button_get_active(togglebutton);

            but=lookup_widget(top,"led_use_num1");
            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(but),state);
            but=lookup_widget(top,"led_use_num2");
            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(but),state);
            but=lookup_widget(top,"led_use_num3");
            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(but),state);

            if (state==FALSE)
                  my_system(NUMOFFCMD);

            doing=FALSE;
      }

      if (!internal)
            set_command(top);

      return;
}
void on_led_use_caps_toggled(GtkToggleButton *togglebutton,
                      gpointer user_data) {
      GtkWidget *top=GTK_WIDGET(togglebutton);
      static gboolean doing=FALSE;
      GtkWidget *but;
      gboolean state;

      if (!doing) {
            doing=TRUE;
            state=gtk_toggle_button_get_active(togglebutton);

            but=lookup_widget(top,"led_use_caps1");
            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(but),state);
            but=lookup_widget(top,"led_use_caps2");
            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(but),state);
            but=lookup_widget(top,"led_use_caps3");
            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(but),state);

            if (state==FALSE)
                  my_system(CAPSOFFCMD);

            doing=FALSE;
      }

      if (!internal)
            set_command(top);

      return;
}
void on_led_use_scroll_toggled(GtkToggleButton *togglebutton,
                      gpointer user_data) {
      GtkWidget *top=GTK_WIDGET(togglebutton);
      static gboolean doing=FALSE;
      GtkWidget *but;
      gboolean state;

      if (!doing) {
            doing=TRUE;
            state=gtk_toggle_button_get_active(togglebutton);

            but=lookup_widget(top,"led_use_scroll1");
            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(but),state);
            but=lookup_widget(top,"led_use_scroll2");
            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(but),state);
            but=lookup_widget(top,"led_use_scroll3");
            gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(but),state);

            if (state==FALSE)
                  my_system(SCROLLOFFCMD);

            doing=FALSE;
      }

      if (!internal)
            set_command(top);

      return;
}


/*
 * Does exit stuff, only once.
 */
static void do_exit(void) {
      static gboolean done=FALSE;

      if (done)
            return;
      my_system(BLANKCOMMAND);
      my_system(NICECOMMAND);
      printf(EXITMSG);
      return;
}

/*
 * Different kinds of quit. All exit.
 */
void on_quit_button_clicked(GtkButton *button,
                      gpointer user_data) {
      do_exit();
      gtk_main_quit();
      return;
}
gboolean on_gled_window_destroy_event(GtkWidget *widget,
                              GdkEvent *event,
                              gpointer user_data) {
      do_exit();
      gtk_main_quit();
      return TRUE;
}
gboolean on_gled_window_delete_event(GtkWidget *widget,
                             GdkEvent *event,
                             gpointer user_data) {
      do_exit();
      gtk_main_quit();
      return TRUE;
}


/*
 * Used in many handlers. Sets the command if not internal.
 * widget must be a real widget.
 */
void command_signal(GtkWidget *widget, gpointer data) {
      if (!internal)
            set_command(widget);
      return;
}

/*
 * Used in several handlers. Sets the command if not internal.
 * data must be a pointer to a real widget.
 */
void command_signal_data(GtkWidget *widget, gpointer data) {
      if (!internal)
            set_command((GtkWidget *)data);
      return;
}






/**********************
 * Blink list handling.
 **********************/

/*
 * Selection to settings.
 * Takes delay from row data and puts it into blink_value.
 */
void on_blink_list_select_row(GtkCList *clist, gint row, gint column,
                        GdkEvent *event, gpointer user_data) {
      gint delay;
      GtkWidget *value;

      delay=GPOINTER_TO_INT(gtk_clist_get_row_data(clist,row));
      value=lookup_widget(GTK_WIDGET(clist),"blink_value");
      gtk_spin_button_set_value(GTK_SPIN_BUTTON(value),delay);
      return;
}

/*
 * The 'Add'-button.
 * Adds a row before the selected row or at the end of the list.
 * Corrects the ON/OFF flags.
 */
void on_blink_add_button_clicked(GtkButton *button,
                         gpointer user_data) {
      GtkWidget *list;
      GtkWidget *spin;
      gint value;
      gchar *str[3];
      gchar buf[10];

      list=lookup_widget(GTK_WIDGET(button), "blink_list");
      spin=lookup_widget(GTK_WIDGET(button), "blink_value");
      value=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));

      str[0]="ON";   /* Will be corrected later */
      snprintf(buf,10,"%d",value);
      str[1]=buf;
      str[2]=NULL;
      gtk_clist_freeze(GTK_CLIST(list));
      list_add(list,str,GINT_TO_POINTER(value));
      blink_list_correct_on_off(list,NULL);
      gtk_clist_thaw(GTK_CLIST(list));
      return;
}

/*
 * Changes the currently selected piece to the current setting
 * in blink_value.
 */
void on_blink_change_button_clicked(GtkButton *button,gpointer user_data) {
      GtkWidget *list;
      GtkWidget *spin;
      gint value;
      gint row;
      gchar buf[10];

      list=lookup_widget(GTK_WIDGET(button),"blink_list");
      if (GTK_CLIST(list)->selection==NULL)
            return;
      row=GPOINTER_TO_INT(GTK_CLIST(list)->selection->data);
      spin=lookup_widget(GTK_WIDGET(button),"blink_value");
      value=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));

      snprintf(buf,10,"%d",value);
      gtk_clist_freeze(GTK_CLIST(list));
      gtk_clist_set_text(GTK_CLIST(list),row,1,buf);
      gtk_clist_set_row_data(GTK_CLIST(list),row,GINT_TO_POINTER(value));
      gtk_clist_thaw(GTK_CLIST(list));
      return;
}





/*
 * Corrects the state of the ON/OFF texts in the first column of the
 * blink list.
 */
void blink_list_correct_on_off(GtkWidget *top,gpointer data) {
      gint i;
      GtkWidget *list;

      list=lookup_widget(top,"blink_list");

      gtk_clist_freeze(GTK_CLIST(list));
      for (i=0; i<GTK_CLIST(list)->rows; i++) {
            if (i&1)
                  gtk_clist_set_text(GTK_CLIST(list),i,0,"OFF");
            else
                  gtk_clist_set_text(GTK_CLIST(list),i,0,"ON");
      }
      gtk_clist_thaw(GTK_CLIST(list));
      return;
}





/**************************
 * Animation list handling.
 **************************/

/*
 * Adds a set LED command to the list.
 */
void on_animation_set_add_button_clicked(GtkButton *button,
                               gpointer user_data) {
      GtkWidget *top=GTK_WIDGET(button);
      gint n,s,c;
      gchar *str[4];
      gint change=0;
      GtkWidget *list;

      n=option_menu_get_active(top,"animation_num_opt");
      c=option_menu_get_active(top,"animation_caps_opt");
      s=option_menu_get_active(top,"animation_scroll_opt");

      if (n==ANIM_OPT_NOTHING &&
          c==ANIM_OPT_NOTHING &&
          s==ANIM_OPT_NOTHING)
            return;

      /* Num Lock */
      switch (n) {
      case ANIM_OPT_ON:
            change|=NUM_ON;
            break;
      case ANIM_OPT_OFF:
            change|=NUM_OFF;
            break;
      case ANIM_OPT_NORMAL:
            change|=NUM_NORMAL;
            break;
      }
            
      /* Caps Lock */
      switch (c) {
      case ANIM_OPT_ON:
            change|=CAPS_ON;
            break;
      case ANIM_OPT_OFF:
            change|=CAPS_OFF;
            break;
      case ANIM_OPT_NORMAL:
            change|=CAPS_NORMAL;
            break;
      }
            
      /* Scroll Lock */
      switch (s) {
      case ANIM_OPT_ON:
            change|=SCROLL_ON;
            break;
      case ANIM_OPT_OFF:
            change|=SCROLL_OFF;
            break;
      case ANIM_OPT_NORMAL:
            change|=SCROLL_NORMAL;
            break;
      }

      list=lookup_widget(top,"animation_list");

      str[0]="set";
      str[1]=str[2]=anim_make_change_command(change);
      str[3]=NULL;
      gtk_clist_freeze(GTK_CLIST(list));
      list_add(list,str,GINT_TO_POINTER(change));
      anim_correct_on_off_flags(list,NULL);
      gtk_clist_thaw(GTK_CLIST(list));

      /* Reset the option menus. */
      option_menu_set_active(top,"animation_num_opt",ANIM_OPT_NOTHING);
      option_menu_set_active(top,"animation_caps_opt",ANIM_OPT_NOTHING);
      option_menu_set_active(top,"animation_scroll_opt",ANIM_OPT_NOTHING);

      return;
}

/*
 * Returns a ncsNCSxnxcxs command of change. The string is in static memory!!
 */
static gchar *anim_make_change_command(gint change) {
      static gchar str[20];
      gint n;

      n=0;
      if (change & NUM_ON)          str[n++]='N';
      if (change & NUM_OFF)         str[n++]='n';
      if (change & NUM_NORMAL)    { str[n++]='x'; str[n++]='n'; }
      if (change & CAPS_ON)         str[n++]='C';
      if (change & CAPS_OFF)        str[n++]='c';
      if (change & CAPS_NORMAL)   { str[n++]='x'; str[n++]='c'; }
      if (change & SCROLL_ON)       str[n++]='S';
      if (change & SCROLL_OFF)      str[n++]='s';
      if (change & SCROLL_NORMAL) { str[n++]='x'; str[n++]='s'; }
      str[n]=0;
      return str;
}

/*
 * Corrects the last column of the animation list to correspond the
 * data in the list.
 */
void anim_correct_on_off_flags(GtkWidget *list,gpointer data) {
      gint n=0,c=0,s=0;   /* 0=normal, 1=on, -1=off */
      gint row;
      gint change;
      gchar str[4];
      gint i;

      list=lookup_widget(list,"animation_list");
      gtk_clist_freeze(GTK_CLIST(list));
      
      for (row=0; row<GTK_CLIST(list)->rows; row++) {
            change=GPOINTER_TO_INT(gtk_clist_get_row_data(GTK_CLIST(list),
                                                row));
            if (change>0) {
                  /* Setting */
                  if (change&NUM_ON)         n=1;
                  if (change&NUM_NORMAL)     n=0;
                  if (change&NUM_OFF)        n=-1;
                  if (change&CAPS_ON)        c=1;
                  if (change&CAPS_NORMAL)    c=0;
                  if (change&CAPS_OFF)       c=-1;
                  if (change&SCROLL_ON)      s=1;
                  if (change&SCROLL_NORMAL)  s=0;
                  if (change&SCROLL_OFF)     s=-1;
                  
                  gtk_clist_set_text(GTK_CLIST(list),row,2,"");
                  continue;
            }
            /* Delay */
            i=0;
            if (n==1)   str[i++]='N';
            if (n==-1)  str[i++]='n';
            if (c==1)   str[i++]='C';
            if (c==-1)  str[i++]='c';
            if (s==1)   str[i++]='S';
            if (s==-1)  str[i++]='s';
            str[i]=0;
            gtk_clist_set_text(GTK_CLIST(list),row,2,str);
      }
      gtk_clist_thaw(GTK_CLIST(list));
      return;
}

/*
 * When selected, set the setting of the row to the boxes.
 */
void on_animation_list_select_row(GtkCList *clist, gint row,
                          gint column, GdkEvent *event,
                          gpointer user_data) {
      GtkWidget *top=GTK_WIDGET(clist);
      GtkWidget *spin;
      gint change;

      if (internal)
            return;

      change=GPOINTER_TO_INT(gtk_clist_get_row_data(GTK_CLIST(clist),row));
      if (change<=0) {
            /* It's a delay */
            change=-change;
            spin=lookup_widget(top,"animation_delay_value");
            gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin),change);
            return;
      }

      /* Num Lock */
      if (change & NUM_ON)
            option_menu_set_active(top,"animation_num_opt",
                               ANIM_OPT_ON);
      else if (change & NUM_OFF)
            option_menu_set_active(top,"animation_num_opt",
                               ANIM_OPT_OFF);
      else if (change & NUM_NORMAL)
            option_menu_set_active(top,"animation_num_opt",
                               ANIM_OPT_NORMAL);
      else
            option_menu_set_active(top,"animation_num_opt",
                               ANIM_OPT_NOTHING);

      /* Caps Lock */
      if (change & CAPS_ON)
            option_menu_set_active(top,"animation_caps_opt",
                               ANIM_OPT_ON);
      else if (change & CAPS_OFF)
            option_menu_set_active(top,"animation_caps_opt",
                               ANIM_OPT_OFF);
      else if (change & CAPS_NORMAL)
            option_menu_set_active(top,"animation_caps_opt",
                               ANIM_OPT_NORMAL);
      else
            option_menu_set_active(top,"animation_caps_opt",
                               ANIM_OPT_NOTHING);

      /* Scroll Lock */
      if (change & SCROLL_ON)
            option_menu_set_active(top,"animation_scroll_opt",
                               ANIM_OPT_ON);
      else if (change & SCROLL_OFF)
            option_menu_set_active(top,"animation_scroll_opt",
                               ANIM_OPT_OFF);
      else if (change & SCROLL_NORMAL)
            option_menu_set_active(top,"animation_scroll_opt",
                               ANIM_OPT_NORMAL);
      else
            option_menu_set_active(top,"animation_scroll_opt",
                               ANIM_OPT_NOTHING);
      return;
}

/*
 * Update a LED setting.
 */
void on_animation_set_change_button_clicked(GtkButton *button,
                                  gpointer user_data) {
      GtkWidget *top=GTK_WIDGET(button);
      GtkWidget *list;
      gint row;

      if (option_menu_get_active(top,"animation_num_opt")
          == ANIM_OPT_NOTHING &&
          option_menu_get_active(top,"animation_caps_opt")
          == ANIM_OPT_NOTHING &&
          option_menu_get_active(top,"animation_scroll_opt")
          == ANIM_OPT_NOTHING)
            return;   /* Nothing new selected. */

      /* I'm a lazy bastard... ;) */
      internal++;
      list=lookup_widget(top,"animation_list");
      if (GTK_CLIST(list)->selection==NULL)  /* Nothing selected. */
            return;
      row=GPOINTER_TO_INT(GTK_CLIST(list)->selection->data);
      if (GPOINTER_TO_INT(gtk_clist_get_row_data(GTK_CLIST(list),row))<=0)
            /* It's a delay. */
            return;
      gtk_clist_freeze(GTK_CLIST(list));
      on_animation_set_add_button_clicked(button,NULL);
      list_remove(top,"animation_list");
      anim_correct_on_off_flags(top,NULL);
      internal--;
      gtk_clist_select_row(GTK_CLIST(list),row,0);
      gtk_clist_thaw(GTK_CLIST(list));
      return;
}

/*
 * Add a delay into the animation.
 */
void on_animation_delay_add_clicked(GtkButton *button,gpointer user_data) {
      GtkWidget *list;
      GtkWidget *spin;
      gint value;
      gchar *str[4];
      gchar buf[10];

      list=lookup_widget(GTK_WIDGET(button), "animation_list");
      spin=lookup_widget(GTK_WIDGET(button), "animation_delay_value");
      value=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
      if (value < 0)
            return;

      str[0]="delay";
      snprintf(buf,10,"%d",value);
      str[1]=buf;
      str[2]="";   /* Will be corrected later. */
      str[3]=NULL;
      gtk_clist_freeze(GTK_CLIST(list));
      list_add(list,str,GINT_TO_POINTER(-value));
      anim_correct_on_off_flags(list,NULL);
      gtk_clist_thaw(GTK_CLIST(list));
      return;
}

/*
 * Update a delay setting.
 */
void on_animation_delay_change_clicked(GtkButton *button,gpointer user_data) {
      GtkWidget *list;
      GtkWidget *spin;
      gint value;
      gint row;
      gchar buf[10];

      list=lookup_widget(GTK_WIDGET(button),"animation_list");
      if (GTK_CLIST(list)->selection==NULL)
            return;
      row=GPOINTER_TO_INT(GTK_CLIST(list)->selection->data);
      value=GPOINTER_TO_INT(gtk_clist_get_row_data(GTK_CLIST(list),row));
      if (value>0)
            /* It's a LED setting.. */
            return;
      spin=lookup_widget(GTK_WIDGET(button),"animation_delay_value");
      value=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));

      snprintf(buf,10,"%d",value);
      gtk_clist_freeze(GTK_CLIST(list));
      gtk_clist_set_text(GTK_CLIST(list),row,1,buf);
      gtk_clist_set_row_data(GTK_CLIST(list),row,GINT_TO_POINTER(-value));
      gtk_clist_thaw(GTK_CLIST(list));
      return;
}




/************* TESTING *************/

/*
 * The test button handler. Execute "ledcontrol cmd".
 * First makes the command, then tests it. If OK, then executes it.
 * If internal set, then takes command straight from user_data and uses
 * it (without checking it).
 */
void on_test_button_clicked(GtkWidget *top, gpointer user_data) {
      GtkWidget *entry;
      gchar *cmd;
      gchar *command;

      /* If internal, then it has been checked. */
      if (!internal) {
            internal++;
            set_command(top);
            entry=lookup_widget(top,"command_entry");
            cmd=gtk_entry_get_text(GTK_ENTRY(entry));
            if (!parse_command_main(cmd,top,FALSE)) {
                  printf("\a");
                  fflush(stdout);
                  return;
            }
            internal--;
      } else
            cmd=(gchar *)user_data;
      command=g_strconcat(COMMAND," ",cmd,NULL);

      my_system(command);
      g_free(command);
      return;
}


/************* NOT YET EDITED ***************/



Generated by  Doxygen 1.6.0   Back to index