/*******************************************************************************
+
+  LEDA 3.5
+
+  _base_panel.c
+
+  This file is part of the LEDA research version (LEDA-R) that can be 
+  used free of charge in academic research and teaching. Any commercial
+  use of this software requires a license which is distributed by the
+  LEDA Software GmbH, Postfach 151101, 66041 Saarbruecken, FRG
+  (fax +49 681 31104).
+
+  Copyright (c) 1991-1997  by  Max-Planck-Institut fuer Informatik
+  Im Stadtwald, 66123 Saarbruecken, Germany     
+  All rights reserved.
+ 
*******************************************************************************/

#include <LEDA/base_window.h>
#include <LEDA/impl/x_basic.h>
#include <LEDA/bool.h>

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>

// static members

BASE_WINDOW* BASE_WINDOW::call_window = 0;


//------------------------------------------------------------------------------
// some auxiliary functions for string manipulation
//------------------------------------------------------------------------------

static char* string_dup(const char* p)
{ if (p == 0) return 0;
  char* q = new char[strlen(p)+1];
  if (q==0) 
  { fprintf(stderr,"string_dup: out of memory");
    abort();
   }
  strcpy(q,p);
  return q;
}

static char* make_string(int x)
{ char str[256];
  sprintf(str,"%d",x);
  return string_dup(str);
 }

static char* make_string(double x)
{ char str[256];
  sprintf(str,"%f",x);
  return string_dup(str);
 }


//------------------------------------------------------------------------------
// construction and destruction of panel items
//------------------------------------------------------------------------------


Panel_Item::Panel_Item(int k, const char* s, void* addr, int h, int i)
{ kind = k;
  enabled = 1;
  label_str = string_dup(s);
  ref = addr;
  height  = h;
  index = i;
  data_str = 0;
  help_str = 0;
  xcoord = 0;
  ycoord = 0;
  width = 0;
  dat1 = 0;
  dat2 = 0;
  offset = 0;
  argc = 0;
  argv = 0;
  action = 0;
  str_action = 0;
  menu_win = 0;
  menu_but = 0;
}


Panel_Item::~Panel_Item()
{ 
  if (label_str) delete[] label_str;
  if (help_str)  delete[] help_str;

  if (kind != String_Item && kind != String_Menu_Item && data_str) 
    delete[] data_str;

  if (argc > 0)
  { int i;
    if (kind == Bitmap_Choice_Item || kind == Button_Item) 
       for(i = 0; i < argc; i++)  x_delete_bitmap(argv[i]); 
    else
       for(i = 0; i < argc; i++) delete[] argv[i];
    delete[] argv;
   }

  if (menu_win) 
  { delete menu_win;
    menu_win = 0;
   }
}



//------------------------------------------------------------------------------
// panel member functions
//------------------------------------------------------------------------------

void BASE_WINDOW::panel_init()
{ 
  item_count = 0;
  but_count = 0;

  item_bg_color = white;
  bitmap_color0 = black;
  bitmap_color1 = black;

  if ( mono() )
   { panel_bg_color = white;
     shadow_color = black;   
     item_bg_color = white;   
     press_color   = white;
     disable_color  = white;
    }
  else
   { panel_bg_color = grey1;
     press_color    = grey2;
     shadow_color   = grey3;
#if defined(unix)
     disable_color  = grey2;
#else
     disable_color  = grey3;
#endif
    }

  panel_width = 0;
  panel_height = 0;

  tw = x_text_width(0,"H");
  th = x_text_height(0,"H");

  ytskip   = th + 2;
  yskip    = th + 11;
  yoff     = th/2 - 1;
  xoff     = tw + 2;

  slider_h = 12;
  color_h  = 12;

//string_h = th + 4;
  string_h = th + 3;
  button_h = string_h;
  choice_h = string_h;

  label_w   = 10;          // minimal label length
  sl_num_w  = 4*tw+tw/2;   // slider num field width
  slider_w  = 15*color_h;  // minimal slider length
  string_w  = slider_w;    // minimal string item length
  number_w  = string_w;    // minimal int/float item length
  choice_w  = 35;          // minimal choice field width
  button_w  = 20;          // minimal button width

  buts_per_line = 0;  
  center_button_label = 1;
  menu_button_style = 0;

  act_str_item = 0;
  last_sel_button = 0;
  panel_menu_mode = 0;

  has_menu_bar = 0;
 }


void BASE_WINDOW::item_error()
{ fprintf(stderr,"sorry, too many items."); 
  exit(1);
 }


//------------------------------------------------------------------------------
// adding items
//------------------------------------------------------------------------------

panel_item BASE_WINDOW::new_panel_item(int k, const char* s, void* addr, int h,
                                                             const char* hlp)
{ if (item_count >= MAX_ITEM_NUM) item_error();
  panel_item p = new Panel_Item(k,s,addr,h,item_count);
  p->help_str = string_dup(hlp);
  Item[item_count++]=p;
  if (k != Text_Item && k != Button_Item)
  { x_set_bold_font(draw_win);
    int w = x_text_width(draw_win,s) + 1;
    if (k == Slider_Item) 
       w += sl_num_w;
    else 
       if (k == String_Menu_Item) 
         w += 4*tw;
       else
         w += 2*tw;
    if (w > label_w) label_w = w;
    x_set_text_font(draw_win);
   }
  return p;
}


void BASE_WINDOW::hspace(int d)
{ panel_item p = new_panel_item(Text_Item,"",0,0,0);
  p->width = d;
  p->height = 0;
}

void BASE_WINDOW::vspace(int d)
{ panel_item p = new_panel_item(Text_Item,"",0,d,0);
  p->width = 0;
  p->height = d;
}


void BASE_WINDOW::new_line()
{ new_panel_item(Newline_Item,"",0,0,0); }

void BASE_WINDOW::fill_line()
{ new_panel_item(Fill_Item,"",0,0,0); }


  

panel_item BASE_WINDOW::text_item(const char* s)
{ panel_item it = (item_count > 0)  ? Item[item_count-1] : 0;

  if (strlen(s) == 0 || it == 0 || it->kind != Text_Item)
    { if (strlen(s) == 0)
         ytskip = x_text_height(draw_win,"H") + 4;
      else
         ytskip = x_text_height(draw_win,"H");
      if (s==0) s = "";
      it = new_panel_item(Text_Item,"",0,ytskip,0); 
      it->argv = new char*[2];
      it->argc = 0;
     }

  it->dat1 = 0;
  it->dat2 = black;

  if (s[0] == '\\')  // formatting cmd 
  { const char* p = s;
    while (*p && !isspace(*p)) p++;
    if (p) {
      if (strncmp(s,"\\bf",    p-s) == 0)  it->dat1 |= 1;
      if (strncmp(s,"\\blue",  p-s) == 0)  it->dat2 = blue;
      if (strncmp(s,"\\red",   p-s) == 0)  it->dat2 = red;
      if (strncmp(s,"\\green", p-s) == 0)  it->dat2 = green;
    }
    s = p+1;
   }
     

  // split into words
  int   argc1 = 0;
  char* argv1[256];

  while (*s && argc1 < 256)
  { while (*s && isspace(*s)) s++;
    int n = 0;
    for(const char* p=s; *p && !isspace(*p); p++) n++;
    if (n > 0)
    { char* w = new char[n+2];
      argv1[argc1++] = w;
      strncpy(w,s,n);
      w[n]   = ' ';
      w[n+1] = '\0';
      s += n;
     }
   }

  int    argc0 = it->argc;
  char** argv0 = it->argv;

  it->argc = argc0 + argc1;
  it->argv = new char*[it->argc];

  int n = 0;
  int i;
  for(i = 0; i < argc0; i++) it->argv[n++] = argv0[i]; 
  for(i = 0; i < argc1; i++) it->argv[n++] = argv1[i]; 

  delete[] argv0;

  return it;
}



panel_item BASE_WINDOW::string_item(const char* s, void* x, 
                                    panel_str_action_func F, const char* hlp)
{ panel_item p = new_panel_item(String_Item,s,x,string_h,hlp);
  p->data_str = access_str(x);
  p->offset = strlen(p->data_str);
  p->dat1 = 0;
  p->dat2 = string_w/tw - 1;
  //if (p->offset > p->dat2) p->offset = p->dat2;
  p->str_action = F;
  return p;
 }




void str_menu_selection(int code)
{ 
  int sel = code & 0xFFFF;
  int i   = (code>>16) & 0xFFFF;

  // I assume that str_menu_selection is called after closing the 
  // sub-menu and that at this moment active_window points to
  // the window that contains the string item to be changed


  BASE_WINDOW* str_menu_win = BASE_WINDOW::active_window;
  panel_item   str_menu_it = str_menu_win->Item[i];

  if (str_menu_it && sel >= 0)
  { str_menu_win->assign_str(str_menu_it->ref,str_menu_it->argv[sel]);
    str_menu_it->data_str = str_menu_win->access_str(str_menu_it->ref);
    str_menu_it->offset = strlen(str_menu_it->data_str);
    str_menu_it->dat1 = 0;
    str_menu_it->dat2 = str_menu_win->string_w/str_menu_win->tw - 1;
    str_menu_win->clipping(1);

    int draw_w = str_menu_win->draw_win;

    int          save_lw = x_set_line_width(draw_w,1);
    line_style   save_ls = x_set_line_style(draw_w,solid);
    text_mode    save_tm = x_set_text_mode(draw_w,transparent);
    drawing_mode save_mo = x_set_mode(draw_w,src_mode);
    
    //str_menu_win->draw_string_item(str_menu_it);

    str_menu_win->activate_string_item(str_menu_it,0);

    x_set_line_width(draw_w,save_lw);
    x_set_line_style(draw_w,save_ls);
    x_set_text_mode(draw_w,save_tm);
    x_set_mode(draw_w,save_mo);

    if (str_menu_it->str_action) 
    { //clipping(2);
      BASE_WINDOW::call_window = str_menu_win;
      str_menu_it->str_action((char*)str_menu_it->argv[sel]);
      //clipping(1);
     }

   }
}



