/*  Copyright (C) 2002 Øyvind Hagen */

/*  This file is part of gkleds. */

/*  gkleds is free software; you can redistribute it and/or modify */
/*  it under the terms of the GNU General Public License Version 2 as */ 
/*  published by the Free Software Foundation */

/*  gkleds is distributed in the hope that it will be useful, */
/*  but WITHOUT ANY WARRANTY; without even the implied warranty of */
/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/*  GNU General Public License for more details.  */

/*  You should have received a copy of the GNU General Public License */
/*  along with gkleds; if not, write to the Free Software */
/*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */

/* gkrellm already includes several libs */
#include <X11/Xlib.h>
#include <X11/XKBlib.h>
#include <X11/extensions/XTest.h>
#include <X11/keysym.h>

#include <gkrellm2/gkrellm.h>

#include "gkleds.h"
#include "pixmaps/leds.xpm"

static GkrellmMonitor gkleds_mon = {
  GKLEDS_NAME,		/* Name, for config tab. */
  0,			/* Id,  0 if a plugin */
  create_plugin,	/* The create_plugin() function */
  get_indicators,	/* The update_plugin() function */
  create_plugin_config,	/* The create_plugin_tab() config function */
  apply_plugin_config,	/* The apply_plugin_config() function */

  save_plugin_config,	/* The save_plugin_config() function */
  load_plugin_config,	/* The load_plugin_config() function */
  GKLEDS_CONFIG,	/* config keyword */

  NULL,			/* Undefined 2 */
  NULL,			/* Undefined 1 */
  NULL,			/* Undefined 0 */

  GKLEDS_PLACEMENT,	/* Insert plugin before this monitor */
  NULL,			/* Handle if a plugin, filled in by GKrellM */
  NULL			/* path if a plugin, filled in by GKrellM */
};

/* default config */
static gkleds_user_conf conf = {
  {2, 2, 2, 0},
  {0, 0},
  {6, 0, 7, 0, 6 ,1, 7, 1, 6, 2, 7, 2},
  {1, 2, 3},
  0,
  GKLEDS_NUM_IMG
};
static gkleds_user_conf na_conf;
static gkleds_indicator_conf ind_conf;
static GkrellmPanel *panel;
static GkrellmDecal *decals[GKLEDS_NUM_IND];
static GkrellmMonitor *monitor;
static gint style_id;
static GtkWidget *gk_vbox;
static GtkWidget *keys_spin[3];
static GtkWidget *combos[2];
static GtkWidget *pmfb_wid;
static GdkPixmap *pixmaps;
static GdkBitmap *masks;


static gint panel_expose_event(GtkWidget *widget, GdkEventExpose *ev){

  /* redraw only the exposed area */  
#ifdef GKLEDS_DEBUG
  printf("panel_expose_event: start/end\n"); fflush(stdout);
#endif
  if(widget == panel->drawing_area)
    gdk_draw_pixmap(widget->window,
		    widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
		    panel->pixmap, ev->area.x, ev->area.y, ev->area.x, 
		    ev->area.y, ev->area.width, ev->area.height);

  return FALSE;
}


static void configure_indicators(gkleds_indicator_conf *ind){
  int i, j, mask;
  char *ind_name = NULL;
  char *lock_names[GKLEDS_NUM_IND] = {"Num Lock","Caps Lock", "Scroll Lock"};
  int key_syms[GKLEDS_NUM_IND] = {XK_Num_Lock, XK_Caps_Lock, XK_Scroll_Lock};
  XkbDescPtr xkb;

#ifdef GKLEDS_DEBUG
  printf("configure_indicators: start\n"); fflush(stdout);
#endif
  /* we need a display handler to get/set indicator status */
  ind->dpy = XOpenDisplay(NULL);
  if(!ind->dpy){
    printf("gkrellm::%s : unable to connect to the X server\n", 
	   GKLEDS_NAME);
    printf("gkrellm::%s : you won't be able to get/set indicator status\n", 
	   GKLEDS_NAME);
    return;
  }
  
  /* get the keycode for set_indicators */
  for(i = 0; i < GKLEDS_NUM_IND; i++)
    ind->keys[i] = XKeysymToKeycode(ind->dpy, key_syms[i]);

  /* get a keyboard structure */
  xkb = XkbAllocKeyboard();
  if(!xkb){
    printf("gkrellm::%s : unable to allocate keyboard structure\n", 
	   GKLEDS_NAME);
    printf("gkrellm::%s : you won't be able to get/set indicator status\n", 
	   GKLEDS_NAME);
    XCloseDisplay(ind->dpy); ind->dpy = NULL;
    return;
  }

  /* get the names(atoms) of the indicators */
  if(XkbGetNames(ind->dpy, XkbIndicatorNamesMask, xkb) != Success){
    printf("gkrellm::%s : unable to get indicator names from the X server\n", 
	   GKLEDS_NAME);
    printf("gkrellm::%s : you won't be able to get/set indicator status\n", 
	   GKLEDS_NAME);
    XkbFreeKeyboard(xkb, 0, True);
    XCloseDisplay(ind->dpy); ind->dpy = NULL;
    return;
  }    

  /* get the correct mask & shift for get_indicators */ 
  for(i = 0; i < XkbNumIndicators; i++){
    if(xkb->names->indicators[i])
      ind_name = XGetAtomName(ind->dpy, xkb->names->indicators[i]);
#ifdef GKLEDS_DEBUG
  if(ind_name) printf("configure_indicators: %s\n", ind_name); fflush(stdout);
#endif
    for(j = 0; j < GKLEDS_NUM_IND; j++){
      if(ind_name && !strcmp(lock_names[j], ind_name)){
	if(XkbGetNamedIndicator(ind->dpy, xkb->names->indicators[i], &mask, 
				NULL, NULL, NULL) == True){
#ifdef GKLEDS_DEBUG
  printf("configure_indicators: mask %d: %d\n", i, mask); fflush(stdout);
#endif
	  ind->masks[j] = 1 << mask;
	  ind->idx[j] = mask;
	}else{
	  printf("gkrellm::%s : unable to get the indicator mask from the X "
		 "server\n", GKLEDS_NAME);
	  printf("gkrellm::%s : you won't be able to get/set indicator "
		 "status\n", GKLEDS_NAME);
	  XkbFreeKeyboard(xkb, 0, True);
	  XCloseDisplay(ind->dpy); ind->dpy = NULL;
	  return;
	}
      }
    }
    if(ind_name){ 
      free(ind_name);
      ind_name = NULL;
    }
  }

  XkbFreeKeyboard(xkb, 0, True);

#ifdef GKLEDS_DEBUG
  printf("configure_indicators: end\n"); fflush(stdout);
#endif
  return;
}


