/* Tk panel stuff.
   Copyright (C) 1995, 1997 Miguel de Icaza
   
   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 <string.h>
#include <stdlib.h>		/* atoi */
#include "fs.h"
#include "dir.h"
#include "tkmain.h"
#include "panel.h"
#include "command.h"
#include "panel.h"		/* current_panel */
#include "command.h"		/* cmdline */
#include "main.h"
#include "mouse.h"
#include "layout.h"		/* get_panel_widget */
#include "ext.h"		/* regex_command */
#include "cmd.h"		/* copy_cmd, ren_cmd, delete_cmd, ... */
#include "tkscreen.h"

/* These two variables keep track of the dimensions of a character in the
 * panel listing.
 */
static int panel_font_width;
static int panel_font_height;

static char *attrib;

void
repaint_file (WPanel *panel, int file_index, int move, int attr, int isstatus)
{
}

void
show_dir (WPanel *panel)
{
    char *tkw = wtk_win (panel->widget);
    char *filter = panel->filter ? panel->filter : "*";

    if (!panel->widget.wdata)
	    return;
    
    tk_evalf ("%s.cwd configure -text {%s [%s]}", tkw, panel->cwd, filter);
}

void
x_fill_panel (WPanel *panel)
{
    const int top = panel->count;
    const int panel_width = panel->widget.cols - 2;
    char  *panel_name, *tag;
    char  buffer [255];
    int   i;
    int   selected;
    if (!panel->widget.wcontainer)
	return;

    selected = panel->selected;
    
    panel_name = copy_strings (wtk_win (panel->widget), ".m.p.panel", 0);

    tk_evalf ("%s delete 0.0 end", panel_name);
    for (i = 0; i < top; i++){
	file_entry *fe = &panel->dir.list [i];
	int marked = fe->f.marked;
	
	format_file (buffer, panel, i, panel_width, NORMAL, 0);

	if (i)
	    tk_evalf ("%s insert end \\n", panel_name);

	
	/* Set the mark tag name -- this probably should go into generic code */
	if (marked)
	    tag = "marked";
	else {
	    tag = "regular";
	    if (S_ISLNK (fe->buf.st_mode)){
		if (fe->f.link_to_dir)
		    tag = "directory";
	    } else if (S_ISDIR (fe->buf.st_mode))
		tag = "directory";
	    else if (is_exe (fe->buf.st_mode))
		tag = "executable";
	}
	tk_evalf ("%s insert end {%s} %s", panel_name, buffer, tag);
    }
    if (panel->active)
	x_select_item (panel);
#if 0
    /* The first time, the panel does not yet have a command */
    if (panel->widget.wdata)
	x_select_item (panel);
    free (panel_name);
#endif
}

/* Adjusts a panel size */
static void
tk_panel_set_size (int index, int boot)
{
    Widget *w;
    WPanel *p;
    char *pn;
    int cols, lines;
    
    w = (Widget *) get_panel_widget (index);
    
    tk_evalf ("panel_info cols");
    cols = atoi (interp->result);
    
    tk_evalf ("panel_info lines");
    lines = atoi (interp->result);

    if ((lines < 10) || (cols < 3))
	return;
    
    w = get_panel_widget (index);
    p = (WPanel *) w;
    
    w->cols = cols;
    w->lines = lines;

    set_panel_formats (p);
    paint_panel (p);
    /* FIXME: paint_panel() includes calls to paint_frame() & x_fill_panel(). */
    /* Why does we call them 2 times ? Timur */
    if (!boot)
	    paint_frame (p);
    x_fill_panel (p);
}

/* Called by panel bootstrap routine */
void
x_panel_set_size (int index)
{
    tk_panel_set_size (index, 1);
}

/* Called at runtime when the size of the window changes */
static void
x_change_size (char *panel, int boot)
{
    if (strncmp (panel, ".left", 5) == 0)
	tk_panel_set_size (0, 0);
    else
	tk_panel_set_size (1, 0);
}

