/* Find file command for the Midnight Commander
   Copyright (C) The Free Software Foundation
   Written 1995 by Miguel de Icaza

   Complete rewrote.
   
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
   
   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.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include <config.h>
#include "tty.h"
#include <string.h>
#include <stdio.h>
#ifdef NEEDS_IO_H
#    include <io.h>
#endif
#include <sys/stat.h>
#include <sys/param.h>
#include <fcntl.h>
#include <ctype.h>
#include "global.h"
#include "win.h"
#include "color.h"
#include "global.h"
#include "setup.h"
#include "find.h"

/* Dialog manager and widgets */
#include "dlg.h"
#include "widget.h"

#include "dialog.h"     /* For do_refresh() */
#define  DIR_H_INCLUDE_HANDLE_DIRENT
#include "dir.h"
#include "panel.h"		/* current_panel */
#include "main.h"		/* do_cd, try_to_select */
#include "wtools.h"
#include "tree.h"
#include "cmd.h"		/* view_file_at_line */
#include "../vfs/vfs.h"

#ifndef PORT_HAS_FLUSH_EVENTS
#    define x_flush_events()
#endif

/* Size of the find parameters window */
#define FIND_Y 12
static int FIND_X = 50;

/* Size of the find window */
#define FIND2_Y LINES-4
static int FIND2_X = 64;

#ifdef HAVE_X
#   define FIND2_X_USE 35
#else
#   define FIND2_X_USE FIND2_X-20
#endif

/* A couple of extra messages we need */
enum {
    B_STOP = B_USER + 1,
    B_AGAIN,
    B_PANELIZE,
    B_TREE,
    B_VIEW
};

/* A list of directories to be ignores, separated with ':' */
char *find_ignore_dirs = 0;

static WInput *in_start;	/* Start path */
static WInput *in_name;		/* Pattern to search */
static WInput *in_with;		/* text inside filename */
static int running = 0;		/* nice flag */
static char *find_pattern;	/* Pattern to search */
static char *content_pattern;	/* pattern to search inside files */
static int count;		/* Number of files displayed */
static int matches;		/* Number of matches */
static int is_start;		/* Status of the start/stop toggle button */
int max_loops_in_idle = 10;
static char *old_dir;

static Dlg_head *find_dlg;	/* The dialog */

#ifdef HAVE_GNOME
static GtkWidget *g_find_dlg;
static GtkWidget *g_status_label;
static GtkWidget *g_clist;
static GtkWidget *g_start_stop;
static GtkWidget *g_start_stop_label;
static GtkWidget *g_view, *g_edit;
static GtkWidget *g_panelize;
static int current_row;
static int idle_tag;
static int stop;
#else
static WButton *stop_button;	/* pointer to the stop button */
static WLabel *status_label;	/* Finished, Searching etc. */
static WListbox *find_list;	/* Listbox with the file list */
#endif

/* For nice updating */
static char *rotating_dash = "|/-\\";

/* This keeps track of the directory stack */
typedef struct dir_stack {
    char *name;
    struct dir_stack *prev;
} dir_stack ;

dir_stack *dir_stack_base = 0;

static struct {
	char* text;
	int len;	/* length including space and brackets */
	int x;
} fbuts [] = {
	{ N_("&Suspend"),   11, 29 },
	{ N_("Con&tinue"),  12, 29 },
	{ N_("&Chdir"),     11, 3  },
	{ N_("&Again"),     9,  17 },
	{ N_("&Quit"),      8,  43 },
	{ N_("Pane&lize"),  12, 3  },
	{ N_("&View - F3"), 13, 20 },
	{ N_("&Edit - F4"), 13, 38 }
};

static char *add_to_list (char *text, void *closure);
static void stop_idle (void *data);
static void status_update (char *text);
static void get_list_info (char **file, char **dir);

/*
 * find_parameters: gets information from the user
 *
 * If the return value is true, then the following holds:
 *
 * START_DIR and PATTERN are pointers to char * and upon return they
 * contain the information provided by the user.
 *
 * CONTENT holds a strdup of the contents specified by the user if he
 * asked for them or 0 if not (note, this is different from the
 * behavior for the other two parameters.
 *
 */