static void set_indicators(GtkWidget *widget, GdkEventButton *ev){
  int i;

#ifdef GKLEDS_DEBUG
      printf("set_indicators: start\n"); fflush(stdout);
#endif
  if(!ind_conf.dpy) return;
  if(widget != panel->drawing_area || 
     (ev->y < ind_conf.y || ev->y > ind_conf.y+ind_conf.h)) return;
#ifdef GKLEDS_DEBUG
      printf("set_indicators: ev->button: %d\n", ev->button);
      printf("set_indicators: mb_panel: %d\n", conf.mb_panel);
      fflush(stdout);
#endif
  if((conf.mb_panel != 0) && (ev->button != conf.mb_panel)) return;

  /* find out if a led has been clicked. if so, then set it */
  /* generating a key event is a nice, low cost way of setting it */
  for(i = 0; i < GKLEDS_NUM_IND; i++){
    if(conf.order[i]  && (ev->x > ind_conf.led_x[i]) && 
       (ev->x < ind_conf.led_x[i]+ind_conf.w)){ 
      XTestFakeKeyEvent(ind_conf.dpy, ind_conf.keys[i], True, CurrentTime);
      XTestFakeKeyEvent(ind_conf.dpy, ind_conf.keys[i], False, CurrentTime);
#ifdef GKLEDS_DEBUG
      printf("set_indicators: ind: %d\n", i); fflush(stdout);
#endif
    }
  }
  
#ifdef GKLEDS_DEBUG
      printf("set_indicators: end\n"); fflush(stdout);
#endif
  return;
}


static void get_indicators(void){
  unsigned int states;
  int i;
  
  if(!ind_conf.dpy) return;

  /* get the indicator status */
  if(XkbGetIndicatorState(ind_conf.dpy, XkbUseCoreKbd, &states) != Success)
    return;

  /* draw the correct led state onto the panel */
  for(i = 0; i < GKLEDS_NUM_IND; i++){
   if(conf.order[i])
     gkrellm_draw_decal_pixmap(panel, decals[i], (states & ind_conf.masks[i]) 
				>> ind_conf.idx[i]); 
  }
  gkrellm_draw_panel_layers(panel);

  return;
}