#if TCL_MAJOR_VERSION > 7
/* Tcl 8.xx support */
static void
compute_font_size (char *font, char *win, char *dest)
{
    Tk_Window window_id;
    Tk_Font tkfont;
    Tk_FontMetrics fmPtr;
    int width, height;
    
    window_id = Tk_NameToWindow (interp, win, Tk_MainWindow (interp));
    if (window_id == NULL) {
        fprintf (stderr, "Error: %s\n\r", interp->result);
        exit (1);
    }
    tkfont = Tk_GetFont (interp, window_id, font);
    if (tkfont == NULL) {
        fprintf (stderr, 
            "This should not happend: %s\n", interp->result);
        exit (1);
    }
    Tk_GetFontMetrics (tkfont, &fmPtr);

    width = Tk_TextWidth (tkfont, "O", 1);
    height = fmPtr.linespace;
    Tk_FreeFont (tkfont);

    sprintf (dest, "%d %d", height, width);
}

#else

#ifdef HAVE_TKFONT
/* PreTcl 8.xx support */
#define GetFontMetrics(tkfont)    \
                ((CONST TkFontMetrics *) &((TkFont *) (tkfont))->fm)

static void
compute_font_size (char *font, char *win, char *dest)
{
    Tk_Window window_id;
    Tk_Font tkfont;
    const TkFontMetrics *fmPtr;
    int width, height;
    
    window_id = Tk_NameToWindow (interp, win, Tk_MainWindow (interp));
    if (window_id == NULL) {
        fprintf (stderr, "Error: %s\n\r", interp->result);
        exit (1);
    }
    tkfont = Tk_GetFont (interp, window_id, font);
    if (tkfont == NULL) {
        fprintf (stderr, 
            "This should not happend: %s\n", interp->result);
        exit (1);
    }
    fmPtr = GetFontMetrics (tkfont);

    width = fmPtr->maxWidth;
    height = fmPtr->ascent + fmPtr->descent;
    Tk_FreeFont (tkfont);

    sprintf (dest, "%d %d", height, width);
}

#else
static char
compute_font_size (char *font, char *win, char *dest)
{
    Tk_Uid    font_uid;
    Tk_Window window_id;
    XFontStruct *f;
    int width, height;
    
    font_uid = Tk_GetUid (font);
    window_id = Tk_NameToWindow (interp, win, Tk_MainWindow (interp));
    if (window_id == NULL){
	fprintf (stderr, "Error: %s\n\r", interp->result);
	exit (1);
    }
    f = Tk_GetFontStruct (interp, window_id, font_uid);

    if (f == NULL){
	fprintf (stderr, "This should not happend: %s\n", interp->result);
	exit (1);
    }

    width = f->max_bounds.width;
    height = f->ascent + f->descent;

    /* Ok, we will use this dummy until monday, the correct thing
     * to do is extract the Tk font information from Tk's hash
     * table (tkFont.c).
     *
     * TkFont->widths ['0'] holds the width
     */
    sprintf (dest, "%d %d", height, width);
    Tk_FreeFontStruct (f);
}
#endif
#endif

/*
 * This routine is called when the user has chosen an action from the popup 
 * menu. Handles the Copy, Move and Delete commands internally, anything 
 * else ends in the regex_command routine.
 */
static void
tk_invoke (WPanel *panel, int idx, char *operation)
{
    char *filename = panel->dir.list [idx].fname;
    int  movedir;
    
    if (STREQ (operation, "Copy"))
	copy_cmd ();
    else if (STREQ (operation, "Move"))
	ren_cmd ();
    else if (STREQ (operation, "Delete"))
	delete_cmd ();
    else {
	regex_command (filename, operation, NULL, &movedir);
    }
}

