//
// "$Id: DiffChooser.cxx 386 2006-03-04 14:15:27Z mike $"
//
// DiffChooser widget code.
//
// Copyright 2005 by Michael Sweet.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License v2 as published
// by the Free Software Foundation.
//
// This program 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.
//
// Contents:
//
//   DiffChooser::DiffChooser() - Create a new chooser.
//   DiffChooser::browser_cb()  - Handle selections in the file browser.
//   DiffChooser::count()       - Return the number of selected files.
//   DiffChooser::input_cb()    - Handle input in the filename field.
//   DiffChooser::menu_cb()     - Handle favorites menu selections.
//   DiffChooser::value()       - Set the filename value.
//   DiffChooser::value()       - Get the filename for the specified item.
//

#include "DiffChooser.h"
#include "FavoritesWindow.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


//
// 'DiffChooser::DiffChooser()' - Create a new chooser.
//

DiffChooser::DiffChooser(int X,		// I - X position
                         int Y,		// I - Y position
			 int W,		// I - Width
			 int H,		// I - Height
			 const char *L)	// I - Label
  : Fl_Group(X, Y, W, H, L),
    input_(X, Y, W, 35),
    menu_(X, Y + 35, W, 25, "Favorites"),
    browser_(X, Y + 60, W, H - 60)
{
  end();

  resizable(&browser_);

  browser_.callback((Fl_Callback *)browser_cb, this);
  browser_.type(FL_MULTI_BROWSER);
  browser_.when(FL_WHEN_CHANGED | FL_WHEN_NOT_CHANGED);

  input_.callback((Fl_Callback *)input_cb, this);
  input_.when(FL_WHEN_ENTER_KEY_CHANGED);

  menu_.callback((Fl_Callback *)menu_cb, this);

  value(".");
}


//
// 'DiffChooser::browser_cb()' - Handle selections in the file browser.
//

void
DiffChooser::browser_cb(Fl_File_Browser *fb,
					// I - File browser widget
                        DiffChooser     *dc)
					// I - DiffChooser widget
{
  char	filename[1024];			// Filename


  if (fb->value() <= 0)
    return;

  snprintf(filename, sizeof(filename), "%s%s", dc->directory_,
           fb->text(fb->value()));

  if (fl_filename_isdir(filename))
  {
    if (Fl::event_clicks())
      dc->value(filename);
    else
    {
      filename[strlen(filename) - 1] = '\0';

      dc->input_.value(filename);
      dc->do_callback();
    }
  }
  else
  {
    dc->input_.value(filename);
    dc->do_callback();
  }
}


//
// 'DiffChooser::count()' - Return the number of selected files.
//

int					// O - Count of selected items
DiffChooser::count()
{
  int	i,				// Looping var
	count;				// Count of selected items


  for (i = 1, count = 0; i <= browser_.size(); i ++)
    if (browser_.selected(i))
      count ++;

  return (count);
}


//
// 'DiffChooser::input_cb()' - Handle input in the filename field.
//