static void create_plugin(GtkWidget *vbox, gint first_create){
  GkrellmStyle *style;
  int height, width, vis, i, j, highest_order, num_img, tmp; 
  int pos[GKLEDS_NUM_IND+1];
  int chart_width = gkrellm_chart_width();
  gboolean theme_found;
  GkrellmPiximage *leds_img = NULL;
  GdkPixmap *subpixmap = NULL;
  GdkBitmap *submask = NULL;
  GdkGC *mask_gc, *pixmap_gc;
  GdkColor color;

#ifdef GKLEDS_DEBUG
  GtkWidget *window;
  GtkWidget *pm_wid;
  printf("create_plugin: start\n"); fflush(stdout);
#endif
  if(first_create){
    configure_indicators(&ind_conf);
    panel = gkrellm_panel_new0();
    gk_vbox = vbox;
  }else{
    gkrellm_destroy_decal_list(panel); 
  }

  /* theme configuration */
  style = gkrellm_meter_style(style_id);
  theme_found = gkrellm_load_piximage("leds", leds_xpm, &leds_img, GKLEDS_NAME);
  if(gkrellm_get_gkrellmrc_integer("gkleds_num_img", &num_img)){
    conf.num_img = num_img + GKLEDS_NUM_IMG;
  }else{
    theme_found = FALSE;
    leds_img = gkrellm_piximage_new_from_xpm_data(leds_xpm);
  }
  
#ifdef GKLEDS_DEBUG
  printf("create_plugin: before sizing\n"); fflush(stdout);
#endif
 /* set the correct width/height for image scaling */
  if(conf.size[0] > 0) width = conf.size[0];
  else width = gdk_pixbuf_get_width(leds_img->pixbuf);

  if(conf.size[1] > 0){ 
    height = conf.size[1];
  }else{
    if(theme_found) height = 
      (int) gdk_pixbuf_get_height(leds_img->pixbuf) / num_img;
    else height = 
      (int) gdk_pixbuf_get_height(leds_img->pixbuf) / GKLEDS_NUM_IMG;
  }
  /* number of visible leds */ 
  for(vis = 0, i = 0; i < GKLEDS_NUM_IND; i++)
    if(conf.order[i] > 0) vis++;
    
  /* constrain to chart width */
  if(vis && (width * vis > chart_width)) width = (int) (chart_width)/vis;

#ifdef GKLEDS_DEBUG
  printf("create_plugin: before combine\n"); fflush(stdout);
#endif
  /* combine theme pixmap and embedded pixmap */
  pixmaps = gdk_pixmap_new(vbox->window, width, height*conf.num_img, -1);
  masks = gdk_pixmap_new(NULL, width, height*conf.num_img, 1);
  mask_gc = gdk_gc_new(masks);
  pixmap_gc = gdk_gc_new(pixmaps);
  /* pixmap_new doesn't clear, we have to clear it for the mask */
  /* to get the transperancy correct */
  gdk_color_black(gdk_colormap_get_system(), &color);
  gdk_gc_set_foreground(mask_gc, &color);
  gdk_draw_rectangle(masks, mask_gc, TRUE, 0, 0, -1, -1);
  if(theme_found){
#ifdef GKLEDS_DEBUG
  printf("create_plugin: theme_img\n"); fflush(stdout);
#endif
    gkrellm_scale_pixbuf_to_pixmap(leds_img->pixbuf, &subpixmap, &submask, 
				 width, height*num_img);
    if(!submask){
      submask = gdk_pixmap_new(NULL, width, height*conf.num_img, 1);
      gdk_color_white(gdk_colormap_get_system(), &color);
      gdk_gc_set_foreground(mask_gc, &color);
      gdk_draw_rectangle(submask, mask_gc, TRUE, 0, 0, -1, -1);
    }
    gdk_draw_pixmap(pixmaps, pixmap_gc, subpixmap, 0, 0, 0, 
		    GKLEDS_NUM_IMG*height, width, num_img*height);
    gdk_draw_pixmap(masks, mask_gc, submask, 0, 0, 0, GKLEDS_NUM_IMG*height, 
		    width, num_img*height);
    gkrellm_free_pixmap(&subpixmap);
    gkrellm_free_bitmap(&submask);
    gkrellm_destroy_piximage(leds_img);
    leds_img = gkrellm_piximage_new_from_xpm_data(leds_xpm);
  }
#ifdef GKLEDS_DEBUG
  printf("create_plugin: embedded\n"); fflush(stdout);
#endif
  gkrellm_scale_pixbuf_to_pixmap(leds_img->pixbuf, &subpixmap, &submask, 
				 width, height*GKLEDS_NUM_IMG);
  gdk_draw_pixmap(pixmaps, pixmap_gc, subpixmap, 0, 0, 0, 0, 
		  width, height*GKLEDS_NUM_IMG);
  gdk_draw_pixmap(masks, mask_gc, submask, 0, 0, 0, 0, 
		  width, height*GKLEDS_NUM_IMG);
  gkrellm_free_pixmap(&subpixmap);
  gkrellm_free_bitmap(&submask);
  gkrellm_destroy_piximage(leds_img);
  gdk_gc_unref(pixmap_gc);
  gdk_gc_unref(mask_gc);
#if 0
  printf("create_plugin: after combine\n"); fflush(stdout);
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  pm_wid = gtk_pixmap_new(pixmaps, masks);
  gtk_container_add(GTK_CONTAINER(window), pm_wid);
  gtk_widget_show(pm_wid);
  gtk_widget_show(window);
#endif

  /* we have to make sure the user configuration is correct */
  for(i = 0; i < GKLEDS_NUM_IND*4; i++)
    if(conf.pm_idx[i] >= conf.num_img) conf.pm_idx[i] = -1;
  for(i = 0; i < GKLEDS_NUM_IND; i++)
    if(conf.order[i] > GKLEDS_NUM_IND) conf.order[i] = 0;

#ifdef GKLEDS_DEBUG
  printf("create_plugin: width: %d\n", width);
  printf("create_plugin: height: %d\n", height);
  printf("create_plugin: vis: %d\n", vis);
  fflush(stdout);
#endif
 
  /* determine the positioning of the leds */
  for(highest_order = 0, i = 0; i < GKLEDS_NUM_IND; i++)
    if(conf.order[i] > highest_order) highest_order = conf.order[i];
  if(vis == 1){
    pos[1] = conf.margin[0] + (int) (chart_width/2 - width/2) - conf.margin[1];
    pos[2] = conf.margin[0] + (int) (chart_width/2 - width/2) - conf.margin[1];
    pos[3] = conf.margin[0] + (int) (chart_width/2 - width/2) - conf.margin[1];
  }
  else if(vis == 2){
    pos[1] = conf.margin[0] + (int) (chart_width/3 - 4*width/6);
    if(highest_order == 3)
      pos[2] = conf.margin[0] + (int) (chart_width/3 - 4*width/6);
    else
      pos[2] = (int) (2*chart_width/3 - 2*width/6) - conf.margin[1];
    pos[3] = (int) (2*chart_width/3 - 2*width/6) - conf.margin[1];
  }
  else if(vis == 3){
    pos[1] = conf.margin[0];
    pos[2] = conf.margin[0] + (int) (chart_width/2 - width/2) - conf.margin[1];
    pos[3] = chart_width - width - conf.margin[1];
  }
  /* constrain to drawing area */
  pos[0] = 0;
  for(i = 1; i < GKLEDS_NUM_IND+1; i++){
    if(pos[i] < 0) pos[i] = 0;
    else if(pos[i] > chart_width) pos[i] = chart_width - width; 
#ifdef GKLEDS_DEBUG
  printf("create_plugin: pos[%d]: %d\n", i, pos[i]); fflush(stdout);
#endif
  }
  ind_conf.w = width;
  ind_conf.h = height;
  ind_conf.y = conf.margin[2];
  for(i = 0; i < GKLEDS_NUM_IND; i++)
    ind_conf.led_x[i] = pos[conf.order[i]];

  /* we have to combine the foreground and background masks, */
  /* and create a stacked mask(2*h) as well as the pixmaps. */
  /* This is to get correct masking on the panel */
#ifdef GKLEDS_DEBUG
  printf("create_plugin: before decals\n");
  if(!pixmaps) printf("create_plugin: no pixmaps\n"); fflush(stdout);
  if(!masks) printf("create_plugin: no masks\n"); fflush(stdout);
#endif
  /* make the decals, create_decal cleans up pixmap/mask */
  for(j = 0; j < GKLEDS_NUM_IND; j++){
    if(conf.order[j]){
      submask = gdk_pixmap_new(NULL, width, height * 2, 1);
      mask_gc = gdk_gc_new(submask);
      /* pixmap_new doesn't clear, we have to clear it for the mask */
      gdk_color_black(gdk_colormap_get_system(), &color);
      gdk_gc_set_foreground(mask_gc, &color);
      gdk_draw_rectangle(submask, mask_gc, TRUE, 0, 0, -1, -1);
      gdk_gc_set_clip_mask(mask_gc, masks);
      subpixmap = gdk_pixmap_new(vbox->window, width, height * 2, -1);
      pixmap_gc = gdk_gc_new(subpixmap);
      gdk_gc_set_clip_mask(pixmap_gc, masks);
      tmp = (j + 1) * 4 - 2;
#ifdef GKLEDS_DEBUG
  printf("create_plugin: tmp: %d\n", tmp); fflush(stdout);
#endif
      for(i = j*4; i < (j+1)*4; i++){
	if(conf.pm_idx[i] >= 0){
#ifdef GKLEDS_DEBUG
  printf("create_plugin: pm %d: %d\n", i, conf.pm_idx[i]); fflush(stdout);
#endif
          gdk_gc_set_clip_origin(pixmap_gc, 0, height * (int) (i/tmp) - 
				 conf.pm_idx[i]*height);
	  gdk_draw_pixmap(subpixmap, pixmap_gc, pixmaps, 0, 
			  conf.pm_idx[i]*height, 0, height * (int) (i/tmp), 
			  width, height);
#ifdef GKLEDS_DEBUG
  printf("create_plugin: index %d: %d\n", i, height * (int) (i/tmp)); 
  fflush(stdout);
#endif
	  gdk_gc_set_clip_origin(mask_gc, 0, height * (int) (i/tmp) - 
				 conf.pm_idx[i]*height);
	  gdk_draw_pixmap(submask, mask_gc, masks, 0, conf.pm_idx[i]*height, 
			  0, height * (int) (i/tmp), width, height);
	}
      }
      decals[j] = gkrellm_create_decal_pixmap(panel, subpixmap, submask, 2, 
					      style, pos[conf.order[j]], 
					      conf.margin[2]);
#if 0
  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  pm_wid = gtk_pixmap_new(subpixmap, submask);
  gtk_container_add(GTK_CONTAINER(window), pm_wid);
  gtk_widget_show(pm_wid);
  gtk_widget_show(window);
#endif
      gdk_gc_unref(pixmap_gc);
      gdk_gc_unref(mask_gc);
    }
  }
#ifdef GKLEDS_DEBUG
  printf("create_plugin: after decals\n"); fflush(stdout);
#endif

  /* configure and create the panel */
  gkrellm_panel_configure(panel, NULL, style);
  gkrellm_panel_configure_add_height(panel, conf.margin[3]);
  gkrellm_panel_create(vbox, monitor, panel);
#ifdef GKLEDS_DEBUG
  printf("create_plugin: after panel_creation\n"); fflush(stdout);
#endif

  if(first_create){
    gtk_signal_connect(GTK_OBJECT(panel->drawing_area), "expose_event", 
  		       (GtkSignalFunc) panel_expose_event, NULL);
    gtk_signal_connect(GTK_OBJECT(panel->drawing_area), "button_press_event", 
  		       (GtkSignalFunc) set_indicators, NULL);
  }

#ifdef GKLEDS_DEBUG
  printf("create_plugin: end\n"); fflush(stdout);
#endif
  return;
}


