
/******************************************************************************
* MODULE     : file_chooser.gen.cc
* DESCRIPTION: A file_chooser widget with horizontal and vertical scrollbars.
* COPYRIGHT  : (C) 1999  Joris van der Hoeven
*******************************************************************************
* This software falls under the GNU general public license and comes WITHOUT
* ANY WARRANTY WHATSOEVER. See the file $TEXMACS_PATH/LICENSE for more details.
* If you don't have this file, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
******************************************************************************/

#include <Widget/basic_widget.gen.h>
#include <file.gen.h>
#include <dir.gen.h>
#include <analyze.gen.h>

#module code_file_chooser_widget
#import basic_event
#import file
#import dir
#import analyze

extern string decode_file_name (string file_name);

/******************************************************************************
* File chooser commands
******************************************************************************/

#define CHANGE_FILE     0
#define CHANGE_DIR      1
#define BUTTON_HOME     2
#define BUTTON_TEXTS    3
#define BUTTON_OK       4
#define BUTTON_CANCEL   5
#define IMAGE_HSIZE     6
#define IMAGE_VSIZE     7
#define IMAGE_CLIP_X1   8
#define IMAGE_CLIP_Y1   9
#define IMAGE_CLIP_X2  10
#define IMAGE_CLIP_Y2  11

class file_chooser_command_rep: public command_rep {
  widget_rep* fch;
  int         type;
public:
  file_chooser_command_rep (widget w, int t): fch(w.rep), type(t) {}
  void apply ();
  ostream& print (ostream& out) {
    return out << "File chooser (" << type << ")"; }
};

void
file_chooser_command_rep::apply () {
  widget fch_wid (fch);
  switch (type) {
  case CHANGE_FILE:
    {
      string s;
      fch_wid[0]["file"]["input"] << get_string ("input", s);
      fch_wid << set_string ("return", s);
      break;
    }
  case CHANGE_DIR:
    {
      string dir;
      fch_wid[0]["directory"]["input"] << get_string ("input", dir);
      if (dir == "cancel") fch_wid << set_string ("return", "cancel");
      else {
	dir= unquote (dir);
	fch_wid << set_string ("directory", dir);
      }
      break;
    }
  case BUTTON_HOME:
    fch_wid << set_string ("directory", "~");
    break;
  case BUTTON_TEXTS:
    fch_wid << set_string ("directory", "$TEXMACS_HOME_PATH/texts");
    break;
  case BUTTON_OK:
    {
      string s;
      fch_wid[0]["file"]["input"] << get_string ("input", s);
      fch_wid << set_string ("return", "\"" * s * "\"");
      break;
    }
  case BUTTON_CANCEL:
    fch_wid << set_string ("return", "cancel");
    break;
  default:
    {
      string which, s;
      if (type == IMAGE_HSIZE) which= "hsize";
      else if (type == IMAGE_VSIZE) which= "vsize";
      else if (type == IMAGE_CLIP_X1) which= "clip-x1";
      else if (type == IMAGE_CLIP_Y1) which= "clip-y1";
      else if (type == IMAGE_CLIP_X2) which= "clip-x2";
      else which= "clip-y2";
      widget inp= fch_wid[0]["image"]["parameters"][which]["input"];
      inp << get_string ("input", s);
      if (s == "cancel") fch_wid << set_string ("return", "cancel");
      else {
	s= unquote (s);
	inp << set_string ("input", s);
      }
      break;
    }
  }
}

command
file_chooser_command (widget fch, int type) {
  return new file_chooser_command_rep (fch, type);
}

/******************************************************************************
* File_list widgets
******************************************************************************/

class file_list_widget_rep: public attribute_widget_rep {
  widget_rep*   fch;
  string        dir;
  array<string> names;
  array<string> suffix;
  bool          dir_flag;
  int           hilight;

public:
  file_list_widget_rep (widget ch, array<string> suffix, bool dir_flag);
  operator tree ();

  widget get_canvas ();

  void handle_get_size (get_size_event ev);
  void handle_repaint (repaint_event ev);
  void handle_mouse (mouse_event ev);
  void handle_set_string (set_string_event ev);
};

/******************************************************************************
* Implementation of file_list widgets
******************************************************************************/