void
DiffChooser::input_cb(Fl_File_Input *fi,// I - File input widget
                      DiffChooser   *dc)// I - DiffChooser widget
{
  const char	*val;			// Filename value
  char		matchname[1024],	// Matching filename
		*ptr;			// Pointer into filename
  int		i,			// Looping var
		min_match,		// Minimum number of matching chars
		max_match,		// Maximum number of matching chars
		num_files,		// Number of files in directory
		first_line;		// First matching line
  const char	*file;			// File from directory


  val = fi->value();
  i   = strlen(val);

  if (Fl::event_key() == FL_Enter ||
      Fl::event_key() == FL_KP_Enter ||
      Fl::event_key() == '/' ||
      (i > 0 && val[i - 1] == '/'))
  {
    if (!fl_filename_isdir(val) && *val)
    {
      dc->do_callback();
      return;
    }

    dc->value(val);
  }

  matchname[sizeof(matchname) - 1] = '\0';

  if (Fl::event_key() == '/' ||
      Fl::event_key() == FL_Delete ||
      Fl::event_key() == FL_BackSpace)
  {
    strncpy(matchname, val, sizeof(matchname) - 1);
    if ((ptr = strrchr(matchname, '/')) != NULL)
      ptr[1] = '\0';

    if (strcmp(matchname, dc->directory_))
    {
      strcpy(dc->directory_, matchname);
      dc->browser_.load(dc->directory_);
    }
  }

  if ((val = strrchr(val, '/')) == NULL)
    val = fi->value();
  else
    val ++;

  // Do filename completion as possible...
  num_files  = dc->browser_.size();
  min_match  = strlen(val);
  max_match  = min_match + 1;
  first_line = 0;

  if (min_match > 0)
  {
    for (i = 1; i <= num_files && max_match > min_match; i ++)
    {
      file = dc->browser_.text(i);

      if (!strncmp(val, file, min_match))
      {
        // OK, this one matches; check against the previous match
	if (!first_line)
	{
	  // First match; copy stuff over...
	  strncpy(matchname, file, sizeof(matchname) - 1);
	  max_match = strlen(matchname);

          // Strip trailing /, if any...
	  if (matchname[max_match - 1] == '/')
	  {
	    max_match --;
	    matchname[max_match] = '\0';
	  }

	  // And then make sure that the item is visible
          dc->browser_.topline(i);
	  first_line = i;
	}
	else
	{
	  // Succeeding match; compare to find maximum string match...
	  while (max_match > min_match)
	    if (!strncmp(file, matchname, max_match))
	      break;
	    else
	      max_match --;

          // Truncate the string as needed...
          matchname[max_match] = '\0';
	}
      }
    }
  }

  // Scroll the file list if we have a match...
  dc->browser_.deselect(0);
  if (first_line)
    dc->browser_.select(first_line);
  dc->browser_.redraw();

  // Add a partial match to the input field...
  if (first_line && min_match < max_match &&
      Fl::event_key() != FL_BackSpace &&
      Fl::event_key() != FL_Delete)
  {
    // Replace the text...
    i = val - fi->value();
    fi->replace(i, i + min_match, matchname);

    // Highlight it with the cursor at the end of the selection so
    // s/he can press the right arrow to accept the selection
    // (Tab and End also do this for both cases.)
    fi->position(i + max_match, i + min_match);
  }
  else
    fi->position(fi->size());
}


//
// 'DiffChooser::menu_cb()' - Handle favorites menu selections.
//

void
DiffChooser::menu_cb(FavoritesMenu *fm,	// I - Favorites menu widget
                     DiffChooser   *dc)	// I - DiffChooser widget
{
  int			i;		// Menu item
  FavoritesWindow	*fw;		// Favorites window


  switch (i = fm->value())
  {
    case 0 : // Add to favorites
	fm->add_favorite(dc->directory_);
        break;

    case 1 : // Manage favorites
	fw = new FavoritesWindow();
	fw->show();

	while (fw->shown())
	  Fl::wait();

        delete fw;
        break;

    case 2 : // Filesystems
	dc->value("");
        break;

    default : // Something else
	dc->value(fm->text(i));
        break;
  }
}


//
// 'DiffChooser::value()' - Set the filename value.
//

void
DiffChooser::value(const char *f)	// I - New filename
{
  char	*ptr;				// Pointer in filename


  if (!f || !*f)
  {
    browser_.load("");
    input_.value("");

    directory_[0] = '\0';
    return;
  }

  fl_filename_absolute(filename_, sizeof(filename_), f);

  ptr = filename_ + strlen(filename_) - 3;
  if (ptr > filename_ && (!strcmp(ptr, "/..") || !strcmp(ptr - 1, "/../")))
  {
    if (*ptr != '/')
      ptr --;

    *ptr = '\0';
    if ((ptr = strrchr(filename_, '/')) != NULL)
      *ptr = '\0';
  }

  if (fl_filename_isdir(filename_))
  {
    if (filename_[strlen(filename_) - 1] != '/')
      snprintf(directory_, sizeof(directory_), "%s/", filename_);
    else
      strcpy(directory_, filename_);

    input_.value(directory_);
  }
  else
  {
    input_.value(filename_);

    if ((ptr = strrchr(filename_, '/')) != NULL)
      ptr[1] = '\0';

    strcpy(directory_, filename_);
  }

  browser_.load(directory_);
}


//
// 'DiffChooser::value()' - Get the filename for the specified item.
//

const char *				// O - Filename
DiffChooser::value(int i)		// I - Item number
{
  // Range check...
  if (i < 1 || i > browser_.size())
    return (NULL);

  snprintf(filename_, sizeof(filename_), "%s%s", directory_, browser_.text(i));
  return (filename_);
}


//
// End of "$Id: DiffChooser.cxx 386 2006-03-04 14:15:27Z mike $".
//