void BASE_WINDOW::add_menu(panel_item p, int argc,const char** argv)
{ 
  if (p->kind != String_Menu_Item)
  { fprintf(stderr,"illegal item %s in window::add_menu\n",p->label_str);
    return;
   }
  

  if (p->argc > 0) 
  { for(int i = 0; i < p->argc; i++) delete[] p->argv[i];
    delete[] p->argv;
   }

  p->argc = argc;

  if (argc == 0) 
  { p->argv = 0;
    return;
   }

  p->argv = new char*[argc];

  int max_choice_len = 0;
  int i;
  for(i = 0; i < argc; i++) 
  { p->argv[i] = string_dup(argv[i]);
    int l = strlen(argv[i]);
    if (l > max_choice_len) max_choice_len = l;
   }

  // build menu panel

  int cols;
  if (argc < 32) cols = 1;
  else if (argc < 64) cols = 2;
  else if (argc < 128) cols = 3;
  else cols = 4;

  int rows = (argc + cols -1)/cols;

  if (p->menu_win) delete p->menu_win;

  p->menu_win = new BASE_WINDOW(-1,-1);
  p->menu_win->buttons_per_line(cols);

  for(int r = 0; r<rows; r++)
    for(int c = 0; c<cols; c++) 
    { int i = c*rows + r;
      if (i < argc)
         p->menu_win->button(p->argv[i],(p->index<<16) + i, 
                                        str_menu_selection);
      else
         p->menu_win->button("",0,(BASE_WINDOW*)0);
     }

  p->menu_but->ref = p->menu_win;
  p->menu_win->owner_item = p->menu_but;
}



panel_item BASE_WINDOW::string_menu_item(const char* s, void* x, const char*,
                                  int argc,const char** argv,
                                  panel_str_action_func F, const char* hlp)
{ 
  if (argc == 0)
     return string_item(s,x,0,hlp);

  panel_item p = new_panel_item(String_Menu_Item,s,x,string_h,hlp);
  p->data_str = access_str(x);
  p->offset = strlen(p->data_str);
  p->dat1 = 0;
  p->dat2 = string_w/tw - 1;
  if (p->offset > p->dat2) p->offset = p->dat2;

  button("",0,(BASE_WINDOW*)0);
  but_count--;

  panel_item q = Item[item_count-1];
  p->menu_but = q;
  q->width = string_h;
  q->height = string_h-1;
  q->menu_but = q;

  //p->menu_win->button_w += 2*string_h-8; 

  p->str_action = F;

  p->argc = 0;
  p->argv = 0;
  add_menu(p,argc,argv);

  return p;
}




panel_item BASE_WINDOW::int_item(const char* s, int* x, const char* hlp)
{ panel_item p = new_panel_item(Int_Item,s,x,string_h,hlp);
  p->data_str = make_string(*x);
  p->offset = strlen(p->data_str);
  p->dat1 = 0;
  p->dat2 = number_w/tw - 1;
  if (p->offset > p->dat2) p->offset = p->dat2;
  return p;
 }

panel_item BASE_WINDOW::float_item(const char* s, double* x, const char* hlp)
{ panel_item p = new_panel_item(Float_Item,s,x,string_h,hlp);
  p->data_str = make_string(*x);
  p->offset = strlen(p->data_str);
  p->dat1 = 0;
  p->dat2 = number_w/tw - 1;
  if (p->offset > p->dat2) p->offset = p->dat2;
  return p;
 }


panel_item BASE_WINDOW::slider_item(const char* s, int* x, int min, int max, 
                                                      panel_action_func F,
                                                      const char* hlp)
{ panel_item p = new_panel_item(Slider_Item,s,x,slider_h,hlp);
  p->data_str = make_string(*x);
  p->dat1 = min;
  p->dat2 = max;
  p->action = F;
  return p;
 }

panel_item BASE_WINDOW::choice_item(const char* s, int* address, int argc,
                             const char** argv, int step, int off,
                             panel_action_func F, const char* hlp)
{ 
  panel_item p = new_panel_item(Choice_Item,s,address,choice_h,hlp);
  p->data_str = 0;
  p->dat2 = step;
  p->offset = off;
  p->argc = argc;
  p->argv = new char*[argc];
  for(int i=0; i<argc; i++) 
  { int w;
    p->argv[i] = string_dup(argv[i]);
    if ((w = x_text_width(draw_win,argv[i])+10) > choice_w) choice_w = w;
   }
  p->action = F;
  return p;
 }


panel_item BASE_WINDOW::choice_mult_item(const char* s, int* address, int argc,
                                   const char** argv, panel_action_func F,
                                   const char* hlp)
{ choice_item(s,address,argc,argv,0,0,F,hlp); 
  panel_item p = Item[item_count-1];
  p->kind = Choice_Mult_Item;
  return p;
 }


panel_item BASE_WINDOW::bitmap_choice_item(const char* label, int *address, 
                                           int argc, int width, int height, 
                                           char **argv, panel_action_func F,
                                           const char* hlp)
{ int h = choice_h;
  if (height > th) h = choice_h + (height-th);
  panel_item p = new_panel_item(Bitmap_Choice_Item,label,address,h,hlp);  

  p->dat1=width+3;	   // width in dat1
  p->dat2=height+4;	   // height in dat2
  p->argc=argc;		   // number of bitmaps
  p->argv=new char*[argc]; // bitmaps
  for(int i=0; i < argc; i++) 
     p->argv[i] = x_create_bitmap(draw_win,width,height,argv[i]);
  p->action = F;
  return p;
}


panel_item BASE_WINDOW::bitmap_choice_mult_item(const char* label, int *address,
                                          int argc, int width, int height, 
                                          char **argv, panel_action_func F,
                                          const char* hlp)
{ 
  bitmap_choice_item(label,address,argc,width,height,argv,F,hlp); 
  panel_item p = Item[item_count-1];
  p->kind = Bitmap_Choice_Mult_Item;
  return p;
 }



panel_item BASE_WINDOW::bool_item(const char* s, char* address,
                                                 panel_action_func F, 
                                                 const char* hlp)
{ panel_item p = new_panel_item(Bool_Item,s,address,color_h,hlp);
  p->action = F;
  return p;
}


panel_item BASE_WINDOW::color_item(const char* s, int* x, panel_action_func F,
                                                          const char* hlp)
{ panel_item p = new_panel_item(Color_Item,s,x,color_h,hlp);
  slider_w  = 16*(color_h+2) - 2;
  string_w  = slider_w;
  number_w  = string_w;
  // button("",0,&color_menu);
  // panel_item q = Item[item_count-1];
  // p->menu_but = q;
  // q->menu_but = q;
  // button_count--;
  p->action = F;
  return p;
 }



int BASE_WINDOW::button(const char* s, int val, panel_action_func F, 
                                                const char* hlp)
{ panel_item p = new_panel_item(Button_Item,s,0,yskip,hlp);
  p->dat1 = (val == -1) ? but_count : val;
  p->action = F;
  x_set_bold_font(draw_win);
  int w = x_text_width(draw_win,s) + x_text_height(draw_win,s);
  x_set_text_font(draw_win);
  if (w  > button_w) button_w = w;
  p->width = w;
  but_count++;
  return p->dat1;
 }


int BASE_WINDOW::button(const char* s, int val, BASE_WINDOW* wp,const char* hlp)
{ 
  if (menu_button_style == 1) return menu_button(s,val,wp);

  panel_item p = new_panel_item(Button_Item,s,0,yskip,hlp);
  p->dat1 = (val == -1) ? but_count : val;
  p->ref = wp;
  if (wp) wp->owner_item = p;
  x_set_bold_font(draw_win);
  int w = x_text_width(draw_win,s) + 28;
  x_set_text_font(draw_win);
  if (w  > button_w) button_w = w;
  p->width = w;
  but_count++;
  return p->dat1;
 }


int BASE_WINDOW::button(int w, int h, char* bits, const char* s, int val, 
                                                        panel_action_func F,
                                                        const char*hlp)
{ if (h+2 > button_h) button_h = h+2;
  if (w+3 > button_w) button_w = w+3;
  panel_item p = new_panel_item(Button_Item,s,0,yskip,hlp);
  p->argv = new char*[1];
  p->argv[0] = x_create_bitmap(draw_win,w,h,bits);
  p->argc = 1;
  p->dat1 = (val == -1) ? but_count : val;
  p->action = F;
  p->width = w;
  but_count++;
  return p->dat1;
 }