static int
find_parameters (char **start_dir, char **pattern, char **content)
{
    int return_value;
#ifndef HAVE_GNOME
    char *temp_dir;
#endif
    static char *in_contents = NULL;
    static char *in_start_dir = NULL;
    static char *in_start_name = NULL;

	static char* labs[] = {N_("Start at:"), N_("Filename:"), N_("Content: ")};
	static char* buts[] = {N_("&Ok"), N_("&Tree"), N_("&Cancel")};
	static int ilen = 30, istart = 14;
	static int b0 = 3, b1 = 16, b2 = 36;

#ifdef ENABLE_NLS
	static int i18n_flag = 0;
	
	if (!i18n_flag)
	{
		register int i = sizeof(labs)/sizeof(labs[0]);
		int l1, maxlen = 0;
		
		while (i--)
		{
			l1 = strlen (labs [i] = _(labs [i]));
			if (l1 > maxlen)
				maxlen = l1;
		}
		i = maxlen + ilen + 7;
		if (i > FIND_X)
			FIND_X = i;
		
		for (i = sizeof(buts)/sizeof(buts[0]), l1 = 0; i--; )
		{
			l1 += strlen (buts [i] = _(buts [i]));
		}
		l1 += 21;
		if (l1 > FIND_X)
			FIND_X = l1;

		ilen = FIND_X - 7 - maxlen; /* for the case of very long buttons :) */
		istart = FIND_X - 3 - ilen;
		
		b1 = b0 + strlen(buts[0]) + 7;
		b2 = FIND_X - (strlen(buts[2]) + 6);
		
		i18n_flag = 1;
	}
	
#endif /* ENABLE_NLS */

find_par_start:
    if (!in_start_dir)
	in_start_dir = g_strdup (".");
    if (!in_start_name)
	in_start_name = g_strdup (easy_patterns ? "*" : ".");
    if (!in_contents)
	in_contents = g_strdup ("");
    
    find_dlg = create_dlg (0, 0, FIND_Y, FIND_X, dialog_colors,
			   common_dialog_callback, "[Find File]", "findfile",
			   DLG_CENTER | DLG_GRID);
    x_set_dialog_title (find_dlg, _("Find File"));

	add_widgetl (find_dlg, button_new (9, b2, B_CANCEL, NORMAL_BUTTON,
		buts[2], 0 ,0, "cancel"), XV_WLAY_RIGHTOF);
#ifndef HAVE_GNOME
	add_widgetl (find_dlg, button_new (9, b1, B_TREE, NORMAL_BUTTON,
		buts[1], 0, 0, "tree"), XV_WLAY_RIGHTOF);
#endif
	add_widgetl (find_dlg, button_new (9, b0, B_ENTER, DEFPUSH_BUTTON,
		buts[0], 0, 0, "ok"), XV_WLAY_CENTERROW);

	in_with  = input_new (7, istart, INPUT_COLOR, ilen,	in_contents, "content");
    add_widgetl (find_dlg, in_with, XV_WLAY_BELOWOF);

	in_name  = input_new (5, istart, INPUT_COLOR, ilen, in_start_name, "name");
    add_widgetl (find_dlg, in_name, XV_WLAY_BELOWOF);

	in_start = input_new (3, istart, INPUT_COLOR, ilen,	in_start_dir, "start");
    add_widgetl (find_dlg, in_start, XV_WLAY_NEXTCOLUMN);

	add_widgetl (find_dlg, label_new (7, 3, labs[2], "label-cont"), XV_WLAY_BELOWOF);
	add_widgetl (find_dlg, label_new (5, 3, labs[1], "label-file"), XV_WLAY_BELOWOF);
	add_widgetl (find_dlg, label_new (3, 3, labs[0], "label-start"), XV_WLAY_NEXTCOLUMN);

    run_dlg (find_dlg);

    switch (find_dlg->ret_value){
    case B_CANCEL:
	return_value = 0;
	break;

#ifndef HAVE_GNOME
    case B_TREE:
	temp_dir = g_strdup (in_start->buffer);
	destroy_dlg (find_dlg);
	g_free (in_start_dir);
	if (strcmp (temp_dir, ".") == 0){
	   g_free (temp_dir);
	    temp_dir = g_strdup (cpanel->cwd);
	}
	in_start_dir = tree (temp_dir);
	if (in_start_dir)
	   g_free (temp_dir);
	else
	    in_start_dir = temp_dir;
	/* Warning: Dreadful goto */
	goto find_par_start;
	break;
#endif
	
    default:
	return_value = 1;
	*start_dir = g_strdup (in_start->buffer);
	*pattern   = g_strdup (in_name->buffer);

	g_free (in_contents);
	if (in_with->buffer [0]){
	    *content    = g_strdup (in_with->buffer);
	    in_contents = g_strdup (*content);
	} else 
	    *content = in_contents = NULL;

	g_free (in_start_dir);
	in_start_dir = g_strdup (*start_dir);
	g_free (in_start_name);
	in_start_name = g_strdup (*pattern);
    }

    destroy_dlg (find_dlg);
			 
    return return_value;
}

static void
push_directory (char *dir)
{
    dir_stack *new;

    new = g_new (dir_stack, 1);
    new->name = g_strdup (dir);
    new->prev = dir_stack_base;
    dir_stack_base = new;
}

