/*
 * Layout routines for the GNOME edition of the GNU Midnight Commander
 *
 * (C) 1998 the Free Software Foundation
 *
 * Author: Miguel de Icaza (miguel@kernel.org)
 */
#include <config.h>
#include "x.h"
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include "dir.h"
#include "panel.h"
#include "gscreen.h"
#include "main.h"
#include "gmain.h"
#include "cmd.h"
#include "dialog.h"
#include "boxes.h"
#include "panelize.h"
#include "gcmd.h"
#include "gcliplabel.h"
#include "gdesktop.h"
#include "setup.h"
#include "../vfs/vfs.h"
#include "gprefs.h"



#define UNDEFINED_INDEX -1

GList *containers = 0;

int output_lines = 0;
int command_prompt = 1;
int keybar_visible = 1;
int message_visible = 1;
int xterm_hintbar = 0;

PanelContainer *current_panel_ptr, *other_panel_ptr;

WPanel *
get_current_panel (void)
{
	if (current_panel_ptr)
		return current_panel_ptr->panel;
	else
		return NULL;
}

WPanel *
get_other_panel (void)
{
	if (other_panel_ptr)
		return other_panel_ptr->panel;
	else
		return NULL;
}

/* FIXME: we probably want to get rid of this code */
int
get_current_index (void)
{
	GList *p;
	int i;
	
	for (i = 0, p = containers; p; p = p->next, i++){
		if (p->data == current_panel_ptr)
			return i;
	}
	printf ("FATAL: current panel is not in the list\n");
	g_assert_not_reached ();
	return -1; /* keep -Wall happy */
}

int
get_other_index (void)
{
	GList *p;
	int i;
	
	for (i = 0, p = containers; p; p = p->next, i++){
		if (p->data == other_panel_ptr)
			return i;
	}
	return UNDEFINED_INDEX;
}

void
set_current_panel (int index)
{
	GList *p;
	
	for (p = containers; index; p = p->next)
		index--;
	current_panel_ptr = p->data; 
}

void
set_new_current_panel (WPanel *panel)
{
	GList *p;

	if (g_list_length (containers) > 1)
		other_panel_ptr = current_panel_ptr;
	
	for (p = containers; p; p = p->next){
		if (((PanelContainer *)p->data)->panel == panel){
			current_panel_ptr = p->data;
		}
	}
}

/*
 * Tries to assign other_panel (ie, if there is anything to assign to
 */
static void
assign_other (void)
{
	GList *p;

	other_panel_ptr = NULL;
	for (p = containers; p; p = p->next)
		if (p->data != current_panel_ptr){
			other_panel_ptr = p->data;
			printf ("PANEL: Found another other\n");
			break;
		}
}

/*
 * This keeps track of the current_panel_ptr and other_panel_ptr as
 * well as the list of active containers
 */
void
layout_panel_gone (WPanel *panel)
{
	PanelContainer *pc_holder = 0;
	int len = g_list_length (containers);
	GList *p;
	
	for (p = containers; p; p = p->next){
		PanelContainer *pc = p->data;

		if (pc->panel == panel){
			pc_holder = pc;
			break;
		}
	}

	if (len > 1){
		containers = g_list_remove (containers, pc_holder);
	}

	/* Check if this is not the current panel */
	if (current_panel_ptr->panel == panel){
		if (other_panel_ptr){
			current_panel_ptr = other_panel_ptr;
			assign_other ();
		} else {
			current_panel_ptr = NULL;
		}
	} else if (other_panel_ptr->panel == panel){
	        /* Check if it was the other panel */
		if (len == 1){
			other_panel_ptr = 0;
		} else 
			assign_other ();
	} else {
	}

	if (len == 1){
		g_free (containers->data);
		g_list_free (containers);
		containers = NULL;
	} else
		g_free (pc_holder);
}

void
set_hintbar (char *str)
{
  /*gtk_label_set (GTK_LABEL (current_panel_ptr->panel->status), str);*/
	x_flush_events ();
}