int BASE_WINDOW::button(int w, int h, char* bits, const char* s, int val, 
                                                             BASE_WINDOW* wp,
                                                             const char*hlp)
{ if (h+2  > button_h) button_h = h+2;
  if (w+3 > button_w) button_w = w+3;
  //if (w+16 > button_w) button_w = w+16;
  panel_item p = new_panel_item(Button_Item,s,0,yskip,hlp);
  p->argv = new char*[1];
  p->argv[0] = x_create_bitmap(draw_win,w,h,bits);
  p->argc = 1;
  p->dat1 = (val == -1) ? but_count : val;
  p->ref = wp;
  if (wp) wp->owner_item = p;
  p->width = w;
  but_count++;
  return p->dat1;
 }


int BASE_WINDOW::menu_button(const char* s, int val, BASE_WINDOW* wp, 
                                                     const char* hlp)
{ panel_item p = new_panel_item(Button_Item,s,0,yskip,hlp);
  p->dat1 = (val == -1) ? but_count : val;
  p->dat2 = 1; // mark as menu_button
  p->ref = wp;
  if (wp) wp->owner_item = p;
  x_set_bold_font(draw_win);
  int w = x_text_width(draw_win,s) + 10;
  x_set_text_font(draw_win);
  if (w  > button_w) button_w = w;
  p->width = w;
  has_menu_bar = 1;
  but_count++;
  return p->dat1;
 }



//------------------------------------------------------------------------
// drawing buttons & items
//------------------------------------------------------------------------

void BASE_WINDOW::draw_label_str(int x, int y, const char* str, int enabled,
                                                                int center)
{ x_set_bold_font(draw_win);
  if (!enabled)
  { x_set_color(draw_win,white);
    x_set_text_font(draw_win);
    if (center)
      x_ctext(draw_win,x+1,y+1,str);
    else
      x_text(draw_win,x+1,y+1,str);
   }
  x_set_color(draw_win,enabled ? black : disable_color);
  if (center)
    x_ctext(draw_win,x,y,str);
  else
    x_text(draw_win,x,y,str);
  x_set_text_font(draw_win);
}


void BASE_WINDOW::draw_label(panel_item it)
{ int x = it->xcoord;
  int y = it->ycoord;
  x_set_bold_font(draw_win);
  int txtw = x_text_width(draw_win,it->label_str);
  int txth = x_text_height(draw_win,it->label_str);
  x_set_color(draw_win,panel_bg_color); 
  //x_box(draw_win,x,y,x+label_w,y+yskip);
  x_box(draw_win,x,y,x+txtw,y+yskip);
  x_set_color(draw_win,it->enabled ? black : disable_color);
  int dy = (yskip-txth)/2;
  x_text(draw_win,x,y+dy,it->label_str);
  x_set_text_font(draw_win);
}


void BASE_WINDOW::draw_box_with_shadow(int x1,int y1,int x2,int y2,int c,int w)
{ // below and right of box(x1,y1,x2,y2)
  x_set_color(draw_win,shadow_color);
  x_box(draw_win,x1+3,y1+3,x2+w,y2+w);
  x_set_color(draw_win,c);
  x_box(draw_win,x1,y1,x2,y2);
  x_set_color(draw_win,black);
  x_rect(draw_win,x1,y1,x2,y2);
 }


void BASE_WINDOW::draw_d3_box(int x1,int y1,int x2,int y2, int pressed, 
                                                           int enabled)
{

  if (pressed && enabled) 
     { //x2++;y2++;
       x_set_color(draw_win,press_color);
       x_box(draw_win,x1,y1,x2,y2);
      }
  else
     { x_set_color(draw_win,panel_bg_color);
       //x_box(draw_win,x1,y1,x2+3,y2+3);
       x_box(draw_win,x1,y1,x2,y2);
      }

  int c1 = (pressed) ? black : white;
  int c2 = (pressed) ? shadow_color : panel_bg_color;
  int c3 = (pressed) ? white : shadow_color;
  int c4 = (pressed) ? press_color : black;

  if (!enabled) 
  { //c1 = white;
    c1 = panel_bg_color;
    c2 = panel_bg_color;
    //c3 = white;
    c3 = disable_color;
    c4 = disable_color;
   }

  x_set_color(draw_win,c1);
  x_line(draw_win,x1,y1,x2+(pressed?2:1),y1);		// outer top corner
  x_line(draw_win,x1,y1+1,x1,y2+(pressed?2:1));		// outer left corner

  x_set_color(draw_win,c2);
  x_line(draw_win,x1+1,y1+1,x2+(pressed?1:0),y1+1);	// inner top
  x_line(draw_win,x1+1,y1+2,x1+1,y2+(pressed?1:0));	// inner left

  x_set_color(draw_win,c3);
  x_line(draw_win,x1+(pressed?1:0),y2+1,x2+1,y2+1);	// outer bottom 
  x_line(draw_win,x2+1,y1+(pressed?1:0),x2+1,y2+2);	// outer right

  x_set_color(draw_win,c4);
  x_line(draw_win,x1+(pressed?2:1),y2,x2,y2);		// inner bottom
  x_line(draw_win,x2,y1+(pressed?2:1),x2,y2+1); 	// inner right

}

int BASE_WINDOW::draw_text_item(panel_item it, int win)
{ 
  x_set_color(draw_win,it->dat2);

  if (it->dat1 & 1) x_set_bold_font(draw_win);

  int max_x = panel_width - xoff;

  int   y = it->ycoord;
  int   i = 0;

  while (i < it->argc)
  { int j = i;
    int x = xoff;
    int w = x_text_width(draw_win,it->argv[i]);

    if (x+w > max_x) i++;

    while(x+w <= max_x)
    { x += w;
      if (++i >= it->argc) break;
      w = x_text_width(draw_win,it->argv[i]);
     }

    int d = 0;
    int r = 0;

    if (i < it->argc && i-j > 1)
    { d = (max_x-x)/(i-j-1);
      r = (max_x-x)%(i-j-1);
     }

 
    x = xoff;

    for(int k=j; k < i; k++)
    { char* s = it->argv[k];
      if (win) x_text(win,x,y,s); 
      x += x_text_width(draw_win,s);
      x += d;
      if (r-- > 0) x++;
     }
    y += ytskip;
   }

  if (it->dat1 == 1) x_set_text_font(draw_win);

  x_set_color(draw_win,black);

  return y - it->ycoord + 2;

}


void BASE_WINDOW::draw_string_item(panel_item it, const char* s)
{ 

  if (it->kind == String_Item || it->kind == String_Menu_Item)  
      it->data_str = access_str(it->ref);

  int active = it == act_str_item;

  int  yt = it->ycoord + (yskip-th)/2;
  draw_label(it);

  int x1 = it->xcoord + label_w;
  int y1 = it->ycoord + (yskip-string_h)/2;
  int x2 = x1 + string_w;
  int y2 = y1 + string_h;
  
  if (s == 0)  s = it->data_str;

  if (it->kind != String_Menu_Item)
  { int xr = x1-10;
    int yr = it->ycoord+yskip/2;

    if (it->dat1 > 0)
      draw_left_menu_button(xr,yr,-1,it->enabled);
    else
     { x_set_color(draw_win,panel_bg_color);
       x_box(draw_win,xr-8,yr-8,xr+8,yr+8);
      }

    xr = x2+10;

    if (strlen(s) > it->dat2)
      draw_right_menu_button(xr,yr,-1,it->enabled);
    else
     { x_set_color(draw_win,panel_bg_color);
       x_box(draw_win,xr-8,yr-8,xr+8,yr+8);
      }
   }

  draw_d3_box(x1,y1,x2,y2,active,it->enabled);


  // write text into box

  while (it->offset > it->dat2)
  { it->dat1++;
    it->dat2++;
   }

  s += it->dat1;

  int xtoff = active ? 5 : 4;
  int ytoff = active ? 1 : 0;


  x_set_fixed_font(draw_win);
  x_set_color(draw_win, black);

  if (!it->enabled)
  { x_set_color(draw_win,white);
    x_text(draw_win,x1+xtoff+1,yt+ytoff+1,s,it->dat2-it->dat1);
    x_set_color(draw_win, disable_color);
   }
  x_text(draw_win,x1+xtoff,yt+ytoff,s,it->dat2-it->dat1);

  x_set_text_font(draw_win);

  if (active)
  { // draw cursor
    int xc = x1 + xtoff + tw*(it->offset - it->dat1);
    x_line(draw_win,xc-4,y2-0,xc-4,y2+1);
    x_line(draw_win,xc-3,y2-1,xc-3,y2+1);
    x_line(draw_win,xc-2,y2-2,xc-2,y2+1);
    x_line(draw_win,xc-1,y2-3,xc-1,y2+1);
    x_line(draw_win,xc+0,y2-4,xc+0,y2+1);
    x_line(draw_win,xc+1,y2-3,xc+1,y2+1);
    x_line(draw_win,xc+2,y2-2,xc+2,y2+1);
    x_line(draw_win,xc+3,y2-1,xc+3,y2+1);
    x_line(draw_win,xc+4,y2-0,xc+4,y2+1);
   }
}