static char*
pop_directory (void)
{
    char *name; 
    dir_stack *next;

    if (dir_stack_base){
	name = dir_stack_base->name;
	next = dir_stack_base->prev;
	g_free (dir_stack_base);
	dir_stack_base = next;
	return name;
    } else
	return 0;
}

static void
insert_file (char *dir, char *file)
{
    char *tmp_name;
    static char *dirname;
    int i;

    if (dir [0] == PATH_SEP && dir [1] == PATH_SEP)
	dir++;
    i = strlen (dir);
    if (i){
	if (dir [i - 1] != PATH_SEP){
	    dir [i] = PATH_SEP;
	    dir [i + 1] = 0;
	}
    }

    if (old_dir){
	if (strcmp (old_dir, dir)){
	   g_free (old_dir);
	    old_dir = g_strdup (dir);
	    dirname = add_to_list (dir, NULL);
	}
    } else {
	old_dir = g_strdup (dir);
	dirname = add_to_list (dir, NULL);
    }
    
    tmp_name = g_strconcat ("    ", file, NULL);
    add_to_list (tmp_name, dirname);
    g_free (tmp_name);
}

static void
find_add_match (Dlg_head *h, char *dir, char *file)
{
    int p = ++matches & 7;

    insert_file (dir, file);

#ifndef HAVE_GNOME
    /* Scroll nicely */
    if (!p)
	listbox_select_last (find_list, 1);
    else
	listbox_select_last (find_list, 0);
#endif
    
#ifndef HAVE_X
	/* Updates the current listing */
	send_message (h, &find_list->widget, WIDGET_DRAW, 0);
	if (p == 7)
	    mc_refresh ();
#endif	    
}

static char *
locate_egrep (void)
{
/* commented out because mc runs egrep via execvp() which searches PATH
    itself. It is likely that egrep in PATH is better than in /bin */
#if 0
    char *paths [] = {
	"/bin/egrep",
	"/usr/bin/egrep",
	"/sbin/egrep",
	"/usr/sbin/egrep",
	NULL
    };
    struct stat s;
    char **p;
    
    for (p = &paths [0]; *p; p++){
	if (stat (*p, &s) == 0)
	    return *p;
    }
#endif
    return "egrep";
}

/* 
 * search_content:
 *
 * Search with egrep the global (FIXME) content_pattern string in the
 * DIRECTORY/FILE.  It will add the found entries to the find listbox.
 */
static void
search_content (Dlg_head *h, char *directory, char *filename)
{
    struct stat s;
    char buffer [BUF_SMALL];
    char *fname, *p;
    int file_fd, pipe, ignoring;
    char c;
    int i;
    pid_t pid;
    static char *egrep_path;

    fname = concat_dir_and_file (directory, filename);

    if (mc_stat (fname, &s) != 0 && !S_ISREG (s.st_mode)){
	g_free (fname);
	return;
    }
    if (!S_ISREG (s.st_mode)){
	g_free (fname);
	return;
    }
    
    file_fd = mc_open (fname, O_RDONLY);
    g_free (fname);
    
    if (file_fd == -1)
	return;

    if (!egrep_path)
	egrep_path = locate_egrep ();

#ifndef GREP_STDIN
    pipe = mc_doublepopen (file_fd, -1, &pid, egrep_path, egrep_path, "-n", content_pattern, NULL);
#else /* GREP_STDIN */
    pipe = mc_doublepopen (file_fd, -1, &pid, egrep_path, egrep_path, "-n", content_pattern, "-", NULL);
#endif /* GREP STDIN */
	
    if (pipe == -1){
	mc_close (file_fd);
	return;
    }
    
    g_snprintf (buffer, sizeof (buffer), _("Grepping in %s"), name_trunc (filename, FIND2_X_USE));

    status_update (buffer);
    mc_refresh ();
    p = buffer;
    ignoring = 0;
    
    enable_interrupt_key ();
    got_interrupt ();
    while (1){
	i = read (pipe, &c, 1);
	if (i != 1)
	    break;
	
	if (c == '\n'){
	    p = buffer;
	    ignoring = 0;
	}
	if (ignoring)
	    continue;
	
	if (c == ':'){
	    char *the_name;
	    
	    *p = 0;
	    ignoring = 1;
	    the_name = g_strconcat (buffer, ":", filename, NULL);
	    find_add_match (h, directory, the_name);
	    g_free (the_name);
	} else {
	    if (p - buffer < (sizeof (buffer)-1) && ISASCII (c) && isdigit (c))
		*p++ = c;
	    else
		*p = 0;
	} 
    }
    disable_interrupt_key ();
    if (i == -1)
	message (1, _(" Find/read "), _(" Problem reading from child "));

    mc_doublepclose (pipe, pid);
    mc_close (file_fd);
}