static void create_plugin_config(GtkWidget *conf_vbox){
  GtkWidget *tabs;
  GtkWidget *tab_vbox;
  GtkWidget *text;
  GtkAdjustment *adjust;
  GtkWidget *spin;
  GtkWidget *label;
  GtkWidget *table;
  GtkWidget *frame;
  GtkWidget *viewport;
  GtkWidget *scroll_bar;
  GtkWidget *hbox;
  GtkWidget *button;
  GtkWidget *pixmap_wid;	
  GtkWidget *mc_combo;	
  GList *combo_list = NULL; 
  GdkPixmap *subpixmap;
  GdkBitmap *submask;
  GdkGC *mask_gc, *pixmap_gc;  
  GdkColor color;
  int width, height, i, j;
  char *info_text[] = 
  {"<b>gkleds\n",
   "\tgkleds monitors the NumLock, CapsLock and ScrollLock keys\n",
   "\tand reports their current status via on screen LEDs.\n\n",
   "<b>Margin Adjustment\n",
   "\tYou can adjust the top, bottom, left and right margins in\n",
   "\tthe options tab.\n\n", 
   "<b>Image Size\n",
   "\tYou can adjust the size of the LEDs in the options tab.\n",
   "\tSetting a size of zero keeps the orginal image size.\n\n",
   "<b>Order and Presence\n",
   "\tYou can choose which LEDs that are to be present and the\n",
   "\torder they'll be in. Setting the order to zero means that\n",
   "\tthe led will not be present on the panel.\n\n",
   "<b>Set Indicator\n",
   "\tSelect which mouse button will cause a state change to the\n",
   "\tvisible indicators on the panel. You can also choose to have\n",
   "\tall or none of the mouse buttons cause a state change\n\n",
   "<b>Image Selection\n",
   "\tYou can choose from different combinations of images in the\n"
   "\toptions tab. All leds consists of four subimages. You can also\n",
   "\tset any of the subimages to none if you want.\n"}; 
  char *about_text_locale = g_strdup_printf
    ("gkleds %s\n\n"
     "Copyright (C) 2002 Øyvind Hagen\n"
     "oyvinha@ifi.uio.no\n"
     "http://www.stud.ifi.uio.no/~oyvinha/gkleds\n\n"
     "Released under the GNU General Public Licence",
     GKLEDS_VERSION);
  char *about_text = g_locale_to_utf8(about_text_locale, -1 , 
					   NULL, NULL, NULL);
  char *margin_text[4] = {"Left Margin", "Right Margin", 
			  "Top Margin", "Bottom Margin"};
  char *size_text[2] = {"Image Width", "Image Height"}; 
  char *image_text[GKLEDS_NUM_IND+4] = {"NumLock", "CapsLock", "ScrollLock", 
				 "Background Off", "Foreground Off", 
				 "Background On", "Foreground On"}; 
  char *mc_text[7] = {"None", "Button 1", "Button 2", "Button 3", 
		      "Button 4", "Button 5", "All"}; 
  
#ifdef GKLEDS_DEBUG
  printf("create_plugin_config: start\n"); fflush(stdout);
#endif
  copy_config(&conf, &na_conf);

  /* the notebook */
  tabs = gtk_notebook_new();
  gtk_notebook_set_tab_pos(GTK_NOTEBOOK(tabs), GTK_POS_TOP);
  gtk_box_pack_start(GTK_BOX(conf_vbox), tabs, TRUE, TRUE, 0);

  /* the options tab */
  tab_vbox = gkrellm_gtk_framed_notebook_page(tabs, "Options");	
  hbox = gtk_hbox_new(FALSE, 0);

  /* the options tab, the margin adjustment area */
  frame = gtk_frame_new("Margin Adjustment");
  table = gtk_table_new(4, 2, FALSE);
  gtk_container_set_border_width(GTK_CONTAINER(table), 5);

  for(i = 0; i < 4; i++){
    adjust = (GtkAdjustment *) gtk_adjustment_new(0, 0, 99, 1, 5, 0);
    spin = gtk_spin_button_new(adjust, 1, 0);
    gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), na_conf.margin[i]);
    label = gtk_label_new(margin_text[i]);
    gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, i, i+1);
    gtk_table_attach_defaults(GTK_TABLE(table), spin, 1, 2, i, i+1);
    gtk_signal_connect(GTK_OBJECT(spin),"changed", 
		       (GtkSignalFunc) spin_misc_changed, &na_conf.margin[i]);
  }
  gtk_container_add(GTK_CONTAINER(frame), table);
  gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0);

  /* the options tab, the image size area */
  frame = gtk_frame_new("Image Size");
  table = gtk_table_new(2, 2, FALSE);
  gtk_container_set_border_width(GTK_CONTAINER(table), 5);

  for(i = 0; i < 2; i++){
    adjust = (GtkAdjustment *) gtk_adjustment_new(0, 0, 99, 1, 5, 0);
    spin = gtk_spin_button_new(adjust, 1, 0);
    gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin), na_conf.size[i]);
    label = gtk_label_new(size_text[i]);
    gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, i, i+1);
    gtk_table_attach_defaults(GTK_TABLE(table), spin, 1, 2, i, i+1);
    gtk_signal_connect(GTK_OBJECT(spin),"changed", 
		       (GtkSignalFunc) spin_misc_changed, &na_conf.size[i]);
  }
  gtk_container_add(GTK_CONTAINER(frame), table);
  gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0);

  /* the options tab, the order & presence area */
  frame = gtk_frame_new("Order & Presence");
  table = gtk_table_new(3, 2, FALSE);
  gtk_container_set_border_width(GTK_CONTAINER(table), 5);

  for(i = 0; i < 3; i++){
   label = gtk_label_new(image_text[i]);
   adjust = (GtkAdjustment *) gtk_adjustment_new(0, 0, 3, 1, 1, 0);
   keys_spin[i] = gtk_spin_button_new(adjust, 1, 0);
   gtk_spin_button_set_value(GTK_SPIN_BUTTON(keys_spin[i]), 
	 		    na_conf.order[i]);
   gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, i, i+1);
   gtk_table_attach_defaults(GTK_TABLE(table), keys_spin[i], 1, 2, i, i+1);
   gtk_signal_connect(GTK_OBJECT(keys_spin[i]),"changed",
		     (GtkSignalFunc) spin_order_changed, (int *) i);
  }
  gtk_container_add(GTK_CONTAINER(frame), table);
  gtk_box_pack_start(GTK_BOX(hbox), frame, TRUE, TRUE, 0);

  gtk_box_pack_start(GTK_BOX(tab_vbox), hbox, FALSE, FALSE, 0);

  /* the options tab, the set indicator area */
  frame = gtk_frame_new("Set Indicator");
  table = gtk_table_new(1, 2, FALSE);
  gtk_container_set_border_width(GTK_CONTAINER(table), 5);
  gtk_table_set_col_spacing(GTK_TABLE(table), 0, 20);
  gtk_table_set_col_spacing(GTK_TABLE(table), 1, 20);
  gtk_table_set_row_spacing(GTK_TABLE(table), 0, 10);

  label = gtk_label_new("Mouse button that will change indicator state");
  gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);

  mc_combo = gtk_combo_new();
  for(i = 0; i < 7; i++){
    combo_list = g_list_append(combo_list, mc_text[i]);
  }
  gtk_combo_set_popdown_strings(GTK_COMBO(mc_combo), combo_list);
  g_list_free(combo_list); combo_list = NULL;
  gtk_combo_set_value_in_list(GTK_COMBO(mc_combo), TRUE, FALSE);
  gtk_table_attach_defaults(GTK_TABLE(table), mc_combo, 1, 2, 0, 1);
  gtk_signal_connect(GTK_OBJECT(GTK_COMBO(mc_combo)->entry), "changed",
		     GTK_SIGNAL_FUNC(combo_mb_panel_changed), NULL);

  gtk_container_add(GTK_CONTAINER(frame), table);
  gtk_box_pack_start(GTK_BOX(tab_vbox), frame, FALSE, FALSE, 0);

  /* the options tab, the image selection area */
  frame = gtk_frame_new("Image Selection");
  table = gtk_table_new(3, 3, FALSE);
  gtk_container_set_border_width(GTK_CONTAINER(table), 5);
  gtk_table_set_col_spacing(GTK_TABLE(table), 0, 20);
  gtk_table_set_col_spacing(GTK_TABLE(table), 1, 20);
  gtk_table_set_row_spacing(GTK_TABLE(table), 0, 10);

  for(i = 0; i < 2; i++){
    combos[i] = gtk_combo_new();
    for(j = GKLEDS_NUM_IND*i; j < GKLEDS_NUM_IND+(4*i); j++){
      combo_list = g_list_append(combo_list, image_text[j]);
    }
    gtk_combo_set_popdown_strings(GTK_COMBO(combos[i]), combo_list);
    g_list_free(combo_list); combo_list = NULL;
    gtk_combo_set_value_in_list(GTK_COMBO(combos[i]), TRUE, FALSE);
    gtk_table_attach_defaults(GTK_TABLE(table), combos[i], i, i+1, 0, 1);
    gtk_signal_connect(GTK_OBJECT(GTK_COMBO(combos[i])->entry), "changed",
		       GTK_SIGNAL_FUNC(pm_idx_altered), (int *) -3);
  }

  /* make feedback pixmap */
  gdk_window_get_size(pixmaps, &width, &height);
  height = (int) height / conf.num_img;
  subpixmap = gdk_pixmap_new(gk_vbox->window, width, height, -1);
  submask = gdk_pixmap_new(NULL, width, height, 1);
  pixmap_gc = gdk_gc_new(subpixmap);
  mask_gc = gdk_gc_new(submask);
  gdk_color_black(gdk_colormap_get_system(), &color);
  gdk_gc_set_foreground(mask_gc, &color);
  gdk_draw_rectangle(submask, mask_gc, TRUE, 0, 0, -1, -1);
  if(na_conf.pm_idx[0] >= 0){  
    gdk_draw_pixmap(subpixmap, pixmap_gc, pixmaps, 
		    0, na_conf.pm_idx[0]*height, 0, 0, width, height);
    gdk_draw_pixmap(submask, mask_gc, masks,
		    0, na_conf.pm_idx[0]*height, 0, 0, width, height);
  }  
  pmfb_wid = gtk_pixmap_new(subpixmap, submask);
  gdk_pixmap_unref(subpixmap);
  gdk_pixmap_unref(submask);
  gtk_table_attach_defaults(GTK_TABLE(table), pmfb_wid, 2, 3, 0, 1);