void BASE_WINDOW::activate_string_item(panel_item it, int x)
{ 
  if (!it->enabled) return;

  int sl = strlen(it->data_str);
  int x0 = it->xcoord + label_w;
  int  j = it->dat1 + (x-x0)/tw;

  if (x > x0 && j <= it->dat2)
  { if (j > sl) j = sl;
    it->offset = j;
   }

  if (act_str_item != it)
  { panel_item it0 = act_str_item;
    act_str_item = it;
    if (it0->kind == Int_Item)
        { delete[] it0->data_str;
          it0->data_str = make_string(*(int*)it0->ref);
         }
    if (it0->kind == Float_Item)
        { delete[] it0->data_str;
          it0->data_str = make_string(*(double*)it0->ref);
         }
    draw_string_item(it0);
   }

  draw_string_item(it);
}
   
   

void BASE_WINDOW::panel_text_edit(panel_item it)
{ 
  char  str[128];
  strcpy(str,it->data_str);

  int k,val;
  int xc,yc;
  int w;
  unsigned long t;

  do k = x_get_next_event(&w,&val,&xc,&yc,&t);
  while (w != draw_win || (k != key_press_event && k != button_press_event));

  if (k == button_press_event) 
  { x_put_back_event();
    return;
   }

  if (val == KEY_RETURN || val == KEY_UP || val == KEY_DOWN)
  { if (it->kind == String_Menu_Item && it->str_action)
    { clipping(2);
      x_set_text_font(draw_win);
      call_window = this;
      it->str_action(str);
      clipping(1);
     }
    x_put_back_event();
    return;
   }


  if (it->kind != String_Item && it->kind != String_Menu_Item) 
   delete[] it->data_str;


  char c    = (char)val;
  int  j    = it->offset;
  int  strl = strlen(str);

  if (isprint(c))
  { for(int i=strl; i>=j; i--) str[i+1] = str[i];
    str[j]=c;
    j++;
    if (j > it->dat2)
    { it->dat1++;
      it->dat2++;
     }
   }
  else
   switch (c) {

   case KEY_BACKSPACE:    
                   if (j > 0) 
                   { for(int i=j; i<=strl; i++) str[i-1] = str[i];
                     if (--j < it->dat1)
                     { it->dat1--;
                       it->dat2--;
                      }
                    }
                   break;

   case KEY_LEFT:  if (j == 0) break;
                   if (--j < it->dat1) 
                   { it->dat1--;
                     it->dat2--;
                   }
                   break;

   case KEY_RIGHT: if (j == strl) break;
                   if (++j > it->dat2)
                   { it->dat1++;
                     it->dat2++;
                   }
                   break;

   case KEY_HOME:  j = 0;
                   it->dat2 -= it->dat1;
                   it->dat1 = 0;
                   break;

   case KEY_END:   j = strl;
                   if (it->dat2 < j) 
                   { it->dat1 += (j-it->dat2);
                     it->dat2 = j;
                   }
                   break;
   }

 it->offset = j;
 draw_string_item(it,str);

 it->data_str = string_dup(str);

 if (it->str_action == 0) return; 

 if (it->kind == String_Item && (isprint(c) || c == KEY_BACKSPACE))
 { clipping(2);
   x_set_text_font(draw_win);
   call_window = this;
   it->str_action(str);
   clipping(1);
  }
}



void BASE_WINDOW::draw_choice_item(panel_item it, int val)
{ draw_label(it);

  int x = it->xcoord + label_w;
  int y = it->ycoord + (yskip-choice_h)/2;
  int n = it->argc;
  int c;

  if (it->kind == Choice_Mult_Item) 
     //c = *(int*)it->ref;
     c = val;
  else
     //c = (*(int*)it->ref - it->offset)/it->dat2;
     c = (val - it->offset)/it->dat2;


  for(int j=0; j<n; j++)
  { bool pressed = (j == c); 

    if (it->kind == Choice_Mult_Item) pressed = ((c&(1 << j)) != 0);

    draw_d3_box(x,y,x+choice_w-3,y+choice_h,pressed,it->enabled);
    int xx = x + choice_w/2 - (pressed ? 1 : 2);
    int yy = y + choice_h/2 - (pressed ? 0 : 1);

    x_set_color(draw_win,black);

    if (!it->enabled)
    { x_set_color(draw_win,white);
      x_ctext(draw_win,xx+1,yy+1,it->argv[j]);
      x_set_color(draw_win,disable_color);
     }
    x_ctext(draw_win,xx,yy,it->argv[j]);

    x += choice_w;
   }

}




void BASE_WINDOW::draw_bitmap_choice_item(panel_item it) 
{
  draw_label(it);

  int n = it->argc;
  int w = it->dat1;
  int h = it->dat2;
  int x = it->xcoord + label_w;
  int y = it->ycoord + (yskip - it->height)/2;
  int c = *(int*)it->ref;

  for(int j=0; j<n; j++) 
  { bool pressed = (j == c); 
    if (it->kind == Bitmap_Choice_Mult_Item) pressed = ((c&(1 << j)) != 0);
    draw_d3_box(x,y+1,x+w,y+h,pressed,it->enabled);
    if (it->enabled)
       x_set_color(draw_win,pressed ? bitmap_color1 : bitmap_color0);
    else
       x_set_color(draw_win, disable_color);
    int dx = pressed ? 3 : 2;
    int dy = pressed ? 1 : 2;
    x_insert_bitmap(draw_win,x+dx,y+h-dy,it->argv[j]);
    x += (w+3);
   }
}



void BASE_WINDOW::change_bitmap_choice_item(panel_item it, int j)
{

  int old = *(int*)it->ref;

  int n = it->argc;
  int w = it->dat1;
  int h = it->dat2;
  int y = it->ycoord + (yskip - it->height)/2;

  int c = old;

  if (it->kind == Bitmap_Choice_Mult_Item)
    c ^= (1 << j);
  else
    c = j;

  if (c == old) return;


  int x = it->xcoord + label_w + j*(w+3);

  if (it->kind == Bitmap_Choice_Mult_Item)
  { int pressed =  (c & (1<<j));
    draw_d3_box(x,y+1,x+w,y+h,pressed,it->enabled);
    if (it->enabled)
       x_set_color(draw_win,pressed ? bitmap_color1 : bitmap_color0);
    else
       x_set_color(draw_win, disable_color);
    if (pressed)
       x_insert_bitmap(draw_win,x+3,y+h-1,it->argv[j]);
    else
       x_insert_bitmap(draw_win,x+2,y+h-2,it->argv[j]);
   }
  else
  { draw_d3_box(x,y+1,x+w,y+h,1,it->enabled);
    x_set_color(draw_win,it->enabled ? bitmap_color1 : disable_color);
    x_insert_bitmap(draw_win,x+3,y+h-1,it->argv[j]);

    if (old >= 0 && old <= n)
    { int x = it->xcoord + label_w + old * (w+3);
      draw_d3_box(x,y+1,x+w,y+h,0,it->enabled);
      x_set_color(draw_win,it->enabled ? bitmap_color0 : disable_color);
      x_insert_bitmap(draw_win,x+2,y+h-2,it->argv[old]);
     }
  }
}



/*
void BASE_WINDOW::draw_color_button(int xt, int yt, int w, int col, int pressed)
{ 
  int x1  = xt - w;
  int y1  = yt - w;
  int x2  = xt + w;
  int y2  = yt + w;

  int save = press_color;
  press_color = col;
  draw_d3_box(x1,y1,x2,y2,pressed,it->enabled);
  press_color = save;

  char col_str[8];
  sprintf(col_str,"%d",col);

  if (!pressed)
  { x_set_color(draw_win,(col==panel_bg_color) ? black : col);
    x_ctext(draw_win,xt,yt,col_str);
   }
  else
  { x_set_color(draw_win,text_color(col));
    x_ctext(draw_win,xt+1,yt+1,col_str);
   }
  x_set_color(draw_win,black);
}
*/



void BASE_WINDOW::draw_color_button(int xt, int yt, int w, int col, int pressed,
                                                                    int enabled)
{ 
  int x1  = xt - w;
  int y1  = yt - w;
  int x2  = xt + w;
  int y2  = yt + w;

  int save = press_color;
  press_color = col;
  draw_d3_box(x1,y1,x2,y2,pressed,enabled);
  press_color = save;

  x_set_color(draw_win, enabled ? col : disable_color);

  if (!pressed || !enabled)
    x_box(draw_win,xt-2,yt-2,xt+2,yt+2);

  x_set_color(draw_win,black);
}



void BASE_WINDOW::draw_color_item(panel_item it)
{ draw_label(it);

  int yt = it->ycoord + yskip/2;
  int xt = it->xcoord + label_w + color_h/2;
  char c = *(int*)it->ref;

  x_set_color(draw_win,panel_bg_color);

  for(int j=0; j < 16; j++)
  { draw_color_button(xt,yt,color_h/2,j,j==c,it->enabled);
    xt += (color_h+2);
   }
}


void BASE_WINDOW::change_color_item(panel_item it, int j)
{ int yt = it->ycoord + yskip/2;
  int xt = it->xcoord + label_w + color_h/2;
  int c = *(int*)it->ref;

  if (j == c) return; 

  *(int*)it->ref = j;

  draw_color_button(xt + j*(color_h+2),yt,color_h/2,j,1,it->enabled);

  if (c >= 0 && c <= 15) 
    draw_color_button(xt + c*(color_h+2),yt,color_h/2,c,0,it->enabled);
}