void
print_vfs_message (char *msg, ...)
{
	va_list ap;
	char str [256];
	
	va_start(ap, msg);
	vsprintf(str, msg, ap);
	va_end(ap);
	if (midnight_shutdown)
		return;
	
	set_hintbar(str);
}

void
rotate_dash (void)
{
}

int
get_current_type (void)
{
	return view_listing;
}


int
get_other_type (void)
{
	return other_panel_ptr ? view_listing : view_nothing;
}

int
get_display_type (int index)
{
	GList *p;

	if (index == UNDEFINED_INDEX)
		return -1;
	
	p = g_list_nth (containers, index);
	if (p)
		return ((PanelContainer *)p->data)->panel->list_type;
	else
		return -1;
}

void
use_dash (int ignore)
{
	/* we dont care in the gnome edition */
}

Widget *
get_panel_widget (int index)
{
	GList *p;
	
	for (p = containers; index; p = p->next)
		index--;
	return (Widget *) ((PanelContainer *)p->data)->panel;
}

/* FIXME: This routine is wrong.  It should follow what the original save_panel_types
 * does.  I can not remember which problem the original routine was trying to address
 * when I did the "New {Left|Rigth} Panel" sections.
 */
void
save_panel_types (void)
{
	GList *p;
	
	for (p = containers; p; p = p->next){
		PanelContainer *pc = p->data;

		panel_save_setup (pc->panel, pc->panel->panel_name);
	}
}

static void
run_cmd (void)
{
	char *cmd;

	cmd = input_dialog (_("Enter command to run"), _("Enter command to run"), "");
	if (cmd && *cmd){
		my_system (EXECUTE_AS_SHELL, shell, cmd);
	}
}

void configure_box (void);

GtkCheckMenuItem *gnome_toggle_snap (void);
GnomeUIInfo gnome_panel_new_menu [] = {
	{ GNOME_APP_UI_ITEM, N_("_Terminal"), N_("Launch a new terminal in the current directory"), gnome_open_terminal},
	/* If this ever changes, make sure you update create_new_menu accordingly. */
	{ GNOME_APP_UI_ITEM, N_("_Directory..."), N_("Creates a new directory"), gnome_mkdir_cmd },
	{ GNOME_APP_UI_ENDOFINFO, 0, 0 }
};


GnomeUIInfo gnome_panel_file_menu [] = {
	GNOMEUIINFO_MENU_NEW_ITEM(N_("New _Window"), N_("Opens a new window"), gnome_open_panel, NULL),
	
	/* We want to make a new menu entry here... */
	/* For example: */
	/* New-> */
	/*  Command Prompt */
	/*  Gimp Image */
	/*  Gnumeric Spreadsheet */
	/*  Text Document */
	/*  etc... */
	{ GNOME_APP_UI_SUBTREE, N_("_New"), 	       NULL, gnome_panel_new_menu},
	{ GNOME_APP_UI_SEPARATOR },
	{ GNOME_APP_UI_ITEM, N_("_Open"),              N_("Opens the selected files"), gnome_open_files },
/*	{ GNOME_APP_UI_ITEM, N_("Open _FTP site"),     N_("Opens an FTP site"), ftplink_cmd },*/
	{ GNOME_APP_UI_ITEM, N_("_Copy..."),           N_("Copy files"), copy_cmd, NULL},
	{ GNOME_APP_UI_ITEM, N_("_Delete..."),         N_("Delete files from disk"), delete_cmd, NULL, NULL, 0, NULL, 'd', GDK_CONTROL_MASK},
	{ GNOME_APP_UI_ITEM, N_("_Move..."),           N_("Rename or move files"), ren_cmd },
	{ GNOME_APP_UI_SEPARATOR },
	{ GNOME_APP_UI_ITEM, N_("C_lose"),             N_("Close this panel"), gnome_close_panel, NULL,
	  NULL, GNOME_APP_PIXMAP_STOCK, GNOME_STOCK_MENU_CLOSE,
	  GNOME_KEY_NAME_CLOSE, GNOME_KEY_MOD_CLOSE, NULL },
	{ GNOME_APP_UI_ENDOFINFO, 0, 0 }
};