file_list_widget_rep::file_list_widget_rep (widget c, array<string> s, bool f):
  attribute_widget_rep (c->dis, 0), fch (c.rep),
  dir (""), suffix (s), dir_flag (f), hilight (-1) {}

file_list_widget_rep::operator tree () {
  return "file_list";
}

widget
file_list_widget_rep::get_canvas () {
  string which (dir_flag? string ("directories"): string ("files"));
  widget fch_wid (fch);
  return fch_wid[0]["list"][which];
}

static bool
has_suffix (string name, array<string> suffix) {
  int i;
  for (i=0; i<N(suffix); i++)
    if (ends (name, suffix[i])) return TRUE;
  return FALSE;
}

static bool
list_in_directory (string dir, string name,
		   array<string> suffix, bool dir_flag)
{
  if (name == "") return FALSE;
  if (name == "..") return dir_flag;
  if (name[0]=='.') return FALSE;
  if (dir_flag) return is_directory (dir * "/" * name);
  else return is_regular (dir * "/" * name) && has_suffix (name, suffix);
}

void
file_list_widget_rep::handle_get_size (get_size_event ev) {
  int i;
  text_extents ex;
  font fn= dis->default_font ();
  ev->w= ev->h= 0;
  for (i=0; i<N(names); i++)
    if (list_in_directory (dir, names[i], suffix, dir_flag)) {
      fn->var_get_extents (names[i], ex);
      ev->w  = max (ev->w, ((ex->x2- ex->x1+ 2)/3) + (6*PIXEL));
      ev->h += ((fn->y2- fn->y1+ 2)/3) + (4*PIXEL);
    }
  abs_round (ev->w, ev->h);
}

void
file_list_widget_rep::handle_repaint (repaint_event ev) { (void) ev;
  int i; 
  text_extents ex;
  win->set_background (dis->white);
  win->clear (0, -h, w, 0);
  font fn= dis->default_font ();
  win->set_shrinking_factor (3);
  SI y= 0;
  for (i=0; i<N(names); i++)
    if (list_in_directory (dir, names[i], suffix, dir_flag)) {
      win->set_color (dis->black);
      if (hilight == i) win->set_color (dis->red);
      fn->var_get_extents (names[i], ex);
      fn ->draw (win, names[i], 9*PIXEL, y-fn->y2-6*PIXEL);
      y += fn->y1- fn->y2- 12*PIXEL;
    }
  win->set_shrinking_factor (1);
}

void
file_list_widget_rep::handle_mouse (mouse_event ev) {
  string type= ev->type;

  if ((type == "release-left") || (type == "release-right")) {
    int i;
    SI y= 0, search= ev->y*3;
    text_extents ex;
    font fn= dis->default_font ();
    for (i=0; i<N(names); i++)
      if (list_in_directory (dir, names[i], suffix, dir_flag)) {
	fn->var_get_extents (names[i], ex);
	if ((search >= (y+ fn->y1- fn->y2- 12*PIXEL)) && (search < y)) break;
	y += fn->y1- fn->y2- 12*PIXEL;
      }
    if (i==N(names)) return;

    string s= names[i];
    widget fch_wid (fch);
    if (hilight == i) {
      if (dir_flag) {
	if (s == "..") {
	  int j; s= dir;
	  for (j=N(s)-1; j>=0; j--)
	    if (s[j]=='/') { s= s (0, j); break; }
	  if (s == "") s= "/";
	}
	else {
	  if ((dir != "") && (dir[N(dir)-1]=='/')) s= dir * s;
	  else s= dir * "/" * s;
	}
	fch_wid << set_string ("directory", s);
      }
      else fch_wid << set_string ("return", "\"" * s * "\"");
    }
    else {
      hilight= i;
      if (!dir_flag) fch_wid << set_string ("file", s);
      this << emit_invalidate_all ();
    }
  }

  if ((type == "press-up") || (type == "press-down")) {
    SI x, y, dy= 100*PIXEL;
    if (type == "press-down") dy= -dy;
    get_canvas () << get_coord2 ("scroll position", x, y);
    y += dy;
    get_canvas () << set_scroll_pos (x, y);
  }
}