void BASE_WINDOW::draw_bool_item(panel_item it)
{ draw_label(it);
  int yt = it->ycoord + yskip/2;
  int xt = it->xcoord + label_w + color_h/2;
  int c  = *(bool*)it->ref;
  int w  = color_h/2;
  draw_d3_box(xt-w,yt-w,xt+w,yt+w,c,it->enabled);
}



void BASE_WINDOW::draw_slider_item(panel_item it, int x)
{ 
  int x0 = it->xcoord + label_w;
  int y0 = it->ycoord + (yskip-slider_h)/2;

  int x1 = x0 + slider_w;
  int y1 = y0 + slider_h;

  int first_time = 0;

  int mi = it->dat1;
  int ma = it->dat2;
  float d = (ma > mi) ? float(slider_w)/(ma-mi+1) : 1;


  int val0 = *(int*)it->ref;

  bool undef = false;

  if (x == 0) // first time
  { first_time = 1;
    draw_label(it);
    x = x0  + (int)(d * (val0 - mi + 0.5)) + 1;
    it->offset = x;
    if (val0 < mi || val0 > ma) undef = true;
   }

  if (x < x0) x = x0;
  if (x > x1) x = x1;

  int val  = mi + int((x-x0-d/2)/d);


  char text[16];

  if (undef)
    sprintf(text,"   ?");
  else
   { *(int*)it->ref = val;
     sprintf(text,"%4d",val);
    }

  int yt = y0 + (slider_h-x_text_height(draw_win,"0"))/2 + 1;
  x_set_color(draw_win,panel_bg_color);
  x_box(draw_win,x0-sl_num_w,y0,x0-1,y1);
  x_set_color(draw_win,it->enabled ? black : disable_color);
  x_set_fixed_font(draw_win);
  x_text(draw_win,x0-sl_num_w,yt,text);
  x_set_text_font(draw_win);

  // slider

  if (first_time || x < it->offset) 
     draw_d3_box(int(x+0.5),y0,x1+1,y1,1,it->enabled);

  if (first_time || x > it->offset) 
     draw_d3_box(x0,y0,int(x+0.5)+1,y1,0,it->enabled);

  it->offset = x;

  x_set_color(draw_win,black);

  if (first_time == 0)
    if (it->action && val != val0) 
    { clipping(2);
      call_window = this;
      it->action(val);
      clipping(1);
     }

}


void BASE_WINDOW::draw_button(panel_item it, int pressed)
{ 
   if ( it->dat2 != 0 ) 
   { draw_menu_button(it,pressed);
     return;
    }

   if (it->menu_but == 0 && menu_button_style == 2) 
   { draw_menu_item(it,pressed);
     return;
    }

   char* s = it->label_str;
   int enab = it->enabled;

   int x1 = it->xcoord;
   int y1 = it->ycoord + (yskip-it->height)/2;
   int x2 = x1 + it->width;
   int y2 = y1 + it->height;
   int xt = x1 + it->width/2;
   int yt = y1 + it->height/2;

   //draw_d3_box(x1,y1,x2,y2,pressed,enab);
   draw_d3_box(x1,y1,x2,y2,pressed);

   x_set_bold_font(draw_win);
   x_set_color(draw_win, enab ? black : disable_color);

   if (it->argc == 0) // insert text
     { int ch = x_text_height(draw_win,"H");
       if (center_button_label)
          if (pressed)
             x_ctext(draw_win,xt+1,yt+1,s);
          else
             x_ctext(draw_win,xt,yt,s);
       else
          if (pressed)
             x_text(draw_win,x1+ch/2+1,yt-ch/2+1,s);
          else
             x_text(draw_win,x1+ch/2,yt-ch/2,s);

       if (it->ref) 
       { int d = (y2-y1)/2;
         if (pressed) d++;
         if (buts_per_line == 1 && item_count == but_count) // in menu
            draw_right_menu_button(x2-d, y1+d,-1,enab);
         else
            draw_down_menu_button(x1+d, y1+d,-1,enab);
        }

     }
   else // bitmap
     if (pressed)
       x_insert_bitmap(draw_win,x1+3,y2-1,it->argv[0]);
     else
       x_insert_bitmap(draw_win,x1+2,y2-2,it->argv[0]);

 x_set_text_font(draw_win);
 x_set_color(draw_win,black);
 x_flush_display();
}


void BASE_WINDOW::draw_menu_button(panel_item it, int pressed)
{ 
   char* s = it->label_str;

   int x1 = it->xcoord;
   int y1 = it->ycoord + (yskip-it->height)/2;
   int x2 = x1 + it->width;
   int y2 = y1 + it->height+1;
   int xt = x1 + it->width/2;
   int yt = y1 + it->height/2;

   if (!pressed) 
     { x_set_color(draw_win,panel_bg_color);
       x_box(draw_win,x1-1,y1-1,x2+1,y2+1);
      }
   else
     draw_d3_box(x1,y1,x2,y2,0,it->enabled);

    x_set_color(draw_win,it->enabled ? black : disable_color);

    // insert text
    x_set_bold_font(draw_win);

    if (pressed)
       x_ctext(draw_win,xt,yt,s);
    else
       x_ctext(draw_win,xt+1,yt+1,s);

    x_set_text_font(draw_win);

   x_flush_display();
}


void BASE_WINDOW::draw_menu_item(panel_item it, int pressed)
{ 
   char* s = it->label_str;

   int x1 = it->xcoord;
   int y1 = it->ycoord + (yskip-it->height)/2;
   int x2 = x1 + it->width;
   int y2 = y1 + it->height+1;
   //int xt = x1 + 5;
   int xt = x1 + button_h + 8;

   int col =  (pressed) ? press_color : white; 
   x_set_color(draw_win,col);
   x_box(draw_win,x1,y1,x2,y2);
   x_set_color(draw_win,black);
   x_rect(draw_win,x1,y1,x2,y2);

/*
   if (pressed)
      draw_d3_box(x1,y1,x2,y2,0,it->enabled);
   else
    { x_set_color(draw_win,panel_bg_color);
      x_box(draw_win,x1,y1,x2,y2);
     }
*/

   int yt = y1 + (it->height-x_text_height(draw_win,"H"))/2;
   x_set_color(draw_win,text_color(col));
   x_text(draw_win,xt,yt,s);
   x_set_color(draw_win,black);

   x_flush_display();
}



void BASE_WINDOW::draw_down_menu_button(int x, int y, int pressed, int enabled)
{ 
  if (pressed >= 0) 
    draw_d3_box(x-9,y-10,x+9,y+10,pressed,enabled);

  int x1 = x-4;
  int y1 = y-3;
  int x2 = x+4;
  int y2 = y-3;
  int x3 = x;
  int y3 = y+4;

  x_set_color(draw_win,grey2);

  for(int i=1; i<7; i++)
     x_line(draw_win,x1+i,y1+i,x2-i,y2+i);

  color c1 = black;
  color c2 = white;
  if (!enabled) c1 = c2 = disable_color;

  x_set_color(draw_win,c1);
  x_line(draw_win,x1,y1,x2,y2);
  x_line(draw_win,x1+1,y1+1,x3,y3);

  x_set_color(draw_win,c2);
  x_line(draw_win,x2,y2,x3,y3);

  x_set_color(draw_win,black);
  x_flush_display();
}


void BASE_WINDOW::draw_right_menu_button(int x, int y, int pressed, int enabled)
{ 
  if (pressed >= 0) 
    draw_d3_box(x-9,y-10,x+9,y+10,pressed,enabled);

  int y1 = y-4;
  int x1 = x-3;
  int y2 = y+4;
  int x2 = x-3;
  int y3 = y;
  int x3 = x+4;

  x_set_color(draw_win,grey2);

  for(int i=1; i<7; i++)
     x_line(draw_win,x1+i,y1+i,x2+i,y2-i);

  color c1 = black;
  color c2 = white;
  if (!enabled) c1 = c2 = disable_color;

  x_set_color(draw_win,c1);
  x_line(draw_win,x1,y1,x2,y2);
  x_line(draw_win,x1+1,y1+1,x3,y3);

  x_set_color(draw_win,c2);
  x_line(draw_win,x2+1,y2-1,x3+1,y3);

  x_set_color(draw_win,black);
  x_flush_display();
}


void BASE_WINDOW::draw_left_menu_button(int x, int y, int pressed, int enabled)
{ 
  if (pressed >= 0) 
    draw_d3_box(x-9,y-10,x+9,y+10,pressed,enabled);

  int y1 = y-4;
  int x1 = x+3;
  int y2 = y+4;
  int x2 = x+3;
  int y3 = y;
  int x3 = x-4;

  x_set_color(draw_win,grey2);

  for(int i=1; i<7; i++)
     x_line(draw_win,x1-i,y1+i,x2-i,y2-i);

  color c1 = black;
  color c2 = white;
  if (!enabled) c1 = c2 = disable_color;

  x_set_color(draw_win,c1);
  x_line(draw_win,x1-1,y1+1,x3,y3);
  x_line(draw_win,x2-1,y2-1,x3-1,y3);

  x_set_color(draw_win,c2);
  x_line(draw_win,x1,y1,x2,y2);

  x_set_color(draw_win,black);
  x_flush_display();
}