static int
do_search (struct Dlg_head *h)
{
    static struct dirent *dp   = 0;
    static DIR  *dirp = 0;
    static char directory [MC_MAXPATHLEN+2];
    struct stat tmp_stat;
#ifndef HAVE_X
    static int pos;
#endif
    static int subdirs_left = 0;
    char *tmp_name;		/* For bulding file names */

    if (!h) { /* someone forces me to close dirp */
	if (dirp) {
	    mc_closedir (dirp);
	    dirp = 0;
	}
        dp = 0;
	return 1;
    }
#ifndef HAVE_X
 do_search_begin:
#endif
    while (!dp){
	
	if (dirp){
	    mc_closedir (dirp);
	    dirp = 0;
	}
	
	while (!dirp){
	    char *tmp;

#ifndef HAVE_X	    
	    attrset (REVERSE_COLOR);
#endif
	    while (1) {
		tmp = pop_directory ();
		if (!tmp){
		    running = 0;
		    status_update (_("Finished"));
		    stop_idle (h);
		    return 0;
		}
		if (find_ignore_dirs){
                    int found;
		    char *temp_dir = g_strconcat (":", tmp, ":", NULL);

                    found = strstr (find_ignore_dirs, temp_dir) != 0;
                    g_free (temp_dir);
		    if (found)
			g_free (tmp);
		    else
			break;
		} else
		    break;
	    } 
	    
	    strcpy (directory, tmp);
	   g_free (tmp);

	    if (verbose){
		    char buffer [BUF_SMALL];

		    g_snprintf (buffer, sizeof (buffer), _("Searching %s"), name_trunc (directory, FIND2_X_USE));
		    status_update (buffer);
	    }
	    /* mc_stat should not be called after mc_opendir
	       because vfs_s_opendir modifies the st_nlink
	    */
	    mc_stat (directory, &tmp_stat);
	    subdirs_left = tmp_stat.st_nlink - 2;
	    /* Commented out as unnecessary
	       if (subdirs_left < 0)
	       subdirs_left = MAXINT;
	    */
	    dirp = mc_opendir (directory);
	}
	dp = mc_readdir (dirp);
    }

    if (strcmp (dp->d_name, ".") == 0 ||
	strcmp (dp->d_name, "..") == 0){
	dp = mc_readdir (dirp);
	return 1;
    }
    
    tmp_name = concat_dir_and_file (directory, dp->d_name);

    if (subdirs_left){
	mc_lstat (tmp_name, &tmp_stat);
	if (S_ISDIR (tmp_stat.st_mode)){
	    push_directory (tmp_name);
	    subdirs_left--;
	}
    }

    if (regexp_match (find_pattern, dp->d_name, match_file)){
	if (content_pattern)
	    search_content (h, directory, dp->d_name);
	else 
	    find_add_match (h, directory, dp->d_name);
    }
    
    g_free (tmp_name);
    dp = mc_readdir (dirp);

    /* Displays the nice dot */
    count++;
    if (!(count & 31)){
	if (verbose){
#ifndef HAVE_X
	    pos = (pos + 1) % 4;
	    attrset (NORMALC);
	    dlg_move (h, FIND2_Y-6, FIND2_X - 4);
	    addch (rotating_dash [pos]);
	    mc_refresh ();
	}
    } else
	goto do_search_begin;
#else
        }
    }
#endif
    x_flush_events ();
    return 1;
}

static void
init_find_vars (void)
{
    char *dir;
    
    if (old_dir){
	g_free (old_dir);
	old_dir = 0;
    }
    count = 0;
    matches = 0;

    /* Remove all the items in the stack */
    while ((dir = pop_directory ()) != NULL)
	g_free (dir);
}

static void
find_do_view_edit (int unparsed_view, int edit, char *dir, char *file)
{
    char *fullname, *filename;
    int line;
    
    if (content_pattern){
	filename = strchr (file + 4, ':') + 1;
	line = atoi (file + 4);
    } else {
	 filename = file + 4;
	 line = 0;
    }
    if (dir [0] == '.' && dir [1] == 0)
	 fullname = g_strdup (filename);
    else if (dir [0] == '.' && dir [1] == PATH_SEP)
	 fullname = concat_dir_and_file (dir+2, filename);
    else
	 fullname = concat_dir_and_file (dir, filename);

    if (edit)
	do_edit_at_line (fullname, line);
    else
        view_file_at_line (fullname, unparsed_view, use_internal_view, line);
    g_free (fullname);
}

#ifdef HAVE_GNOME
static void
select_row (GtkCList *clist, gint row, gint column, GdkEvent *event)
{
	gtk_widget_set_sensitive (g_edit, TRUE);
	gtk_widget_set_sensitive (g_view, TRUE);
	current_row = row;
}