GnomeUIInfo gnome_panel_edit_menu [] = {
	{ GNOME_APP_UI_ITEM, N_("Select _All"),        N_("Select all files in the current Panel"), gnome_select_all_cmd, NULL, NULL, 0, NULL, 'a', GDK_CONTROL_MASK  },
	{ GNOME_APP_UI_ITEM, N_("_Select Files..."),   N_("Select a group of files"), select_cmd },
	{ GNOME_APP_UI_ITEM, N_("_Invert Selection"),  N_("Reverses the list of tagged files"), reverse_selection_cmd },
	{ GNOME_APP_UI_SEPARATOR },
	{ GNOME_APP_UI_ITEM, N_("_Rescan Directory"),  N_("Rescan the directory contents"), reread_cmd },
	{ GNOME_APP_UI_SEPARATOR },
	GNOMEUIINFO_MENU_PREFERENCES_ITEM(gnome_configure_box, NULL),
	{ GNOME_APP_UI_ENDOFINFO, 0, 0 }
};

GnomeUIInfo gnome_panel_view_menu [] = {
	{ GNOME_APP_UI_ITEM, N_("Icon View"), NULL, gnome_icon_view_cmd, NULL},
	{ GNOME_APP_UI_ITEM, N_("Partial View"), NULL, gnome_partial_view_cmd, NULL},
	{ GNOME_APP_UI_ITEM, N_("Full View"), NULL, gnome_full_view_cmd, NULL},
	{ GNOME_APP_UI_ITEM, N_("Custom View"), NULL, gnome_custom_view_cmd, NULL},
	{GNOME_APP_UI_ENDOFINFO, NULL, NULL, NULL, NULL, NULL,
	 GNOME_APP_PIXMAP_NONE, NULL, 0, 0, NULL}
};

GnomeUIInfo gnome_panel_layout_menu [] = {
	{ GNOME_APP_UI_ITEM, N_("_Sort By..."),     N_("Confirmation settings"), gnome_sort_cmd },
	{ GNOME_APP_UI_ITEM, N_("_Filter View..."),    N_("Global option settings"), gnome_filter_cmd },
	{ GNOME_APP_UI_SEPARATOR },
	{ GNOME_APP_UI_RADIOITEMS, NULL , NULL, gnome_panel_view_menu},
	{ GNOME_APP_UI_ENDOFINFO, 0, 0 }
};

GnomeUIInfo gnome_panel_commands_menu [] = {
	{ GNOME_APP_UI_ITEM, N_("_Find File..."),      N_("Locate files on disk"),   find_cmd, NULL,
	  NULL, GNOME_APP_PIXMAP_STOCK, GNOME_STOCK_MENU_JUMP_TO},
	  
/*	{ GNOME_APP_UI_ITEM, N_("_Compare panels..."), N_("Compare two panel contents"), gnome_compare_panels },*/
	{ GNOME_APP_UI_ITEM, N_("_Run Command..."),               N_("Runs a command"), run_cmd, NULL,
	  NULL, GNOME_APP_PIXMAP_STOCK, GNOME_STOCK_MENU_OPEN, GDK_F2, 0 },
	{ GNOME_APP_UI_ITEM, N_("_Run Command in panel..."),N_("Run a command and put the results in a panel"), gnome_external_panelize },

#ifdef USE_VFS					  
	{ GNOME_APP_UI_ITEM, N_("_Active VFS list..."),N_("List of active virtual file systems"), reselect_vfs },
#endif
#ifdef USE_EXT2FSLIB
	/*does this do anything?*/
/*	{ GNOME_APP_UI_ITEM, N_("_Undelete files (ext2fs only)..."), N_("Recover deleted files"), undelete_cmd },*/
#endif
#ifdef WITH_BACKGROUND
	{ GNOME_APP_UI_ITEM, N_("_Background jobs..."),   N_("List of background operations"), jobs_cmd },
#endif
	{ GNOME_APP_UI_ENDOFINFO, 0, 0 }
};