void BASE_WINDOW::make_menu_bar()
{ // collects all buttons inserted so far into a menu bar
  for(int i=0; i<item_count; i++)
  { panel_item it = Item[i];
    if (it->kind == Button_Item) 
    { it->dat2 = 1; // goes into menu bar
      x_set_bold_font(draw_win);
      it->width = x_text_width(draw_win,it->label_str) + 10;
      x_set_text_font(draw_win);
      has_menu_bar = 1;
     }
   }
}


void BASE_WINDOW::place_panel_items()
{
  bool is_menu = (p_win && item_count == but_count);

  // adjust panel width

  if (is_menu)  // menu
    { xoff  = 0;
      yoff  = 0;
      yskip = button_h+2;
      panel_width = buts_per_line*(button_w+1) + 1;
      center_button_label = 0;
      if (menu_button_style == 2) 
      { //panel_width += button_h;
        panel_width--;
        yskip = button_h+1;
       }
     }
  else
  { if (yskip < button_h+2) yskip = button_h+2;
    int w = x_text_width(draw_win,default_frame_label);
    if (panel_width == -1 && item_count == but_count) // only buttons
    { if (w > 300) w = 300;
      if (w > panel_width) panel_width = w;
     }
    //else if (panel_width < 0) panel_width = 50;

    if (has_menu_bar) xoff = 3;
   }

  if (buts_per_line > but_count) buts_per_line = but_count;


  // set width for all items and adjust panel width

  int max_w = 0;
  int max_choice_width = 0;
  panel_item max_choice_it = 0;

  int non_button_items = 0;

  int i;

  for(i = 0; i<item_count; i++)
  { panel_item it = Item[i];
    if (it->kind == Button_Item && it->dat2) continue; // menu bar

    switch (it->kind)
    {
      case  Button_Item: 
             if (it->menu_but != it) 
                it->width  = button_w;
             break;

      case  Text_Item:
             { it->width = 0;
               for(int j=0; j<it->argc; j++) 
                   it->width += x_text_width(draw_win,it->argv[j]);
               if (it->width > 400) it->width =  400;
               non_button_items = 1;
               break;
              }

      case Bool_Item:
             it->width = label_w + color_h;
             non_button_items = 1;
             break;

      case Choice_Item:
      case Choice_Mult_Item: 
           { int w = choice_w*it->argc - 3;
             if (w > max_choice_width) 
             { max_choice_it = it;
               max_choice_width = w; 
              }
             it->width = label_w + w;
             non_button_items = 1;
             break;
            }

      case Color_Item:
             it->width = label_w + 16*(color_h+2)-2;
             non_button_items = 1;
             break;

      case Bitmap_Choice_Item:
      case Bitmap_Choice_Mult_Item:
             it->width = label_w + it->argc * (it->dat1+3) - 3;
             non_button_items = 1;
             break;

      case Slider_Item:
             it->width = label_w + slider_w;
             non_button_items = 1;
             break;

      case String_Item:
      case String_Menu_Item:
             it->width = label_w + string_w;
             non_button_items = 1;
             break;

      case Float_Item:
      case Int_Item:
             it->width = label_w + number_w;
             non_button_items = 1;
             break;
  
     }


    //if (it->kind == Text_Item || it->kind == Button_Item) continue; 

    int w = it->width;
    if (it->kind == String_Item) w += xoff;

    if (w > max_w) max_w = w;

  }


  int w = max_w + 2*xoff;
  if (w > panel_width) panel_width = w;

  // adjust width of string and slider items

  if (slider_w < max_choice_width && slider_w + choice_w > max_choice_width)
  { slider_w = max_choice_width;
    string_w = max_choice_width;
    number_w = max_choice_width;
   }

  for(i = 0; i<item_count; i++)
  { panel_item it = Item[i];
    switch (it->kind) {
      case String_Item:
      case String_Menu_Item:
              it->dat2 = string_w/tw - 1;
      case Slider_Item:
      case Float_Item:
      case Int_Item:
              it->width = label_w + string_w;
              break;
      }
   }


  if (slider_w > max_choice_width && slider_w - choice_w < max_choice_width)
  { int dw = (slider_w - max_choice_width)/max_choice_it->argc;
    choice_w += dw;
    for(i = 0; i<item_count; i++)
    { panel_item it = Item[i];
      switch (it->kind) {
        case Choice_Item:
        case Choice_Mult_Item:
                it->width = label_w + choice_w*it->argc - 3;
                break;
      
       }
     }
   }


  //  button layout

  int bxoff = xoff;  // left and right button boundary space
  int bskip = th/2;  // horizontal space between buttons

  if ( !is_menu)
    { int bw = button_w + bskip;

      int w = but_count*bw+2*xoff-bskip;
      if (buts_per_line == 0 && non_button_items && w < panel_width) 
          buts_per_line = but_count;

      if (buts_per_line) // increase panel width if necessary 
      { w = buts_per_line*bw+2*xoff-bskip;
        if (w > panel_width) panel_width = w;
        // center buttons
        bxoff = (panel_width - (buts_per_line * bw - bskip))/2;
       }
     }
  else
    bskip = 1;



  // place items

  int cur_ycoord = yoff/2;
  int cur_xcoord = xoff;
  int cur_skip = 0;

  // menu bar

  if (has_menu_bar)
  { 
    int x = cur_xcoord+3;
    int y = cur_ycoord;

    // menu buttons
    for(i=0; i<item_count; i++)
    { panel_item it = Item[i];
      if (it->kind == Button_Item && it->dat2 && it->ref)
      { it->height = button_h;
        it->xcoord = x;
        it->ycoord = y;
        x += (it->width + bskip);
      }
     }

    x = panel_width;

    // non-menu buttons
    for(i=item_count-1; i>=0; i--)
    { panel_item it = Item[i];
      if (it->kind == Button_Item && it->dat2 && it->ref==0)
      { x -= (it->width + bskip);
        it->height = button_h;
        it->xcoord = x;
        it->ycoord = y;
      }

    }

  }

  if (has_menu_bar == 0) cur_ycoord += yoff;

  cur_xcoord = panel_width+1;

  for(i = 0; i<item_count; i++)
  { panel_item it = Item[i];

    if (it->kind == Button_Item)  continue;
    if (it->kind == Space_Item)   continue;
    if (it->kind == Fill_Item)    continue;
    if (it->kind == Newline_Item) continue;


    if ( it ->kind == Text_Item ||
        (cur_xcoord > xoff && cur_xcoord + it->width > panel_width) )
    { 
      cur_xcoord = xoff;
      cur_ycoord += cur_skip;

      switch (it->kind) { 
        case Bitmap_Choice_Item:
        case Bitmap_Choice_Mult_Item:
              cur_ycoord += (it->dat2 - yskip)/2;
              cur_skip =  (yskip + it->dat2)/2;
              if (cur_skip < yskip) cur_skip = yskip;
              break;
        case Text_Item: 
              if (cur_skip == yskip) cur_ycoord += th/2;
              cur_skip = draw_text_item(it,0);
              break;
        default: 
              cur_skip = yskip;
              break;
        }
     }

    it->xcoord = cur_xcoord;
    it->ycoord = cur_ycoord;

    if (it->kind == Bool_Item)
      cur_xcoord += it->width + 2*xoff;
    else
      cur_xcoord += max_w+4*xoff;

    if (it->menu_but)
    { panel_item p = it->menu_but;
      p->xcoord = it->xcoord + label_w - p->width - 4;
      p->ycoord = it->ycoord;
     }
   }

  cur_ycoord += cur_skip;


  // place buttons

  int x = cur_xcoord;
  int y = cur_ycoord;

  if (is_menu)
  { x = 0;
    y = 0;
   }

  if (non_button_items) y += yoff;

  if (has_menu_bar == 0)
  { y -= yskip;
    x = panel_width+1;
   }

  for(i=0; i<item_count; i++)
  { panel_item it = Item[i];

    switch (it->kind) {

     case Newline_Item: x = bxoff;
                        y += yskip;
                        break;

     case Space_Item:   if (it->width > 0) x += it->width;
                        break;


     case Fill_Item:  { if (x > bxoff && x+it->width > panel_width-bxoff) 
                        { x = bxoff;
                          y += yskip;
                         }
                        int x1 = panel_width - button_w - bxoff;
                        if (x1 > x) x = x1;
                        break;
                       }

     case Button_Item: { if (it->menu_but == it || it->dat2) continue;
                         if (x > bxoff && x+it->width > panel_width-bxoff) 
                         { x = bxoff;
                           y += yskip;
                          }
                         it->height = button_h;
                         it->xcoord = x;
                         it->ycoord = y;
                         x += (it->width + bskip);
                         break;
                       }
     }
  }

  y += yskip;

  if (has_menu_bar) 
     y += 5; 
  else
     if (but_count > 0) y += (yoff+1);

//scrolling
//if (menu_button_style == 2 && but_count > 8) y = 8*(button_h+1);

  panel_height = y;


/*
  if (panel_height == 0 || panel_height > y) panel_height = y;

  if (menu_button_style == 2 && Item[item_count-1]->menu_but == 0)
  { button("",-1,(BASE_WINDOW*)0);
    panel_item p = Item[item_count-1];
    p->width  = bxoff-4;
    p->height = p->width;
    p->xcoord = 1;
    p->ycoord = (yskip - p->height)/2 - 2;
    p->menu_but = p;

    button("",-1,(BASE_WINDOW*)0);
    p = Item[item_count-1];
    p->width  = bxoff-4;
    p->height = p->width;
    p->xcoord = 1;
    p->ycoord = panel_height - yskip - (yskip - p->height)/2 + 2;
    p->menu_but = p;
   }
*/

}