#ifdef GKLEDS_DEBUG
  printf("create_plugin_config: before button image\n"); fflush(stdout);
#endif
  hbox = gtk_hbox_new(FALSE, 0);

  button = gtk_button_new_with_label("none");
  gtk_signal_connect(GTK_OBJECT(button), "clicked",
		       GTK_SIGNAL_FUNC(pm_idx_altered), (int *) -1);
  gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
  for(i = 0; i < conf.num_img; i++){
    subpixmap = gdk_pixmap_new(gk_vbox->window, width, height, -1);
    submask = gdk_pixmap_new(NULL, width, height, 1);
    gdk_draw_pixmap(subpixmap, pixmap_gc, pixmaps, 0, i*height, 0, 0, 
		    width, height);
    gdk_draw_pixmap(submask, mask_gc, masks, 0, i*height, 0, 0, width, height);
    pixmap_wid = gtk_pixmap_new(subpixmap, submask);
    button = gtk_button_new ();
    gtk_signal_connect(GTK_OBJECT(button), "clicked",
		       GTK_SIGNAL_FUNC(pm_idx_altered), (int *) i);
    gtk_container_add(GTK_CONTAINER(button), pixmap_wid);
    gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
    gdk_pixmap_unref(subpixmap);
    gdk_pixmap_unref(submask);
  }
  /* clean up gcs */
  gdk_gc_unref(pixmap_gc);
  gdk_gc_unref(mask_gc);