void
file_list_widget_rep::handle_set_string (set_string_event ev) {
  if (ev->which == "directory") {
    dir= ev->s;
    bool flag;
    names= read_directory (dir, flag);
    SI w, h;
    this << get_size (w, h, 0);
    get_canvas () << set_extents (0, -h, w, 0);
    hilight=-1;
    if (attached ()) this << emit_invalidate_all ();
  }
  else attribute_widget_rep::handle_set_string (ev);
}

/******************************************************************************
* image widgets
******************************************************************************/

class image_widget_rep: public attribute_widget_rep {
  string file_name;
public:
  image_widget_rep (display dis);
  operator tree ();
  void handle_get_size (get_size_event ev);
  void handle_repaint (repaint_event ev);
  void handle_set_string (set_string_event ev);
};

/******************************************************************************
* Implementation of image widgets
******************************************************************************/

image_widget_rep::image_widget_rep (display dis):
  attribute_widget_rep (dis, 0, south_west), file_name ("") {}

image_widget_rep::operator tree () {
  return "image";
}

void
image_widget_rep::handle_get_size (get_size_event ev) {
  ev->w= 221*PIXEL;
}

void
image_widget_rep::handle_repaint (repaint_event ev) { (void) ev;
  win->set_background (dis->white);
  win->clear (0, 0, w, h);
  layout_dark_outline (win, 0, 0, w, h);
  if (file_name != "") {
    SI x1, y1, x2, y2;
    ps_bounding_box (file_name, "", x1, y1, x2, y2);
    
    SI ww= w-2*PIXEL, hh= h-2*PIXEL;
    if ((x2>x1) && (y2>y1) && (ww>0) && (hh>0)) {
      if (((x2-x1)*hh) > ((y2-y1)*ww))
	hh= (ww*(y2-y1))/(x2-x1);
      else ww= (hh*(x2-x1))/(y2-y1);
    }

    win->postscript (file_name, "", ww, hh, PIXEL, PIXEL, x1, y1, x2, y2);
  }
}

void
image_widget_rep::handle_set_string (set_string_event ev) {
  if (ev->which == "name") {
    file_name= ev->s;
    if (attached ()) this << emit_invalidate_all ();
  }
  else attribute_widget_rep::handle_set_string (ev);
}

/******************************************************************************
* File_Chooser widgets
******************************************************************************/

class file_chooser_widget_rep: public attribute_widget_rep {
  command       cmd;
  string        type;
  array<string> suffix;
  string        magn;

public:
  file_chooser_widget_rep (display dis, command cmd, string type, string magn);
  operator tree ();

  widget input_widget (string what, int type);
  widget button_widget (string what, int type);

  void handle_get_size (get_size_event ev);
  void handle_set_string (set_string_event ev);
  void handle_get_string (get_string_event ev);
  void handle_destroy (destroy_event ev);
};

/******************************************************************************
* Implementation of file_chooser widgets
******************************************************************************/

widget
file_chooser_widget_rep::input_widget (string what, int type) {
  array<widget> ww (2);
  array<string> nn (2);
  ww[0]= text_widget (dis, what, FALSE, "english");
  ww[1]= input_text_widget (dis, file_chooser_command (this, type));
  nn[1]= "input";
  return horizontal_list (dis, ww, nn);
}

widget
file_chooser_widget_rep::button_widget (string what, int type) {
  return command_button (text_widget (dis, what, FALSE, "english"),
			 file_chooser_command (this, type), TRUE);
}