void BASE_WINDOW::draw_panel_items()
{ drawing_mode save = x_set_mode(draw_win,src_mode);
  x_set_color(draw_win,panel_bg_color);
  x_box(draw_win,0,0,window_width-1,panel_height);

//if (win_parent == 0 && window_height > panel_height)
//    draw_d3_box(0,0,window_width-2,panel_height-3,0);

  if (has_menu_bar) 
     draw_d3_box(xoff,yoff/2,window_width-xoff,yoff/2+yskip,0);

  redraw_panel();
  x_set_mode(draw_win,save);
}


void BASE_WINDOW::redraw_panel(panel_item x)
{
  // redraw x or (if x==0) all items

  int          save_lw = x_set_line_width(draw_win,1);
  line_style   save_ls = x_set_line_style(draw_win,solid);
  text_mode    save_tm = x_set_text_mode(draw_win,transparent);
  drawing_mode save_mo = x_set_mode(draw_win,src_mode);

  clipping(1);

  x_set_color(draw_win,black);

  for(int i=0; i<item_count; i++)
  { panel_item it=Item[i];

    if (x != 0 && it != x) continue;

    switch (it->kind) {

    case Button_Item:
        { draw_button(it,it == last_sel_button);
          break;
         }

    case Text_Item:
        { draw_text_item(it,draw_win);
          break;
         }

    case Choice_Item:
    case Choice_Mult_Item:
        { draw_choice_item(it,*(int*)it->ref);
          break;
         }

    case Bool_Item:
        { draw_bool_item(it);
          break;
         }

    case Color_Item:
        { draw_color_item(it);
          break;
         }

    case Bitmap_Choice_Item:
    case Bitmap_Choice_Mult_Item:
        { draw_bitmap_choice_item(it);
          break;
         }

    case Slider_Item:
        { draw_slider_item(it,0);
          break;
         }

    case Int_Item:
        { if (act_str_item == 0) act_str_item = it;
          delete[] it->data_str;
          it->data_str = make_string(*(int*)it->ref);
          draw_string_item(it);
          break;
         }
 
    case Float_Item:
        { if (act_str_item == 0) act_str_item = it;
          delete[] it->data_str;
          it->data_str = make_string(*(double*)it->ref);
          draw_string_item(it);
          break;
         }
  
    case String_Item:
        { if (act_str_item == 0) act_str_item = it;
          draw_string_item(it);
          break;
         }

    case String_Menu_Item:
        { if (act_str_item == 0) act_str_item = it;
          draw_string_item(it);
          //int xr = it->xcoord + label_w - 10;
          //draw_down_menu_button(xr,it->ycoord+yskip/2,0,it->enabled);
          break;
         }
   }

 }

 x_set_line_width(draw_win,save_lw);
 x_set_line_style(draw_win,save_ls);
 x_set_mode(draw_win,save_mo);
 x_set_text_mode(draw_win,save_tm);

}


    
void BASE_WINDOW::open_sub_panel(panel_item it)
{ 
  BASE_WINDOW* wp = (BASE_WINDOW*)it->ref;

  wp->last_sel_button = 0;

  if (wp->is_open()) return;

  int x,y;
  BASE_WINDOW* wpp = p_root;;

  wp->p_win  = this;
  wp->p_root = p_root;

  if (this != p_root) 
    { x = window_width + 1;
      y = it->ycoord + 1;
      wp->hotx1 = -window_width;
      wp->hotx2 = 0;
      wp->hoty1 = 0;
      wp->hoty2 = yskip;
      x_set_border_width(wp->draw_win,0);
    }
  else
   { x = it->xcoord; 
     y = it->ycoord + (yskip+it->height)/2 + 2;

     wp->hotx1 = 0;
     wp->hotx2 = it->width;
     wp->hoty1 = it->ycoord - y;
     wp->hoty2 = 0;

     if (it->menu_but == it && wp->buts_per_line == 1)
       { y += 3;
         wp->menu_button_style = 2;
         wp->yskip = 0;
         wp->button_w = string_w + button_h + 3;
        }
     else
        x_set_border_width(wp->draw_win,0);
    }

    x_window_to_screen(draw_win,&x,&y);
    x_screen_to_window(p_root->draw_win,&x,&y);

    wp->place_panel_items();

    if (p_root->window_height < y + wp->panel_height ||
        p_root->window_width  < x + wp->panel_width)
    { 
      wpp = wp;
      x_window_to_screen(p_root->draw_win,&x,&y);

      int dx = x_display_width()  - (x + wp->panel_width);
      int dy = x_display_height() - (y + wp->panel_height);
  
      if (dx < 0) 
      { x += dx;
        wp->hotx1 -= dx;
        wp->hotx2 -= dx;
       }
  
      if (dy < 0)
      { y += dy;
        wp->hoty1 -= dy;
        wp->hoty2 -= dy;
       }
    }

  active_window = wp;

  wp->display(x,y,wpp);

  x_grab_pointer(wp->draw_win);

  { // skip pending events from wpp
    int e,w,v,x,y;
    unsigned long t;
    do e = x_check_next_event(&w, &v, &x, &y, &t);
    while (e != no_event && wp->draw_win != w);
    if (e != no_event) x_put_back_event();
  }

  wp->last_sel_button = 0;
  wp->panel_menu_mode = 1;
}

    
    
void BASE_WINDOW::close_sub_panel(BASE_WINDOW* wp)
{ wp->last_sel_button = 0;
  wp->panel_menu_mode = 0;

  if (wp->is_open())
  { 
    // read all remaining events 
    int e,w,v,x,y;
    unsigned long t;
    while (x_check_next_event(&w, &v, &x, &y, &t) != no_event);

    wp->close();

    wp = wp->p_win;

    if (!x_display_bits_saved()) 
       { while (x_get_next_event(&w, &v, &x, &y, &t) != exposure_event);
         x_put_back_event();
         event_handler(wp,1);
        }
    else
       while ((e=x_check_next_event(&w, &v, &x, &y, &t)) != no_event)
       { if (e == exposure_event)
         { x_put_back_event();
           event_handler(wp,1);
          }
        }
  }
}