GnomeUIInfo gnome_panel_about_menu [] = {
/*	GNOMEUIINFO_HELP ("midnight-commander"), */
	{ GNOME_APP_UI_ITEM, N_("_About"),              N_("Information on this program"), gnome_about_cmd, NULL, NULL,
	  GNOME_APP_PIXMAP_STOCK, GNOME_STOCK_MENU_ABOUT },
	GNOMEUIINFO_END
};

GnomeUIInfo gnome_panel_menu [] = {
	{ GNOME_APP_UI_SUBTREE, N_("_File"),     NULL, &gnome_panel_file_menu },
	{ GNOME_APP_UI_SUBTREE, N_("_Edit"),     NULL, &gnome_panel_edit_menu },
	{ GNOME_APP_UI_SUBTREE, N_("_Layout"),   NULL, &gnome_panel_layout_menu },
	{ GNOME_APP_UI_SUBTREE, N_("_Commands"), NULL, &gnome_panel_commands_menu },
	{ GNOME_APP_UI_SUBTREE, N_("_Help"),     NULL, &gnome_panel_about_menu },
	{ GNOME_APP_UI_ENDOFINFO, 0, 0 }
};

GtkCheckMenuItem *
gnome_toggle_snap (void)
{
	return NULL; /*GTK_CHECK_MENU_ITEM (gnome_panel_desktop_menu [1].widget);*/
}

void
gnome_init_panels (void)
{
	current_panel_ptr = NULL;
	other_panel_ptr = NULL;
}

static void
gnome_close_panel_event (GtkWidget *widget, GdkEvent *event, WPanel *panel)
{
	gnome_close_panel (widget, panel);
}

static void
panel_enter_event (GtkWidget *widget, GdkEvent *event, WPanel *panel)
{
	/* Avoid unnecessary code execution */
	if (get_current_panel () == panel)
		return;
	
	set_new_current_panel (panel);
	dlg_select_widget (panel->widget.parent, panel);
	send_message (panel->widget.parent, (Widget *) panel, WIDGET_FOCUS, 0);
}

struct _TbItems {
	char *key, *text, *tooltip, *icon;
	void (*cb) (GtkWidget *, void *);
	GtkWidget *widget; /* will be filled in */
};
typedef struct _TbItems TbItems;

void user_menu_cmd (void);

static TbItems tb_items[] =
{
/* 1Help   2Menu   3View   4Edit   5Copy   6RenMov 7Mkdir  8Delete 9PullDn 10Quit */
    {"F1", "Help", "Interactive help browser", GNOME_STOCK_MENU_BLANK, (void (*) (GtkWidget *, void *)) help_cmd, 0},
    {"F2", "Menu", "User actions", GNOME_STOCK_MENU_BLANK, (void (*) (GtkWidget *, void *)) user_menu_cmd, 0},
    {"F3", "View", "View file", GNOME_STOCK_MENU_BLANK, (void (*) (GtkWidget *, void *)) view_panel_cmd, 0},
    {"F4", "Edit", "Edit file", GNOME_STOCK_MENU_BLANK, (void (*) (GtkWidget *, void *)) edit_panel_cmd, 0},
    {"F5", "Copy", "Copy file or directory", GNOME_STOCK_MENU_COPY, (void (*) (GtkWidget *, void *)) copy_cmd, 0},
    {"F6", "Move", "Rename or move a file or directory", GNOME_STOCK_MENU_BLANK, (void (*) (GtkWidget *, void *)) ren_cmd, 0},
    {"F7", "Mkdir", "Create directory", GNOME_STOCK_MENU_BLANK, (void (*) (GtkWidget *, void *)) mkdir_panel_cmd, 0},
    {"F8", "Dlete", "Delete file or directory", GNOME_STOCK_MENU_BLANK, (void (*) (GtkWidget *, void *)) delete_cmd, 0},
    {"F9", "Menu", "Pull down menu", GNOME_STOCK_MENU_BLANK, (void (*) (GtkWidget *, void *)) 0, 0},
    {"F10", "Quit", "Close and exit", GNOME_STOCK_MENU_QUIT, (void (*) (GtkWidget *, void *)) 0, 0},
    {0, 0, 0, 0, 0, 0}
};