#ifdef GKLEDS_DEBUG
  printf("create_plugin_config: after button image\n"); fflush(stdout);
#endif
  
  viewport = gtk_viewport_new(NULL, NULL);
  scroll_bar = 
    gtk_hscrollbar_new(gtk_viewport_get_hadjustment(GTK_VIEWPORT(viewport)));
  gtk_container_add(GTK_CONTAINER(viewport), hbox);
  gtk_table_attach_defaults(GTK_TABLE(table), viewport, 0, 3, 1, 2);
  gtk_table_attach_defaults(GTK_TABLE(table), scroll_bar, 0, 3, 2, 3);

  gtk_container_add(GTK_CONTAINER(frame), table);
  gtk_box_pack_end(GTK_BOX(tab_vbox), frame, FALSE, FALSE, 0);

  /* the info tab */
  tab_vbox = gkrellm_gtk_framed_notebook_page(tabs, "Info");
  text = gkrellm_gtk_scrolled_text_view(tab_vbox, NULL, GTK_POLICY_AUTOMATIC, 
			       GTK_POLICY_AUTOMATIC);
  gkrellm_gtk_text_view_append_strings(text, info_text, 
			sizeof(info_text)/sizeof(gchar *));
  /* the about tab */
  tab_vbox = gkrellm_gtk_framed_notebook_page(tabs, "About");
  text = gtk_label_new(about_text); 
  gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_CENTER);
  gtk_box_pack_start(GTK_BOX(tab_vbox), text, TRUE, TRUE, 0);
  if(about_text) free(about_text);