static void
find_do_chdir (void)
{
	gtk_idle_remove (idle_tag);
	idle_tag = 0;
	stop = B_ENTER;
	gtk_main_quit ();
}

static void
find_do_again (void)
{
	gtk_idle_remove (idle_tag);
	idle_tag = 0;
	stop = B_AGAIN;
	gtk_main_quit ();
}

static void
find_do_panelize (void)
{
	gtk_idle_remove (idle_tag);
	idle_tag = 0;
	stop = B_PANELIZE;
	gtk_main_quit ();}


static void
find_start_stop (void)
{

	if (is_start){
		idle_tag = gtk_idle_add ((GtkFunction)do_search, g_find_dlg);
	} else {
		gtk_idle_remove (idle_tag);
		idle_tag = 0;
	}
	
	gtk_label_set_text (GTK_LABEL (g_start_stop_label),
			    is_start ? _("Suspend") : _("Restart"));
	is_start = !is_start;
	status_update (is_start ? _("Stopped") : _("Searching"));
}


static void
find_do_view (void)
{
	char *file, *dir;
	
        get_list_info (&file, &dir);

	find_do_view_edit (0, 0, dir, file);
}

static void
find_do_edit (void)
{
	char *file, *dir;
	
        get_list_info (&file, &dir);

	find_do_view_edit (0, 1, dir, file);
}

static void
setup_gui (void)
{
	GtkWidget *sw, *b1, *b2, *b3;
	GtkWidget *box, *box2;
	
	g_find_dlg = gnome_dialog_new (
		_("Find file"),
		GNOME_STOCK_BUTTON_OK,
		NULL);

	/* The buttons */
	b1 = gtk_button_new_with_label (_("Change to this directory"));
	b2 = gtk_button_new_with_label (_("Search again"));
	g_start_stop_label = gtk_label_new (_("Suspend"));
	g_start_stop = gtk_button_new ();
	gtk_container_add (GTK_CONTAINER (g_start_stop), g_start_stop_label);
	
	g_view = gtk_button_new_with_label (_("View this file"));
	g_edit = gtk_button_new_with_label (_("Edit this file"));
	g_panelize = gtk_button_new_with_label (_("Send the results to a Panel"));
	
	box = gtk_hbox_new (TRUE, GNOME_PAD);
	gtk_box_pack_start (GTK_BOX (box), b1, 0, 1, 0);
	gtk_box_pack_start (GTK_BOX (box), b2, 0, 1, 0);
	gtk_box_pack_start (GTK_BOX (box), g_start_stop, 0, 1, 0);
	
/*	RECOONECT	_("Panelize contents"), */
/*		_("View"),
		_("Edit"), */
	
	sw = gtk_scrolled_window_new (NULL, NULL);
	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
					GTK_POLICY_NEVER,
					GTK_POLICY_AUTOMATIC);
	g_clist = gtk_clist_new (1);
	gtk_clist_set_selection_mode (GTK_CLIST (g_clist), GTK_SELECTION_SINGLE);
	gtk_widget_set_usize (g_clist, -1, 200);
	gtk_container_add (GTK_CONTAINER (sw), g_clist);
	gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (g_find_dlg)->vbox),
			    sw, TRUE, TRUE, GNOME_PAD_SMALL);

	current_row = -1;
	stop = 0;
	gtk_signal_connect (GTK_OBJECT (g_clist), "select_row",
			    GTK_SIGNAL_FUNC (select_row), NULL);

	/*
	 * Connect the buttons
	 */
	gtk_signal_connect (
		GTK_OBJECT (b1), "clicked", GTK_SIGNAL_FUNC (find_do_chdir), NULL);
	gtk_signal_connect (
		GTK_OBJECT (b2), "clicked", GTK_SIGNAL_FUNC (find_do_again), NULL);
	gtk_signal_connect (
		GTK_OBJECT (g_start_stop), "clicked", GTK_SIGNAL_FUNC (find_start_stop), NULL);
	gtk_signal_connect (
		GTK_OBJECT (g_panelize), "clicked", GTK_SIGNAL_FUNC (find_do_panelize), NULL);

	/*
	 * View/edit buttons
	 */
	gtk_signal_connect (
		GTK_OBJECT (g_view), "clicked", GTK_SIGNAL_FUNC (find_do_view), NULL);
	gtk_signal_connect (
		GTK_OBJECT (g_edit), "clicked", GTK_SIGNAL_FUNC (find_do_edit), NULL);

	gtk_widget_set_sensitive (g_view, FALSE);
	gtk_widget_set_sensitive (g_edit, FALSE);
	box2 = gtk_hbox_new (1, GNOME_PAD + GNOME_PAD);
	gtk_box_pack_start (GTK_BOX (box2), g_view, 0, 0, 0);
	gtk_box_pack_start (GTK_BOX (box2), g_edit, 0, 0, 0);

	gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (g_find_dlg)->vbox),
			    box, TRUE, TRUE, GNOME_PAD_SMALL);
	gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (g_find_dlg)->vbox),
			    box2, TRUE, TRUE, GNOME_PAD_SMALL);
	
	g_status_label = gtk_label_new (_("Searching"));
	gtk_misc_set_alignment (GTK_MISC (g_status_label), 0.0, 0.5);
	gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (g_find_dlg)->vbox),
			    g_status_label, TRUE, TRUE, GNOME_PAD_SMALL);
	
	gtk_widget_show_all (g_find_dlg);
	gtk_widget_hide (GTK_WIDGET (g_view));
	gtk_widget_hide (GTK_WIDGET (g_edit));
}