static GtkWidget *create_toolbar (GtkWidget * window, GtkWidget *widget)
{
	GtkWidget *toolbar;
	TbItems *t;
	
	toolbar = gtk_toolbar_new (GTK_ORIENTATION_HORIZONTAL, GTK_TOOLBAR_BOTH);
	
	for (t = &tb_items[0]; t->text; t++){
		t->widget = gtk_toolbar_append_item (GTK_TOOLBAR (toolbar),
						     t->text,
					     t->tooltip,
						     0,
						     gnome_stock_pixmap_widget (window, t->icon),
						     t->cb,
						     t->cb ? widget : 0);
	}
	return toolbar;
}

static void
create_new_menu_from (GnomeApp *app, WPanel *panel, char *file)
{
	gint pos;
	GtkWidget *shell = NULL;
	DIR *dir;
	struct stat filedata;
	struct dirent *dirstruc;
	GnomeDesktopEntry *gde;
	GtkWidget *menu;
	char *file2;
	
	dir = opendir (file);
	if (dir == NULL)
		return;
	
	shell = gnome_app_find_menu_pos (app->menubar, _("File/New/Directory..."), &pos);
	menu = gtk_menu_item_new ();
	gtk_widget_show (menu);
	gtk_menu_shell_insert (GTK_MENU_SHELL (shell), menu, pos++);

	if (shell == NULL){
		closedir (dir);
		return;
	}

	while ((dirstruc = readdir (dir)) != NULL){
		if (dirstruc->d_name[0] == '.')
			continue;

		file2 = g_concat_dir_and_file (file, dirstruc->d_name);

		if ((stat (file2, &filedata) != -1) && (S_ISREG (filedata.st_mode))){
			char *test;
			
			gde = gnome_desktop_entry_load (file2);

			test = rindex(dirstruc->d_name, '.');

			if (test == NULL || gde == NULL || strcmp (test, ".desktop")){
				g_free (file2);
				continue;
			}

			if (!gnome_is_program_in_path (gde->tryexec)){
				g_free (file2);
				g_print ("yes!\n");
				continue;
			}
			
			menu = gtk_menu_item_new_with_label (gde->name);
			gtk_widget_show (menu);
			gtk_menu_shell_insert (GTK_MENU_SHELL (shell), menu, pos++);

			/* This is really bad, but it works. */
			if (gde->comment)
				gtk_object_set_data (GTK_OBJECT (menu), "apphelper_statusbar_hint",
						     gde->comment);
			gtk_signal_connect (GTK_OBJECT (menu), "activate", GTK_SIGNAL_FUNC (gnome_run_new),
					    gde);
		}
		g_free (file2);
	}
}

/**
 * create_new_menu:
 *
 * Creates the child New menu items
 */
static void
create_new_menu (GnomeApp *app, WPanel *panel)
{
	gchar *file, *file2;

	file = gnome_unconditional_datadir_file ("mc/templates");
	create_new_menu_from (app, panel, file);
	
	file2 = gnome_datadir_file ("mc/templates");
	if (file2 != NULL){
		if (strcmp (file, file2) != 0)
			create_new_menu_from (app, panel, file2);
	}
	g_free (file);
	g_free (file2);
}