static void
tk_add_popup_entry (char *option, char *cmd, int idx)
{
    tk_evalf (".m add command -label {%s} -command {%s invoke %d {%s}}", option, cmd, idx, option);
}

/* This routine assumes the Tcl code has already created the .m menu */
static void
tk_load_popup (WPanel *panel, char *cmd, int idx)
{
    char *filename = panel->dir.list [idx].fname;
    char *p, *q;
    int c;

    tk_evalf ("popup_add_action {%s} %s %d", filename, cmd, idx);
    
    p = regex_command (filename, NULL, NULL, NULL);
    if (!p)
	return;
    tk_evalf (".m add separator");
    for (;;){
	while (*p == ' ' || *p == '\t')
	    p++;
	if (!*p)
	    break;
	q = p;
	while (*q && *q != '=' && *q != '\t')
	    q++;
	c = *q;
	*q = 0;
	tk_add_popup_entry (p, cmd, idx);
	if (!c)
	    break;
	p = q + 1;
    }
}

void
tk_set_sort (WPanel *panel, char *name)
{
    sortfn *f;

    if (!name || (f = get_sort_fn (name)) == NULL)
	return;
    
    if (!name)
	return;
    set_sort_to (panel, f);
}

void
tk_return_drag_text (WPanel *panel)
{
    if (panel->marked){
	sprintf (interp->result, "%d file%s", panel->marked,
		 panel->marked== 1 ? "" : "s");
    } else
	strcpy (interp->result, "1 file");
}

int
tk_panel_callback (ClientData cd, Tcl_Interp *interp, int ac, char *av[])
{
    Gpm_Event e;
    WPanel *panel = (WPanel *) cd;
    char b [20];
    char *p;
    int  mouse_etype = 0;
    
    p = av [1];
    
    if (STREQ (p, "mdown"))
	mouse_etype = GPM_DOWN;
    else if (STREQ (p, "mup"))
	mouse_etype = GPM_UP;
    else if (STREQ (p, "double"))
	mouse_etype = GPM_DOUBLE|GPM_UP;
    else if (STREQ (p,  "motion"))
	mouse_etype = GPM_MOVE|GPM_DRAG;
    else if (STREQ (p, "resize")){
	x_change_size (av [2], 0);
	return TCL_OK;
    } else if (STREQ (p, "fontdim")){
	compute_font_size (av [2], av [3], interp->result);
	return TCL_OK;
    } else if (STREQ (p, "load")){
	tk_load_popup (panel, av [0], atoi (av [2])-1);
	tk_evalf ("tk_popup .m %s %s", av [3], av [4]);
	return TCL_OK;
    } else if (STREQ (p, "invoke")){
	tk_invoke (panel, atoi (av [2]), av [3]);
	return TCL_OK;
    } else if (STREQ (p, "refresh")){
	reread_cmd ();
	return TCL_OK;
    } else if (STREQ (p, "setmask")){
	filter_cmd ();
	return TCL_OK;
    } else if (STREQ (p, "nomask")){
	if (panel->filter){
	    free (panel->filter);
	    panel->filter = 0;
	}
	reread_cmd ();
	return TCL_OK;
    } else if (STREQ (p, "sort")) {
	tk_set_sort (panel, av [2]);
	return TCL_OK;
    } else if (STREQ (p, "reverse")){
	panel->reverse = !panel->reverse;
	do_re_sort (panel);
	return TCL_OK;
    } else if (STREQ (p, "dragtext")){
	tk_return_drag_text (panel);
	return TCL_OK;
    } 

    if (STREQ (p, "top")){
	int old_sel = panel->selected;
	int new = old_sel;
	
	panel->top_file = atoi (av [2]) - 1;
	if (old_sel < panel->top_file)
	    new = panel->top_file;
	if (old_sel > (panel->top_file + ITEMS (panel)))
	    new = panel->top_file + ITEMS (panel) - 1;

	if (new != old_sel){
	    unselect_item (panel);
	    panel->selected = new;
	    select_item (panel);
	}
	return TCL_OK;
    }
    
    if (mouse_etype){
	for (p = av [3]; *p && *p != '.'; p++)
	    ;
	*p++ = 0;
	e.buttons = atoi (av [2]);
    	e.y = atoi (av [3]) + 2 - panel->top_file;
	e.x = atoi (p);
	e.type = mouse_etype;
	panel_event (&e, panel);
    } else {
	/* We are currently dealing with those */
	fprintf (stderr, "Unknown command: %s\n", p);
	return TCL_ERROR;
    }
    return TCL_OK;
}