file_chooser_widget_rep::file_chooser_widget_rep (
  display dis, command cmd2, string type2, string magn2):
  attribute_widget_rep (dis, 1), cmd (cmd2), type (type2), magn (magn2)
{
  ref_count++;

  if (type == "TeXmacs") suffix << ".tm" << ".ts" << ".tp";
  else if (type == "tex") suffix << ".tex" << ".sty" << ".cls";
  else if (type == "latex") suffix << ".tex" << ".sty" << ".cls";
  else if (type == "html") suffix << ".html" << ".htm";
  else if (type == "postscript") suffix << ".ps" << ".eps";
  else if (type == "image")
    suffix << ".ps" << ".eps" << ".tif" << ".pdf" << ".pnm"
	   << ".gif" << ".ppm" << ".xpm" << ".fig" << ".png" << ".jpg";
  else suffix << "";

  SI sep= 3*PIXEL;
  array<widget> cw2 (5);
  array<string> cn2 (5);
  cw2[0]= glue_widget (dis, FALSE, TRUE, sep);
  cw2[1]= canvas_widget (new file_list_widget_rep (this, suffix, TRUE));
  cn2[1]= "directories";
  cw2[2]= glue_widget (dis, FALSE, TRUE, sep);
  cw2[3]= canvas_widget (new file_list_widget_rep (this, suffix, FALSE));
  cn2[3]= "files";
  cw2[4]= glue_widget (dis, FALSE, TRUE, sep-PIXEL);

  array<widget> cw3 (9);
  cw3[0]= glue_widget (dis, FALSE, FALSE, sep);
  cw3[1]= button_widget ("home", BUTTON_HOME);
  cw3[2]= glue_widget (dis, FALSE, FALSE, sep);
  cw3[3]= button_widget ("texts", BUTTON_TEXTS);
  cw3[4]= glue_widget (dis, TRUE, FALSE);
  cw3[5]= button_widget ("ok", BUTTON_OK);
  cw3[6]= glue_widget (dis, FALSE, FALSE, sep);
  cw3[7]= button_widget ("cancel", BUTTON_CANCEL);
  cw3[8]= glue_widget (dis, FALSE, FALSE, sep);

  int cwn= ((type == "image")? 15: 9);
  array<widget> cw (cwn);
  array<string> cn (cwn);
  cw[0]    = glue_widget (dis, TRUE, FALSE, 0, sep);
  cw[1]    = input_widget ("Directory:", CHANGE_DIR);
  cn[1]    = "directory";
  cw[2]    = glue_widget (dis, TRUE, FALSE, 0, sep);
  cw[3]    = input_widget ("File:", CHANGE_FILE);
  cn[3]    = "file";
  cw[4]    = glue_widget (dis, TRUE, FALSE, 0, sep);
  cw[5]    = horizontal_list (dis, cw2, cn2);
  cn[5]    = "list";
  cw[6]    = glue_widget (dis, TRUE, FALSE, 0, sep);
  cw[cwn-2]= horizontal_list (dis, cw3);
  cn[cwn-2]= "buttons";
  cw[cwn-1]= glue_widget (dis, TRUE, FALSE, 0, sep);

  if (type == "image") {
    array<widget> imw (11);
    array<string> ims (11);
    imw[ 0]= input_widget ("width:", IMAGE_HSIZE);
    ims[ 0]= "hsize";
    imw[ 1]= glue_widget (dis, TRUE, FALSE, 0, sep);
    imw[ 2]= input_widget ("height:", IMAGE_VSIZE);
    ims[ 2]= "vsize";
    imw[ 3]= glue_widget (dis, TRUE, FALSE, 0, sep);
    imw[ 4]= input_widget ("left border:", IMAGE_CLIP_X1);
    ims[ 4]= "clip-x1";
    imw[ 5]= glue_widget (dis, TRUE, FALSE, 0, sep);
    imw[ 6]= input_widget ("lower border:", IMAGE_CLIP_Y1);
    ims[ 6]= "clip-y1";
    imw[ 7]= glue_widget (dis, TRUE, FALSE, 0, sep);
    imw[ 8]= input_widget ("right border:", IMAGE_CLIP_X2);
    ims[ 8]= "clip-x2";
    imw[ 9]= glue_widget (dis, TRUE, FALSE, 0, sep);
    imw[10]= input_widget ("upper border:", IMAGE_CLIP_Y2);
    ims[10]= "clip-y2";

    array<widget> cw4 (5);
    array<string> cn4 (5);
    cw4[0] = glue_widget (dis, FALSE, FALSE, sep);
    cw4[1] = vertical_list (dis, imw, ims);
    cn4[1] = "parameters";
    cw4[2] = glue_widget (dis, FALSE, FALSE, sep);
    cw4[3] = new image_widget_rep (dis);
    cn4[3] = "image";
    cw4[4] = glue_widget (dis, FALSE, FALSE, sep);

    cw[ 7] = separator_widget (dis);
    cw[ 8] = glue_widget (dis, TRUE, FALSE, 0, sep);
    cw[ 9] = horizontal_list (dis, cw4, cn4);
    cn[ 9] = "image";
    cw[10] = glue_widget (dis, TRUE, FALSE, 0, sep);
    cw[11] = separator_widget (dis);
    cw[12] = glue_widget (dis, TRUE, FALSE, 0, sep);
  }

  a[0]= vertical_list (dis, cw, cn);

  ref_count--;
}