#ifdef GKLEDS_DEBUG
  printf("create_plugin_config: end\n"); fflush(stdout);
#endif
  return;
}


/* callback for spinbutton changed in margin adjustment and image size */
static void spin_misc_changed(GtkWidget *spin, gpointer data){
  
#ifdef GKLEDS_DEBUG
  printf("spin_misc_changed: start/end\n"); fflush(stdout);
#endif
  *((int *) data) = 
    gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));

  return;
}


/* callback for spinbutton changed in order & presence */
static void spin_order_changed(GtkWidget *spin, gpointer data){
  int indicator = (int) data;
  int order = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
  int old_indicator = -1; /* new order's old indicator */
  int old_order; /* new indicator's old order */
  int i;

#ifdef GKLEDS_DEBUG
  printf("spin_order_changed: start\n"); fflush(stdout);
  printf("spin_order_changed: indicator: %d\n", indicator); fflush(stdout);
  printf("spin_order_changed: order: %d\n", order); fflush(stdout);
#endif
  /* find the new indicator's old order */
  old_order = na_conf.order[indicator];
  
  /* find the new order's old indicator */
  for(i = 0; i < 3; i++){
    if(na_conf.order[i] == order)
      old_indicator = i;
  }
  if(na_conf.order[old_indicator] != order) old_indicator = -1;

  /* set the new indicator to the new order */
  na_conf.order[indicator] = order;

#ifdef GKLEDS_DEBUG
  printf("spin_order_changed: indicator: %d\n", indicator); 
  printf("spin_order_changed: order: %d\n", order); 
  printf("spin_order_changed: old_indicator: %d\n", old_indicator); 
  printf("spin_order_changed: old_order: %d\n", old_order); 
  fflush(stdout);
#endif
  /* set the old indicator to the old order and spin it */
  /* old_indicator isn't sane when order == 0, */
  /* and we don't want to spin it if new order is free */ 
  /* NB! this will create a new changed signal, */
  /* which we will have to deal with */
  if(order && (old_indicator != -1)){
    na_conf.order[old_indicator] = old_order;
    gtk_spin_button_set_value(GTK_SPIN_BUTTON(keys_spin[old_indicator]), 
			      (float) old_order);
  }

#ifdef GKLEDS_DEBUG
  printf("spin_order_changed: end\n"); fflush(stdout);
#endif
  return;
}


/* callback for which mouse button sets led state */
static void combo_mb_panel_changed(GtkWidget *mc_entry, gpointer data){
  const char *mc_text = gtk_entry_get_text(GTK_ENTRY(mc_entry));
  
#ifdef GKLEDS_DEBUG
  printf("combo_mb_panel_changed: start/end\n"); fflush(stdout);
#endif
  if(!strcmp(mc_text, "None")) na_conf.mb_panel = -1;
  else if(!strcmp(mc_text, "Button 1")) na_conf.mb_panel = 1;
  else if(!strcmp(mc_text, "Button 2")) na_conf.mb_panel = 2;
  else if(!strcmp(mc_text, "Button 3")) na_conf.mb_panel = 3;
  else if(!strcmp(mc_text, "Button 4")) na_conf.mb_panel = 4;
  else if(!strcmp(mc_text, "Button 5")) na_conf.mb_panel = 5;
  else if(!strcmp(mc_text, "All")) na_conf.mb_panel = 0;

  return;
}


/* callback for pixmap buttons clicked and combos changed in image selection */
static void pm_idx_altered(GtkWidget *widget, gpointer data){
  const char *key_txt = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(combos[0])->entry));
  const char *subimg_txt = 
    gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(combos[1])->entry));
  int index = 0;
  GdkPixmap *subpixmap;
  GdkBitmap *submask;
  GdkGC *mask_gc, *pixmap_gc;
  GdkColor color;
  int width, height;

#ifdef GKLEDS_DEBUG
  printf("pm_idx_altered: start\n"); fflush(stdout);
#endif
  /* find the correct index into pm_idx */
  if(!strcmp(key_txt, "NumLock"))
    index += 0; 
  else if(!strcmp(key_txt, "CapsLock"))
    index += 4; 
  else if(!strcmp(key_txt, "ScrollLock"))
    index += 8; 
  else 
    return;
  if(!strcmp(subimg_txt, "Background Off"))
    index += 0; 
  else if(!strcmp(subimg_txt, "Foreground Off"))
    index += 1; 
  else if(!strcmp(subimg_txt, "Background On"))
    index += 2; 
  else if(!strcmp(subimg_txt, "Foreground On"))
    index += 3; 
  else
    return;
#ifdef GKLEDS_DEBUG
  printf("pm_idx_altered: index: %d\n", index); fflush(stdout);
#endif

  /* data isn't sane for combo */
  if((int) data != -3) na_conf.pm_idx[index] = (int) data;
#ifdef GKLEDS_DEBUG
  if((int) data != -3) printf("pm_idx_altered: is_button\n"); 
  else printf("pm_idx_altered: is_combo widget\n");
  fflush(stdout);
#endif
  
  /* update pixmap of choosen led subimage */
  gdk_window_get_size(pixmaps, &width, &height);
  height = (int) height / conf.num_img;
  subpixmap = gdk_pixmap_new(gk_vbox->window, width, height, -1);
  submask = gdk_pixmap_new(NULL, width, height, 1);
  pixmap_gc = gdk_gc_new(subpixmap);
  mask_gc = gdk_gc_new(submask);
  gdk_color_black(gdk_colormap_get_system(), &color);
  gdk_gc_set_foreground(mask_gc, &color);
  gdk_draw_rectangle(submask, mask_gc, TRUE, 0, 0, -1, -1);
  if(na_conf.pm_idx[index] >= 0){  
    gdk_draw_pixmap(subpixmap, pixmap_gc, pixmaps, 
		    0, na_conf.pm_idx[index]*height, 0, 0, width, height);
    gdk_draw_pixmap(submask, mask_gc, masks, 
		    0, na_conf.pm_idx[index]*height, 0, 0, width, height);
  }  
  gtk_pixmap_set(GTK_PIXMAP(pmfb_wid), subpixmap, submask);
  /* clean up gc and pixmaps */
  gdk_pixmap_unref(subpixmap);
  gdk_pixmap_unref(submask);
  gdk_gc_unref(pixmap_gc);
  gdk_gc_unref(mask_gc);