static int
run_process ()
{
	idle_tag = gtk_idle_add ((GtkFunction)do_search, g_find_dlg);

	gnome_dialog_run (GNOME_DIALOG (g_find_dlg));
	g_start_stop = NULL;
	
	return stop;
}

static void
kill_gui ()
{
	gtk_object_destroy (GTK_OBJECT (g_find_dlg));
}

static void
stop_idle (void *data)
{
	if (g_start_stop)
		gtk_widget_set_sensitive (GTK_WIDGET (g_start_stop), FALSE);
}

static void
status_update (char *text)
{
	gtk_label_set_text (GTK_LABEL (g_status_label), text);
	x_flush_events ();
}

static char *
add_to_list (char *text, void *data)
{
	int row;
	char *texts [1];

	texts [0] = text;
	
	row = gtk_clist_append (GTK_CLIST (g_clist), texts);
	gtk_clist_set_row_data (GTK_CLIST (g_clist), row, data);
#if 1
	if (gtk_clist_row_is_visible (GTK_CLIST (g_clist), row) != GTK_VISIBILITY_FULL)
		gtk_clist_moveto (GTK_CLIST (g_clist), row, 0, 0.5, 0.0);
#endif
	return text;
}

static void
get_list_info (char **file, char **dir)
{
	if (current_row == -1)
		*file = *dir = NULL;
	gtk_clist_get_text (GTK_CLIST (g_clist), current_row, 0, file);
	*dir = gtk_clist_get_row_data (GTK_CLIST (g_clist), current_row);
}
#else

static void
get_list_info (char **file, char **dir)
{
    listbox_get_current (find_list, file, dir);
}

static char *
add_to_list (char *text, void *data)
{
	return listbox_add_item (find_list, 0, 0, text, data);
}

static void
stop_idle (void *data)
{
	set_idle_proc (data, 0);
}

static int
view_edit_currently_selected_file (int unparsed_view, int edit)
{
    WLEntry *entry = find_list->current;
    char *dir;

    if (!entry)
        return MSG_NOT_HANDLED;

    dir = entry->data;

    if (!entry->text || !dir)
	return MSG_NOT_HANDLED;

    find_do_view_edit (unparsed_view, edit, dir, entry->text);
    return MSG_HANDLED;
}

static int
find_callback (struct Dlg_head *h, int id, int Msg)
{
    switch (Msg){
#ifndef HAVE_X    
    case DLG_DRAW:
        common_dialog_repaint (h);
	break;
#endif

    case DLG_KEY:
	if (id == KEY_F(3) || id == KEY_F(13)){
	    int unparsed_view = (id == KEY_F(13));
	    return view_edit_currently_selected_file (unparsed_view, 0);
	}
	if (id == KEY_F(4)){
	    return view_edit_currently_selected_file (0, 1);
	 }
	 return MSG_NOT_HANDLED;

     case DLG_IDLE:
	 do_search (h);
	 break;
     }
     return 0;
}

/* Handles the Stop/Start button in the find window */
static int
start_stop (int button, void *extra)
{
    running = is_start;
    set_idle_proc (find_dlg, running);
    is_start = !is_start;

    status_update (is_start ? _("Stopped") : _("Searching"));
    button_set_text (stop_button, fbuts [is_start].text);

    return 0;
}

/* Handle view command, when invoked as a button */
static int
find_do_view_file (int button, void *extra)
{
    view_edit_currently_selected_file (0, 0);
    return 0;
}

/* Handle edit command, when invoked as a button */
static int
find_do_edit_file (int button, void *extra)
{
    view_edit_currently_selected_file (0, 1);
    return 0;
}