file_chooser_widget_rep::operator tree () {
  return tree (TUPLE, "file_chooser", (tree) a[0]);
}

void
file_chooser_widget_rep::handle_get_size (get_size_event ev) {
  ev->w= 451*PIXEL;
  if (type == "image") ev->h= 500*PIXEL;
  else ev->h= 350*PIXEL;
}

void
file_chooser_widget_rep::handle_set_string (set_string_event ev) {
  if (ev->which == "directory") {
    string dir= decode_file_name (ev->s);
    a[0]["directory"]["input"] << set_string ("input", dir);
    a[0]["file"]["input"] << set_string ("input", "");
    a[0]["list"]["directories"] << set_string ("directory", dir);
    a[0]["list"]["files"] << set_string ("directory", dir);
  }
  else if (ev->which == "file") {
    a[0]["file"]["input"] << set_string ("input", ev->s);
    if (type == "image") {
      string dir, name= ev->s;
      a[0]["directory"]["input"] << get_string ("input", dir);
      if (is_web_file (name) || starts (name, "/") || (name==""));
      else name = dir * "/" * name;
      a[0]["image"]["image"] << set_string ("name", name);
      array<string> ps_suffix;
      ps_suffix << ".ps" << ".eps";
      widget par_wid= a[0]["image"]["parameters"];
      if (has_suffix (name, ps_suffix)) {
	par_wid["hsize"]["input"] << set_string ("input", "");
	par_wid["vsize"]["input"] << set_string ("input", "");
      }
      else {
	par_wid["hsize"]["input"] << set_string ("input", magn);
	par_wid["vsize"]["input"] << set_string ("input", magn);
      }
    }
  }
  else if (ev->which == "return") {
    string s= ev->s;
    if (is_quoted (s) && (!has_suffix (unquote (s), suffix)))
      a[0]["file"]["input"] << set_string ("input", unquote (s) * suffix[0]);
    else {
      a[0]["file"]["input"] << set_string ("input", s);
      cmd ();
    }
  }
  else attribute_widget_rep::handle_set_string (ev);
}

void
file_chooser_widget_rep::handle_get_string (get_string_event ev) {
  if (ev->which == "input") {
    string dir, name;
    a[0]["directory"]["input"] << get_string ("input", dir);
    a[0]["file"]["input"] << get_string ("input", name);
    if (name == "cancel") { ev->s= "cancel"; return; }
    dir= unquote (dir);
    while ((N(dir)>0) && (dir[N(dir)-1]=='/')) dir= dir (0, N(dir)-1);
    name= unquote (name);
    if (is_web_file (name) || starts (name, "/"))
      ev->s= "\"" * name * "\"";
    else ev->s= "\"" * dir * "/" * name * "\"";
    if (type == "image") {
      string hsize, vsize, cx1, cy1, cx2, cy2;
      widget par= a[0]["image"]["parameters"];
      par["hsize"]["input"] << get_string ("input", hsize);
      par["vsize"]["input"] << get_string ("input", vsize);
      par["clip-x1"]["input"] << get_string ("input", cx1);
      par["clip-y1"]["input"] << get_string ("input", cy1);
      par["clip-x2"]["input"] << get_string ("input", cx2);
      par["clip-y2"]["input"] << get_string ("input", cy2);
      ev->s=
	"(" * ev->s * " \"" * hsize * "\" \"" * vsize *
	"\" \"" * cx1 * "\" \"" * cy1 *
	"\" \"" * cx2 * "\" \"" * cy2 * "\")";
    }
  }
  else attribute_widget_rep::handle_get_string (ev);
}

void
file_chooser_widget_rep::handle_destroy (destroy_event ev) {
  (void) ev;
  this << set_string ("return", "cancel");
}

/******************************************************************************
* exported routines
******************************************************************************/

widget
file_chooser_widget (display dis, command cmd, string type, string magn) {
  return new file_chooser_widget_rep (dis, cmd, type, magn);
}

#endmodule // code_file_chooser_widget