#ifdef GKLEDS_DEBUG
  printf("pm_idx_altered: end\n"); fflush(stdout);
#endif
  return;
}


/* just hide how config copying is done */
static inline void copy_config(gkleds_user_conf *from, gkleds_user_conf *to){

  memcpy(to, from, sizeof(gkleds_user_conf));
 
  return;
}


static void apply_plugin_config(void){

#ifdef GKLEDS_DEBUG
  int i;
  printf("apply_plugin_config: start\n"); fflush(stdout);
#endif
  copy_config(&na_conf, &conf);

#ifdef GKLEDS_DEBUG
  printf("apply_plugin_config:   margin: "); fflush(stdout);
  for(i = 0; i < 4; i++)
    printf("%d ", conf.margin[i]); fflush(stdout);
  printf("\n"); fflush(stdout);
  printf("apply_plugin_config:   width: "); fflush(stdout);
  for(i = 0; i < 2; i++)
    printf("%d ", conf.size[i]); fflush(stdout);
  printf("\n"); fflush(stdout);
  printf("apply_plugin_config:   pmidx: "); fflush(stdout);
  for(i = 0; i < GKLEDS_NUM_IND*4; i++)
    printf("%d ", conf.pm_idx[i]); fflush(stdout);
  printf("\n"); fflush(stdout);
  printf("apply_plugin_config:   order: "); fflush(stdout);
  for(i = 0; i < GKLEDS_NUM_IND; i++)
    printf("%d ", conf.order[i]); fflush(stdout);
  printf("\n"); fflush(stdout);
  printf("apply_plugin_config:   num_img: %d\n", conf.num_img); fflush(stdout);
#endif
  create_plugin(gk_vbox, 0);

#ifdef GKLEDS_DEBUG
  printf("apply_plugin_config: end\n"); fflush(stdout);
#endif
  return;
}


static void save_plugin_config(FILE *f){
  int i;

#ifdef GKLEDS_DEBUG
  printf("save_plugin_config: start/end\n"); fflush(stdout);
#endif
  fprintf(f, "%s margin", GKLEDS_CONFIG); 
  for(i = 0; i < 4; i++)
    fprintf(f, " %d", conf.margin[i]);
  fprintf(f, "\n"); 

  fprintf(f, "%s size", GKLEDS_CONFIG); 
  for(i = 0; i < 2; i++)
    fprintf(f, " %d", conf.size[i]);
  fprintf(f, "\n"); 

  fprintf(f, "%s pm_idx", GKLEDS_CONFIG); 
  for(i = 0; i < GKLEDS_NUM_IND*4; i++)
    fprintf(f, " %d", conf.pm_idx[i]);
  fprintf(f, "\n"); 
  
  fprintf(f, "%s order", GKLEDS_CONFIG); 
  for(i = 0; i < GKLEDS_NUM_IND; i++)
    fprintf(f, " %d", conf.order[i]);
  fprintf(f, "\n"); 

  fprintf(f, "%s mb_panel %d\n", GKLEDS_CONFIG, conf.mb_panel); 

  return;
}


/* this is run once for each gkleds_plugin line in user_config */
static void load_plugin_config(gchar *arg){
  char config_name[GKLEDS_CONF_LENGTH];
  char config_data[GKLEDS_CONF_LENGTH];

#ifdef GKLEDS_DEBUG
  printf("load_plugin_config: start/end\n"); fflush(stdout);
#endif
  /* if overflow then use default config */
  if(strlen(arg) > GKLEDS_CONF_LENGTH)
    return;

  sscanf(arg,"%s %[^\n]", config_name, config_data);
  if(!strcmp(config_name, "margin"))
    sscanf(config_data, "%d %d %d %d", &conf.margin[0], &conf.margin[1], 
	   &conf.margin[2], &conf.margin[3]);
  else if(!strcmp(config_name, "size"))
    sscanf(config_data, "%d %d", &conf.size[0], &conf.size[1]);
  else if(!strcmp(config_name, "pm_idx"))
    sscanf(config_data, "%d %d %d %d %d %d %d %d %d %d %d %d", 
	   &conf.pm_idx[0], &conf.pm_idx[1], &conf.pm_idx[2], &conf.pm_idx[3],
	   &conf.pm_idx[4], &conf.pm_idx[5], &conf.pm_idx[6], &conf.pm_idx[7],
	   &conf.pm_idx[8], &conf.pm_idx[9], &conf.pm_idx[10],
	   &conf.pm_idx[11]);
  else if(!strcmp(config_name, "order"))
    sscanf(config_data, "%d %d %d", &conf.order[0], &conf.order[1], 
	   &conf.order[2]);
  else if(!strcmp(config_name, "mb_panel"))
    sscanf(config_data, "%d", &conf.mb_panel);

  return;
}


extern GkrellmMonitor *gkrellm_init_plugin(void){

#ifdef GKLEDS_DEBUG
  printf("\ngklrellm_init_plugin: start/end\n"); fflush(stdout);
#endif
  style_id = gkrellm_add_meter_style(&gkleds_mon, GKLEDS_NAME);
  monitor = &gkleds_mon;

  return &gkleds_mon;
}