static void
setup_gui (void)
{
#ifdef ENABLE_NLS
	static int i18n_flag = 0;
	if (!i18n_flag)
	{
		register int i = sizeof (fbuts) / sizeof (fbuts[0]);
		while (i--)
			fbuts [i].len = strlen (fbuts [i].text = _(fbuts [i].text)) + 3;
		fbuts [2].len += 2; /* DEFPUSH_BUTTON */
		i18n_flag = 1;
	}
#endif /* ENABLE_NLS */

	/*
	 * Dynamically place buttons centered within current window size
	 */
	{
		int l0 = max (fbuts[0].len, fbuts[1].len);
		int l1 = fbuts[2].len + fbuts[3].len + l0 + fbuts[4].len;
		int l2 = fbuts[5].len + fbuts[6].len + fbuts[7].len;
		int r1, r2;
		
		FIND2_X = COLS - 16;

		/* Check, if both button rows fit within FIND2_X */
		if (l1 + 9 > FIND2_X) FIND2_X = l1 + 9;
		if (l2 + 8 > FIND2_X) FIND2_X = l2 + 8;

		/* compute amount of space between buttons for each row */
		r1 = (FIND2_X - 4 - l1) % 5;
		l1 = (FIND2_X - 4 - l1) / 5;
		r2 = (FIND2_X - 4 - l2) % 4;
		l2 = (FIND2_X - 4 - l2) / 4;

		/* ...and finally, place buttons */
		fbuts [2].x = 2 + r1/2 + l1;
		fbuts [3].x = fbuts [2].x + fbuts [2].len + l1;
		fbuts [0].x = fbuts [3].x + fbuts [3].len + l1;
		fbuts [4].x = fbuts [0].x + l0 + l1;
		fbuts [5].x = 2 + r2/2 + l2;
		fbuts [6].x = fbuts [5].x + fbuts [5].len + l2;
		fbuts [7].x = fbuts [6].x + fbuts [6].len + l2;
	}
    
    find_dlg = create_dlg (0, 0, FIND2_Y, FIND2_X, dialog_colors,
			   find_callback, "[Find File]", "mfind", DLG_CENTER | DLG_GRID);
    
    x_set_dialog_title (find_dlg, _("Find file"));

	add_widgetl (find_dlg,
		button_new (FIND2_Y-3, fbuts[7].x, B_VIEW, NORMAL_BUTTON, 
			fbuts[7].text, find_do_edit_file, find_dlg, "button-edit"), 0);
	add_widgetl (find_dlg,
		button_new (FIND2_Y-3, fbuts[6].x, B_VIEW, NORMAL_BUTTON,
			fbuts[6].text, find_do_view_file, find_dlg, "button-view"), 0);
	add_widgetl (find_dlg,
		button_new (FIND2_Y-3, fbuts[5].x, B_PANELIZE, NORMAL_BUTTON,
			fbuts[5].text, 0, 0, "button-panelize"), XV_WLAY_CENTERROW);

	add_widgetl (find_dlg,
		button_new (FIND2_Y-4, fbuts[4].x, B_CANCEL, NORMAL_BUTTON,
			fbuts[4].text, 0, 0, "button-quit"), XV_WLAY_RIGHTOF);
    stop_button = button_new (FIND2_Y-4, fbuts[0].x, B_STOP, NORMAL_BUTTON,
		fbuts[0].text, start_stop, find_dlg, "start-stop");
	add_widgetl (find_dlg, stop_button, XV_WLAY_RIGHTOF);
	add_widgetl (find_dlg,
		button_new (FIND2_Y-4, fbuts[3].x, B_AGAIN, NORMAL_BUTTON, 
			fbuts[3].text, 0, 0, "button-again"), XV_WLAY_RIGHTOF);
	add_widgetl (find_dlg, 
		button_new (FIND2_Y-4, fbuts[2].x, B_ENTER, DEFPUSH_BUTTON,
			fbuts[2].text, 0, 0, "button-chdir"), XV_WLAY_CENTERROW);

    status_label = label_new (FIND2_Y-6, 4, _("Searching"), "label-search");
    add_widgetl (find_dlg, status_label, XV_WLAY_BELOWOF);

    find_list = listbox_new (2, 2, FIND2_X-4, FIND2_Y-9, listbox_finish, 0, "listbox");
    add_widgetl (find_dlg, find_list, XV_WLAY_EXTENDWIDTH);
}

static int
run_process (void)
{
    set_idle_proc (find_dlg, 1);
    run_dlg (find_dlg);
    return find_dlg->ret_value;
}

static void
status_update (char *text)
{
	label_set_text (status_label, text);
}

static void
kill_gui (void)
{
    set_idle_proc (find_dlg, 0);
    destroy_dlg (find_dlg);
}
#endif