int BASE_WINDOW::panel_event_handler(int w, int k, int b, int x, int y, 
                                                         unsigned long t)
{
  //printf("panel event: w = %d  k = %d  b = %d  x = %d  y = %d\n",w,k,b,x,y);

  int          save_lw = x_set_line_width(draw_win,1);
  line_style   save_ls = x_set_line_style(draw_win,solid);
  text_mode    save_tm = x_set_text_mode(draw_win,transparent);
  drawing_mode save_mo = x_set_mode(draw_win,src_mode);
  clipping(1);

  if (w != draw_win) 
  { x = -1;
    y = -1;
   }

  int but = -1;

  panel_item it = 0;

  if (k == key_press_event && item_count == but_count) 
    { int n = item_count-3;

      int dy = 0;
      if (b == KEY_UP   && Item[0]->ycoord < 0) dy =  yskip;
      if (b == KEY_DOWN && Item[n]->ycoord > panel_height) dy = -yskip;

      if (dy != 0)
      { int          save_lw = x_set_line_width(draw_win,1);
        line_style   save_ls = x_set_line_style(draw_win,solid);
        text_mode    save_tm = x_set_text_mode(draw_win,transparent);
        drawing_mode save_mo = x_set_mode(draw_win,src_mode);
        for(int i=0; i<item_count; i++) 
        { panel_item p = Item[i];
          if (p->menu_but == 0) 
          { p->ycoord += dy;
            draw_button(p,0);
           }
         }
        x_set_line_width(draw_win,save_lw);
        x_set_line_style(draw_win,save_ls);
        x_set_mode(draw_win,save_mo);
        x_set_text_mode(draw_win,save_tm);
        return -1;
       }
     }


  if (k == key_press_event && act_str_item != 0) 
     { // input for active string item
        if (b == KEY_RETURN || b == KEY_DOWN || b == KEY_UP)
           { int i = act_str_item->index;
             int ki;
             do { if (b == KEY_UP)
                    { if (--i < 0) i = item_count-1; }
                  else
                    { if (++i >= item_count) i = 0; }
                  ki =  Item[i]->kind; 
                 }
             while (!Item[i]->enabled ||
                    (ki != String_Item && ki != String_Menu_Item && 
                     ki != Int_Item    && ki != Float_Item));
             activate_string_item(Item[i],panel_width);
            }
         else // character
            { it = act_str_item;
              x = it->xcoord + string_w + label_w;
              y = it->ycoord;
              x_put_back_event(); // event will be handled by panel_text_edit
             }
       }
    else
      { int i;
        for(i=0; i<item_count; i++) // search for selected item
        { it = Item[i];
          if (!it->enabled) continue;
          int x1 = it->xcoord;
          int x2 = it->xcoord + it->width + 4;
          int y1 = it->ycoord + (yskip-it->height)/2;
          int y2 = y1 + it->height + 1;
          if (panel_menu_mode) 
          { y1 -= 2;
            y2 += 2;
           }
          if (it->kind != Button_Item) x1 += label_w;
          if (x < x1 || x > x2 || y < y1 || y > y2) continue;
          break;
         }
 
        if (i == item_count)  // no item selected
        { it = 0;
          if (last_sel_button)
          { panel_item lsb = last_sel_button;
            last_sel_button = 0;
            draw_button(lsb,0);
            if (lsb->ref)
            { close_sub_panel((BASE_WINDOW*)lsb->ref);
              active_window = this;
             }
           }
         }

       if (k != button_press_event && !panel_menu_mode) it = 0;
 
       if (k == button_release_event || 
          (k == button_press_event && panel_menu_mode))
       { 
         if (last_sel_button == 0)
           { active_window = this;
             panel_menu_mode = 0; }
         else
           { it = last_sel_button;
             if (it->ref)
               { BASE_WINDOW* wp =  ((BASE_WINDOW*)it->ref);
                 active_window = wp;
                 wp->panel_menu_mode = 0;
                 it = 0;
                }
             else
               { draw_button(it,0);
                 but = it->dat1;
                }
            }
         }

      }


    if (it && b == 2) // display help text
    { if (it->kind == Button_Item)draw_button(it,1);
      BASE_WINDOW hw(-1,-1);
      char buf[256];
      sprintf(buf,"\\bf %s",it->label_str);
      hw.text_item(buf);
      hw.text_item("");
      if (it->help_str)
         hw.text_item(it->help_str);
      else 
         hw.text_item("No help available.");

      hw.place_panel_items();

      hw.set_panel_bg_color(ivory);

      // compute position on root window
      int xpos = x;
      int ypos = y;

      x_window_to_screen(draw_win,&xpos,&ypos);

      int dx = x_display_width() - (xpos + hw.panel_width);
      if (dx < 0) xpos += dx;

      hw.display(xpos+12,ypos+11,&hw);

      { // wait for release event
        int w,v,x,y;
        unsigned long t;
        while (x_get_next_event(&w,&v,&x,&y,&t) != button_release_event);
       }
      if (it->kind == Button_Item)draw_button(it,0);
      it = 0;
     }



    if (but == -1 && it) // item selected
    {
      switch (it->kind) {
  
      case Text_Item: break;
  
      case Slider_Item:
      { int xoff1 = it->xcoord + label_w;
        int w = draw_win;
        int xlast = 0;
        if (x < xoff1 || x > xoff1+slider_w) break;
        //while (w == draw_win)
        for(;;)
        { if (xlast != x)
          { xlast = x;
            draw_slider_item(it,xlast);
           }
          if (x_get_next_event(&w,&b,&x,&b,&t) == button_release_event) break;
         }
        break;
       }
  
      case Int_Item:
      { activate_string_item(it,x);
        panel_text_edit(it);
        *(int*)it->ref = atoi(it->data_str);
        break;
       }
  
      case Float_Item:
      { activate_string_item(it,x);
        panel_text_edit(it);
        *(double*)it->ref = atof(it->data_str);
        break;
       }
  
      case String_Item:
      case String_Menu_Item:
      { activate_string_item(it,x);
        panel_text_edit(it);
        assign_str(it->ref,it->data_str);
        break;
       }
  
     case Choice_Item:
     { int d = (x-it->xcoord-label_w)/choice_w;
       if (d < it->argc)
       { int x = it->offset + d * it->dat2;
         draw_choice_item(it,x);
         if (it->action)
         { clipping(2);
           call_window = this;
           it->action(x);
           clipping(1);
          }
         *(int*)it->ref = x;
        }
       break;
      }

     case Choice_Mult_Item:
     { int d = (x-it->xcoord-label_w)/choice_w;
       if (d < it->argc)
       { int x = *(int*)it->ref;
         x ^= (1 << d);
         draw_choice_item(it,x);
         if (it->action)
         { clipping(2);
           call_window = this;
           it->action(x);
           clipping(1);
          }
         *(int*)it->ref = x;
        }
       break;
      }
  
  
     case Bool_Item:
     { int b = (*(bool*)it->ref) ? 0 : 1;
       if (it->action) 
       { clipping(2);
         call_window = this;
         it->action(b);
         clipping(1);
       }
       *(bool*)it->ref = (bool)b;
       draw_bool_item(it);
       break;
      }
  
     case Color_Item:
     { int d = (x - it->xcoord - label_w)/(color_h+2);
       if (d < 16) 
       { if (it->action) 
         { clipping(2);
           call_window = this;
           it->action(d);
           clipping(1);
         }
        change_color_item(it,d);
       }
       break;
      }
  
     case Bitmap_Choice_Mult_Item:
     case Bitmap_Choice_Item:
     { int d = (x - it->xcoord - label_w)/(it->dat1 + 3);
       if (d < it->argc) 
       { int c = d;
         if (it->kind == Bitmap_Choice_Mult_Item) 
            c = (*(int*)it->ref) ^ (1 << d);
         if (c != *(int*)it->ref) change_bitmap_choice_item(it,d);
         if (it->action) 
         { clipping(2);
           call_window = this;
           it->action(c);
           clipping(1);
         }
         *(int*)it->ref = c;
        }
       break;
     }

     case Button_Item:
     { 
       panel_item lsb = last_sel_button;

       if (it != lsb) 
       { draw_button(it,1);
         last_sel_button = it;
        }
       else  // second click on active button
         if (k == button_press_event) last_sel_button = 0;
    
       if (lsb && (it != lsb || k==button_press_event))
       { draw_button(lsb,0);
         if (lsb->ref)
         { close_sub_panel((BASE_WINDOW*)lsb->ref);
           active_window = this;
          }
        }

       if (it != lsb && it->ref) open_sub_panel(it);

       it = 0;
       break;
     }

    }
  }

  int result = but;

  if (it  && it->kind == Button_Item  && it->ref == 0 ) 
  { BASE_WINDOW* wp = this;
    while(wp->p_win) 
    { BASE_WINDOW* wpp = wp->p_win;
      close_sub_panel(wp);
      wp->p_win = 0;
      wp = wpp;
     }
    active_window = wp;

    panel_item lsb = wp->last_sel_button;

    if (lsb)
    { drawing_mode save_mo = x_set_mode(wp->draw_win,src_mode);
      wp->clipping(1);
      wp->draw_button(lsb,0);
      x_set_mode(wp->draw_win,save_mo);
      wp->clipping(2);
      wp->last_sel_button = 0;
      if (lsb->dat1 > -1) result = lsb->dat1;
     }

     if (lsb == 0 || lsb->menu_but == lsb) result = -1;

   }

  x_set_line_width(draw_win,save_lw);
  x_set_line_style(draw_win,save_ls);
  x_set_mode(draw_win,save_mo);
  x_set_text_mode(draw_win,save_tm);
  clipping(2);

 if (it && it->kind == Button_Item && it->action)
 { call_window = this;
   it->action(but);
  }

 return result;

}

BASE_WINDOW* BASE_WINDOW::get_window(const char* s)
{
  for(int i = 0; i<item_count; i++)
    {
      panel_item p = Item[i];
      if( p->kind == Button_Item && p->ref && 
	  strlen(p->label_str) == strlen(s) &&
	  !strcmp(p->label_str, s) )
	return (BASE_WINDOW*)p->ref;
    }
  return 0;
}

BASE_WINDOW* BASE_WINDOW::set_window(const char* s, BASE_WINDOW* wp)
{
  BASE_WINDOW* ret = 0;
  
  for(int i = 0; i<item_count; i++)
    {
      panel_item p = Item[i];
      if( p->kind == Button_Item && p->ref && 
	  strlen(p->label_str) == strlen(s) &&
	  !strcmp(p->label_str, s) )
	{ 
	  ret =  (BASE_WINDOW*)p->ref;
	  p->ref = wp;
          if(wp) wp->owner_item = p;
          ret->owner_item = 0;
	  return ret;
	}
    }
  return 0;
}


void BASE_WINDOW::enable_item(panel_item it) 
{ if (!it->enabled)
  { it->enabled = 1; 
    if (is_open())
    { clipping(1);
      redraw_panel(it);
      clipping(2);
     }
   }
}


void BASE_WINDOW::disable_item(panel_item it) 
{ if (it->enabled)
  { it->enabled = 0; 
    if (is_open()) 
    { clipping(1);
      redraw_panel(it);
      clipping(2);
     }
   }
}


void BASE_WINDOW::enable_button(int but)  
{ for(int i = 0; i<item_count; i++)
  { panel_item p = Item[i];
    if (p->kind == Button_Item && p->dat1 == but) 
    { enable_item(p);
      break;
     }
   }
 }


void BASE_WINDOW::disable_button(int but)  
{ for(int i = 0; i<item_count; i++)
  { panel_item p = Item[i];
    if (p->kind == Button_Item && p->dat1 == but) 
    { disable_item(p);
      break;
     }
   }
 }


// Iteration

panel_item BASE_WINDOW::first_item() { return Item[0]; }

panel_item BASE_WINDOW::next_item(panel_item it) 
{ int i = it->index + 1;
  return (i < item_count) ? Item[i] : 0;
 }