WPanel *
create_container (Dlg_head *h, char *name, char *geometry)
{
	PanelContainer *container = g_new (PanelContainer, 1);
	WPanel     *panel;
	GtkWidget  *app, *vbox;
	int        xpos, ypos, width, height;

	gnome_parse_geometry (geometry, &xpos, &ypos, &width, &height);
	
	container->splitted = 0;
	app = gnome_app_new ("gmc", name);
	gtk_window_set_wmclass (GTK_WINDOW (app), "gmc", "gmc");

	/* Geometry configuration */
	if (width != -1 && height != -1)
		gtk_widget_set_usize (GTK_WIDGET (app), width, height);
	else
		gtk_widget_set_usize (GTK_WIDGET (app), 540, 360);
	if (xpos != -1 && ypos != -1)
		gtk_widget_set_uposition (GTK_WIDGET (app), xpos, ypos);

	panel = panel_new (name);

	
	vbox = gtk_vbox_new (FALSE, 0);
	gtk_container_set_border_width (GTK_CONTAINER (vbox), 0);
	gnome_app_set_contents (GNOME_APP (app), vbox);
	gnome_app_create_menus_with_data (GNOME_APP (app), gnome_panel_menu, panel);
	create_new_menu (GNOME_APP (app), panel);

	panel->ministatus = GNOME_APPBAR(gnome_appbar_new(FALSE, TRUE, GNOME_PREFERENCES_NEVER));
	gnome_app_set_statusbar(GNOME_APP (app), GTK_WIDGET(panel->ministatus));

	gnome_app_install_menu_hints (GNOME_APP (app), gnome_panel_menu);
	/*
	 * I am trying to unclutter the screen, so this toolbar is gone now
	 */
/*	gnome_app_set_toolbar(GNOME_APP (app), GTK_TOOLBAR(create_toolbar(app, 0))); */

	gtk_signal_connect (GTK_OBJECT (app),
			    "enter_notify_event",
			    GTK_SIGNAL_FUNC (panel_enter_event),
			    panel);
	gtk_signal_connect (GTK_OBJECT (app),
			    "delete_event",
			    GTK_SIGNAL_FUNC (gnome_close_panel_event),
			    panel);
	/* Ultra nasty hack follows:
	 * I am setting the panel->widget.wdata value here before the
	 * panel X stuff gets created in the INIT message section of the
	 * widget.  There I put a pointer to the vbox where the panel
	 * should pack itself
	 */
	panel->widget.wdata = (widget_data) vbox;
	container->panel = panel;
	
	containers = g_list_append (containers, container);

	if (!current_panel_ptr){
		current_panel_ptr = container;
	} else if (!other_panel_ptr)
		other_panel_ptr = container;

	bind_gtk_keys (GTK_WIDGET (app), h);
	return panel;
}

WPanel *
new_panel_with_geometry_at (char *dir, char *geometry)
{
	WPanel *panel;

	mc_chdir (dir);
	panel = create_container (desktop_dlg, dir, geometry);
	add_widget (desktop_dlg, panel);
	set_new_current_panel (panel);
	x_flush_events ();

	return panel;
}

WPanel *
new_panel_at (char *dir)
{
	return new_panel_with_geometry_at (dir, NULL);
}

void
setup_panels (void)
{
	load_hint ();
}

/*
 * GNOME's implementation of the update_panels routine
 */
void
update_panels (int force_update, char *current_file)
{
	int reload_others = !(force_update & UP_ONLY_CURRENT);
	GList *p;

	/* Test if there are panels open */
	if (!cpanel)
		return;
	
	update_one_panel_widget (cpanel, force_update, current_file);

	if (reload_others){
		for (p = containers; p; p = p->next){
			PanelContainer *pc = p->data;
			
			if (p->data == current_panel_ptr)
				continue;
			
			update_one_panel_widget (pc->panel, force_update, UP_KEEPSEL);
		}
	}
	mc_chdir (cpanel->cwd);
}