static int
find_file (char *start_dir, char *pattern, char *content, char **dirname,  char **filename)
{
    int return_value = 0;
    char *dir;
    char *dir_tmp, *file_tmp;

    setup_gui ();
	    
    /* FIXME: Need to cleanup this, this ought to be passed non-globaly */
    find_pattern    = pattern;
    content_pattern = content;
    
    init_find_vars ();
    push_directory (start_dir);

    return_value = run_process ();

    /* Remove all the items in the stack */
    while ((dir = pop_directory ()) != NULL)
	g_free (dir);

    get_list_info (&file_tmp, &dir_tmp);

    if (dir_tmp)
	*dirname  = g_strdup (dir_tmp);
    if (file_tmp)
	*filename = g_strdup (file_tmp);

#ifndef HAVE_GNOME
    if (return_value == B_PANELIZE && *filename){
	int status, link_to_dir, stalled_link;
	int next_free = 0;
	int i;
	struct stat buf;
	WLEntry *entry = find_list->list;
	dir_list *list = &cpanel->dir;
	char *dir, *name;

	for (i = 0; entry && i < find_list->count; entry = entry->next, i++){
	    char *filename;

	    if (content_pattern)
		filename = strchr (entry->text+4, ':')+1;
	    else
		filename = entry->text+4;

	    if (!entry->text || !entry->data)
		continue;
	    dir = entry->data;
	    if (dir [0] == '.' && dir [1] == 0)
		name = g_strdup (filename);
	    else if (dir [0] == '.' && dir [1] == PATH_SEP)
		name = concat_dir_and_file (dir + 2, filename);
	    else
		name = concat_dir_and_file (dir, filename);
	    status = handle_path (list, name, &buf, next_free, &link_to_dir,
	        &stalled_link);
	    if (status == 0) {
		g_free (name);
		continue;
	    }
	    if (status == -1) {
	        g_free (name);
		break;
	    }

	    /* don't add files more than once to the panel */
	    if (content_pattern && next_free > 0){
		if (strcmp (list->list [next_free-1].fname, name) == 0) {
		    g_free (name);
		    continue;
		}
	    }

	    if (!next_free) /* first turn i.e clean old list */
    		panel_clean_dir (cpanel);
	    list->list [next_free].fnamelen = strlen (name);
	    list->list [next_free].fname = name;
	    file_mark (cpanel, next_free, 0);
	    list->list [next_free].f.link_to_dir = link_to_dir;
	    list->list [next_free].f.stalled_link = stalled_link;
	    list->list [next_free].f.dir_size_computed = 0;
	    list->list [next_free].buf = buf;
	    next_free++;
           if (!(next_free & 15))
	       rotate_dash ();
	}
	if (next_free){
	    cpanel->count = next_free;
	    cpanel->is_panelized = 1;
	  /* Done by panel_clean_dir a few lines above 
	    cpanel->dirs_marked = 0;
	    cpanel->marked = 0;
	    cpanel->total = 0;
	    cpanel->top_file = 0;
	    cpanel->selected = 0;*/

	    if (start_dir [0] == PATH_SEP){
		strcpy (cpanel->cwd, PATH_SEP_STR);
		chdir (PATH_SEP_STR);
	    }
	}
    }
#endif

    kill_gui ();
    do_search (0); /* force do_search to release resources */
    if (old_dir){
	g_free (old_dir);
	old_dir = 0;
    }
    return return_value;
}

void
do_find (void)
{
    char *start_dir, *pattern, *content;
    char *filename, *dirname;
    int  v, dir_and_file_set;
    int done = 0;
    
    while (!done){
	if (!find_parameters (&start_dir, &pattern, &content))
	    break;

	dirname = filename = NULL;
	is_start = 0;
	v = find_file (start_dir, pattern, content, &dirname, &filename);
	g_free (start_dir);
	g_free (pattern);
	
	if (v == B_ENTER){
	    if (dirname || filename){
		if (dirname){
		    do_cd (dirname, cd_exact);
		    if (filename)
			try_to_select (cpanel, filename + (content ? 
			   (strchr (filename + 4, ':') - filename + 1) : 4) );
		} else if (filename)
		    do_cd (filename, cd_exact);
		paint_panel (cpanel);
		select_item (cpanel);
	    }
	    if (dirname)  
		g_free (dirname);
	    if (filename) 
		g_free (filename);
	    break;
	}
	if (content) 
	    g_free (content);
	dir_and_file_set = dirname && filename;
	if (dirname) g_free (dirname);
	if (filename) g_free (filename);
	if (v == B_CANCEL)
	    break;
	
	if (v == B_PANELIZE){
	    if (dir_and_file_set){
	        try_to_select (cpanel, NULL);
	        paint_panel (cpanel);
	    }
	    break;
	}
    }
}