void
x_create_panel (Dlg_head *h, widget_data parent, WPanel *panel)
{
    char *cmd;
    widget_data container = panel->widget.wcontainer;
    
    cmd = tk_new_command  (container, panel, tk_panel_callback, 'p');
    tk_evalf ("panel_setup %s %s", wtk_win (panel->widget), cmd);
    tk_evalf ("panel_bind %s %s", wtk_win (panel->widget), cmd);
    paint_frame (panel);
    x_fill_panel (panel);
}

/* Called when f.marked has changed on a file */
void
x_panel_select_item (WPanel *panel, int index, int value)
{
    char *s;

    s = value ? "add" : "remove";
    tk_evalf ("panel_mark_entry %s %s %d", wtk_win(panel->widget), s, index+1);
}

void
x_unselect_item (WPanel *panel)
{
    const int selected = panel->selected + 1;

    if (panel->active)
	tk_evalf ("panel_unmark_entry %s %d\n", wtk_win (panel->widget), selected);
}

void
x_select_item (WPanel *panel)
{
    const int selected = panel->selected + 1;
    char *cmd = wtcl_cmd (panel->widget);

    if (!(cmd && *cmd))
	return;

    if (panel->active)
	tk_evalf ("panel_select %s %d %s", wtk_win (panel->widget),
		  selected, cmd);
}

void
x_adjust_top_file (WPanel *panel)
{
    tk_evalf ("%s.m.p.panel yview %d.0", wtk_win (panel->widget),
         panel->top_file+1);
}

void
x_filter_changed (WPanel *panel)
{
    show_dir (panel);
}

/* The following two routines may be called at the very beginning of the
 * program, so we have to check if the widget has already been constructed
 */
static sort_label_last_pos;

void
x_add_sort_label (WPanel *panel, int index, char *text, char *tag, void *sr)
{
    int pos;
    
    if (!panel->format_modified)
	return;

    if (!panel->widget.wdata)
	return;

    /* We may still not have this information */
    if (!panel_font_width){
	tk_evalf ("panel_info heightc");
	panel_font_height = atoi (interp->result);
	
	tk_evalf ("panel_info widthc");
	panel_font_width = atoi (interp->result);
    }
    
    pos = sort_label_last_pos;
    sort_label_last_pos += (strlen (text) * panel_font_width) + (4);

    /* The addition down there is to account for the canvas border width */
    tk_evalf ("panel_add_sort %s %d {%s} %d %d {%s}", wtk_win(panel->widget),
	      strlen (text), text, pos , sort_label_last_pos, *tag ? tag : "Type");
}

void
x_sort_label_start (WPanel *panel)
{
    if (!panel->widget.wdata)
	return;
    sort_label_last_pos = 0;
    tk_evalf ("panel_sort_label_start %s", wtk_win (panel->widget));
}

void
x_reset_sort_labels (WPanel *panel)
{
    if (!panel->widget.wdata)
	return;
    sort_label_last_pos = 0;
    tk_evalf ("panel_reset_sort_labels %s", wtk_win (panel->widget));
}

void
panel_update_cols (Widget *widget, int frame_size)
{
	/* nothing */
}

void
display_mini_info (WPanel *panel)
{
	/* FIXME: implement */
}

void
x_panel_destroy (WPanel *panel)
{
}