/* $Id: e2_file_info_dialog.c 863 2008-05-05 22:55:54Z tpgww $

Copyright (C) 2003-2008 tooar <tooar@gmx.net>
Portions copyright (C) 1999 Michael Clark.

This file is part of emelFM2.
emelFM2 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 3, or (at your option)
any later version.

emelFM2 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 emelFM2; see the file GPL. If not, contact the Free Software
Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/

#include "emelfm2.h"
#include <string.h>
#include <unistd.h>
#include <langinfo.h>
#include <time.h>
#include <pwd.h>
#include <grp.h>
#include "e2_dialog.h"

//max length of the file type string
#define FILE_TYPE_LENGTH 128

typedef struct _E2_FileInfoRuntime
{
	GtkWidget *dialog;
	gchar *localpath; //path of item being displayed
	GtkWidget *description;
	GtkWidget *target;
	GtkWidget *size;
	GtkWidget *user;
	GtkWidget *group;
	GtkWidget *permissions;
	GtkWidget *accessdate;
	GtkWidget *moddate;
	GtkWidget *changedate;
} E2_FileInfoRuntime;

/**
@brief get description and mimetype of @a filepath, using the 'file' command
@a readable_type and/or @a mime_type are set to NULL if nothing valid is available
@param filepath localised path string of item to be processed
@param readable_type store for pointer to determined description string
@param mime_type store for pointer to determined mime (and for text files, charset too) string

@return TRUE if both description and mume commands are executed successfully (which happens if file options are bad)
*/
static gboolean _e2_file_info_dialog_get_file_type (VPATH *filepath,
	gchar **readable_type, gchar **mime_type)
{
#ifdef E2_VFS
	if (e2_fs_item_is_mounted (filepath))
	{
#endif
	size_t bsize = 512;
#ifdef USE_GLIB2_10
	gchar *buf = (gchar *) g_slice_alloc ((gulong) bsize);
#else
	gchar *buf = (gchar *) g_try_malloc ((gulong) bsize);
#endif
	CHECKALLOCATEDWARNT (buf, );
	if (buf == NULL)
	{
		*readable_type = NULL;
		*mime_type = NULL;
		return FALSE;
	}
	//CHECKME -b option ok ? -i option to get mimetype
	gchar *command = g_strdup_printf ("file -bhnprs \"%s\"", VPSTR (filepath));
	E2_FILE *pipe = e2_fs_open_pipe (command);
	g_free (command);
	if (pipe == NULL)
	{
		*readable_type = NULL;
		*mime_type = NULL;
		return FALSE;
	}

# ifdef __USE_GNU
	ssize_t bytes_read = getdelim (&buf, &bsize, '\n', pipe);
	if (bytes_read > 1)	//not an empty line
	{
		*(buf+bytes_read-1) = '\0';	//strip trailing \n
# else //getdelim() not available
	gchar *strtmp = fgets (buf, bsize, pipe);
	if (strtmp != NULL && *strtmp != '\n')
	{
		*(buf+strlen(buf)-1) = '\0';
# endif
		*readable_type = g_strdup (buf);
	}
	else
	{
		*readable_type = NULL;	//NOTE this is really an error, but returns TRUE
	}
	e2_fs_pipe_close (pipe);
	//get mimetype (and for text files, encoding type) string
	//we use -i option to get encoding for text files, instead of just --mime-type,
	//because separate usage of  --mime-encoding does not work properly
	command = g_strdup_printf ("file -bhnpri \"%s\"", VPSTR (filepath));
	pipe = e2_fs_open_pipe (command);
	g_free (command);
	if (pipe == NULL)
	{
		if (*readable_type != NULL)
		{
			g_free (*readable_type);
			*readable_type = NULL;
		}
		*mime_type = NULL;
		return FALSE;
	}

# ifdef __USE_GNU
	bytes_read = getdelim (&buf, &bsize, '\n', pipe);
	if (bytes_read > 1)	//not an empty line
	{
		*(buf+bytes_read-1) = '\0';	//strip trailing \n
# else //getdelim() not available
	strtmp = fgets (buf, bsize, pipe);
	if (strtmp != NULL && *strtmp != '\n')
	{
		*(buf+strlen(buf)-1) = '\0';
# endif
		*mime_type = g_strdup (buf);
	}
	else
	{
		*mime_type = NULL;
	}
	e2_fs_pipe_close (pipe);
# ifdef USE_GLIB2_10
	g_slice_free1 (bsize, buf);
# else
	g_free (buf);
# endif
	return TRUE;
#ifdef E2_VFS
	}
	else	//not a local item
	{
# ifdef E2_VFSTMP
		FIXME
# endif
		*readable_type = NULL;
		*mime_type = NULL;
		return FALSE;
	}
#endif
}
/**
@brief set popup menu position

This function is supplied when calling gtk_menu_popup(), to position
the displayed menu.
set @a push_in to TRUE for menu completely inside the screen,
FALSE for menu clamped to screen size

@param menu the GtkMenu to be positioned
@param x	place to store gint representing the menu left
@param y  place to store gint representing the menu top
@param push_in place to store pushin flag
@param dialog the dialog widget in focus when the menu key was pressed

@return
*/
static void _e2_file_info_dialog_set_menu_position (GtkWidget *menu,
	gint *x, gint *y, gboolean *push_in, GtkWidget *dialog)
{
	gint left, top;
	gtk_window_get_position (GTK_WINDOW (dialog), &left, &top);
	GtkAllocation alloc = dialog->allocation;
	*x = left + alloc.x + alloc.width/2;
	*y = top + alloc.y + alloc.height/2;
	*push_in = FALSE;
}
/**
@brief copy dialog data to clipboard

@param menuitem UNUSED the selected widget
@param rt runtime struct to work on

@return
*/
static void _e2_file_info_dialog_copy_cb (GtkWidget *menuitem,
	E2_FileInfoRuntime *rt)
{
	//some format massaging is needed ...
	gchar *utf = F_FILENAME_FROM_LOCALE (rt->localpath);
	const gchar *target;
	if (rt->target != NULL)
		target = gtk_label_get_text (GTK_LABEL(rt->target));
	else
		target = "";
	gchar *typelabel;
	const gchar *type = gtk_label_get_text (GTK_LABEL(rt->description));
	if (g_str_has_prefix (type, _("Type:")))
		typelabel = "";
	else
		typelabel = g_strconcat (_("Type:"), " ", NULL);

	GString *text = g_string_sized_new (512);
	g_string_printf (text,
		"%s %s\n" "%s%s%s\n" "%s %s\n" "%s %s\n""%s %s\n" "%s %s\n" "%s %s\n" "%s %s\n" "%s %s\n",
		_("Item:"), utf,
		typelabel, type, target,
		_("Size:"), gtk_label_get_text (GTK_LABEL(rt->size)),
		_("User:"), gtk_label_get_text (GTK_LABEL(rt->user)),
		_("Group:"), gtk_label_get_text (GTK_LABEL(rt->group)),
		_("Permissions:"), gtk_label_get_text (GTK_LABEL(rt->permissions)),
		_("Accessed:"), gtk_label_get_text (GTK_LABEL(rt->accessdate)),
		_("Modified:"), gtk_label_get_text (GTK_LABEL(rt->moddate)),
		_("Changed:"), gtk_label_get_text (GTK_LABEL(rt->changedate))
	);
	GtkClipboard *cb = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
	gtk_clipboard_set_text (cb, text->str, text->len);
	F_FREE (utf);
	if (*typelabel != '\0')
		g_free (typelabel);
	g_string_free (text, TRUE);
}
/**
@brief construct and show dialog context menu

@param textview the textview widget where the click happened
@param event_button which mouse button was clicked (0 for a menu key)
@param event_time time that the event happened (0 for a menu key)
@param rt runtime struct to work on

@return
*/
static void _e2_file_info_dialog_show_context_menu (GtkWidget *dialog,
	guint event_button, gint event_time, E2_FileInfoRuntime *rt)
{
	GtkWidget *menu = gtk_menu_new ();
	e2_menu_add (menu, _("_Copy"), GTK_STOCK_COPY,
		_("Copy displayed data"), _e2_file_info_dialog_copy_cb, rt);
	g_signal_connect (G_OBJECT (menu), "selection-done",
		G_CALLBACK (e2_menu_destroy_cb), NULL);
	if (event_button == 0)
		gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
			(GtkMenuPositionFunc) _e2_file_info_dialog_set_menu_position,
			rt->dialog, event_button, event_time);
	else
		//this was a button-3 click
		gtk_menu_popup (GTK_MENU (menu), NULL, NULL,
			NULL, NULL, event_button, event_time);
}
/**
@brief menu-button press callback

@param textview the textview widget where the press happened
@param rt dialog runtime data struct

@return TRUE always
*/
static gboolean _e2_file_info_dialog_popup_menu_cb (GtkWidget *dialog,
	E2_FileInfoRuntime *rt)
{
	gint event_time = gtk_get_current_event_time ();
	_e2_file_info_dialog_show_context_menu (dialog, 0, event_time, rt);
	return TRUE;
}
/**
@brief mouse button press callback

@param textview the widget where the button was pressed
@param event gdk event data
@param rt rt data for the dialog

@return TRUE (stop other handlers) for btn 3 press, else FALSE (allow other handlers)
*/
static gboolean _e2_file_info_dialog_button_press_cb (GtkWidget *dialog,
	GdkEventButton *event, E2_FileInfoRuntime *rt)
{
	if (event->button == 3)
	{
		_e2_file_info_dialog_show_context_menu (dialog, 3, event->time, rt);
		return TRUE;
	}
	return FALSE;
}
/**
@brief dialog response callback

@param dialog the permissions-dialog where the response was triggered
@param response the response for the clicked button
@param result pointer to store for the dialog return value

@return
*/
static void _e2_file_info_dialog_response_cb (GtkDialog *dialog, gint response,
	DialogButtons *result)
{
	gtk_widget_destroy (GTK_WIDGET (dialog));
	if (response == GTK_RESPONSE_CLOSE)	//not covered in the general decoder
	{
		*result = CANCEL;
		gtk_main_quit ();
	}
	else
		e2_dialog_response_decode_cb (dialog, response, result);
}
/**
@brief create and run file info dialog

@param localpath localised path string of item to be processed
@param multi TRUE when the dialog needs buttons consistent with >1 item being processed

@return enumerator for pressed dialog button
*/
DialogButtons e2_file_info_dialog_run (VPATH *localpath, gboolean multi)
{
	GtkWidget *dialog;
	GtkWidget *dialog_vbox;
	GtkWidget *table;
	GString *label_text = g_string_sized_new (NAME_MAX + 32);  //this is plenty big for all uses here
	gchar *s1, *s2;
	gchar *mime;
	gchar date_string[32];
    struct stat statbuf;
	struct tm *tm_ptr;
	struct passwd *pw_buf;
	struct group *grp_buf;
	E2_FileInfoRuntime rt;
	DialogButtons choice;

	if (e2_fs_lstat (localpath, &statbuf E2_ERR_NONE()))
//explain to user ??
		return CANCEL;

	//must prevent the default dialog response, it stuffs up Q cleanups
	rt.dialog =
	dialog = e2_dialog_create (NULL, NULL, _("file info"),
		_e2_file_info_dialog_response_cb, &choice);
	dialog_vbox = GTK_DIALOG (dialog)->vbox;
	gtk_container_set_border_width (GTK_CONTAINER (dialog_vbox), E2_PADDING);

	rt.localpath = VPSTR (localpath);	//store data for copying
	s1 = g_filename_display_basename (VPSTR (localpath));
	s2 = g_markup_escape_text (s1, -1);
	g_string_printf (label_text, "<b>%s</b>", s2);
	e2_widget_add_mid_label (dialog_vbox, label_text->str, 0.5, TRUE, 0);
	g_free (s1);
	g_free (s2);
	e2_widget_add_separator (dialog_vbox, TRUE, E2_PADDING);
	mime = NULL;

	if (S_ISREG (statbuf.st_mode))
	{
		gchar *readable = NULL;
		//CHECKME
		if (statbuf.st_dev == 0 || statbuf.st_size > statbuf.st_blocks * statbuf.st_blksize)
		{
			s2 = _("Virtual file");
		}
		else
		{
			//for regular files, get type-data from 'file' command
			if (_e2_file_info_dialog_get_file_type (localpath, &readable, &mime)
				&& readable != NULL	//pipe open may succeed with bad option
				&& *readable != '\0')
					s2 = e2_utf8_from_locale (readable);	//freeme later
			else
			{
				if (readable != NULL)	//ensure s2 etc not freed later
				{
					g_free (readable);
					readable = NULL;
				}
				s2 = _("Unknown");
			}
		}

		GdkPixbuf *pixbuf = NULL;
		if (mime != NULL && *mime != '\0')
		{
			gchar *m2 = g_strdup (mime);
			//mime string returned by 'find' is of the form "text/plain; <stuff>"
			//from freedesktop.org, icon name is of the form media-subtype (e.g. text-plain).
			//If no specific icon is found, fallback to media-x-generic (e.g. text-x-generic).
			s1 = strchr (m2, ';');
			if (s1 != NULL)
				*s1 = '\0';
			s1 = strchr (m2, '/');
			if (s1 != NULL)
				*s1 = '-';
			GtkIconTheme *icon_theme = gtk_icon_theme_get_for_screen
				(gtk_widget_get_screen (app.main_window));	//app.main_window->screen;
			pixbuf = gtk_icon_theme_load_icon (icon_theme, m2,
				GTK_ICON_SIZE_LARGE_TOOLBAR, 0, NULL);
			if (pixbuf == NULL)
			{
				if (s1 != NULL)
				{
					*s1 = '\0';
					gchar *inm = g_strconcat (m2, "-x-generic", NULL);
					pixbuf = gtk_icon_theme_load_icon (icon_theme, inm,
						GTK_ICON_SIZE_LARGE_TOOLBAR, 0, NULL);
					g_free (inm);
					if (pixbuf == NULL)
					{	//fallback to anything that looks half-reasonable !
						s1++;	//pass the \0
						GList *inames = gtk_icon_theme_list_icons (icon_theme, NULL);
						GList *member;
						for (member=inames; member != NULL; member = member->next)
						{
							inm = (gchar *)member->data;
		//					printd (DEBUG, "candidate name = %s", inm);
							if (strstr (inm, s1) != NULL)
							{
								pixbuf = gtk_icon_theme_load_icon (icon_theme, inm,
									GTK_ICON_SIZE_LARGE_TOOLBAR, 0, NULL);
								break;
							}
						}
						e2_list_free_with_data (&inames);
					}
				}
			}
			g_free (m2);
		}
		if (pixbuf != NULL)
		{
			GtkWidget *image = gtk_image_new_from_pixbuf (pixbuf);
			GtkWidget *hbox = e2_widget_add_box (dialog_vbox, TRUE, 0, FALSE,
				FALSE, E2_PADDING_LARGE);
			gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
			//CHECKME escape label ?
			rt.description =
			e2_widget_add_mid_label (hbox, s2, 0, TRUE, 0);
			g_object_unref (G_OBJECT (pixbuf));
		}
		else
		{
			g_string_printf (label_text, "%s %s", _("Type:"), s2);
			//CHECKME escape label ?
			rt.description =
			e2_widget_add_mid_label (dialog_vbox, label_text->str, 0, TRUE, 0);
		}
		if (readable != NULL)
		{
			g_free (readable);
			g_free (s2);
		}
	}
	//links to dirs reported as dirs by (e2_fs_is_dir3 (localpath))
	else if (S_ISDIR (statbuf.st_mode))
	{
		GtkIconTheme *icon_theme = gtk_icon_theme_get_for_screen
				(gtk_widget_get_screen (app.main_window));	//app.main_window->screen;
		GdkPixbuf *pixbuf = gtk_icon_theme_load_icon (icon_theme, "stock_open",
				GTK_ICON_SIZE_LARGE_TOOLBAR, 0, NULL);
		s2 =  _("Directory");
		if (pixbuf != NULL)
		{
			GtkWidget *image = gtk_image_new_from_pixbuf (pixbuf);
			GtkWidget *hbox = e2_widget_add_box (dialog_vbox, TRUE, 0, FALSE,
				FALSE, E2_PADDING_LARGE);
			gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
			//CHECKME escape label ?
			rt.description =
			e2_widget_add_mid_label (hbox, s2, 0, TRUE, 0);
			g_object_unref (G_OBJECT (pixbuf));
		}
		else
		{
			g_string_printf (label_text, "%s %s", _("Type:"), s2);
			rt.description =
			e2_widget_add_mid_label (dialog_vbox, label_text->str, 0, TRUE, 0);
		}
		//FIXME report the device holding the dir
	}
	else // not regular or directory
	{
		if (S_ISLNK (statbuf.st_mode))
			s2 = _("Symbolic Link");
		else if (S_ISCHR (statbuf.st_mode))
			s2 = _("Character Device");
		else if (S_ISBLK (statbuf.st_mode))
			s2 = _("Block Device");
		else if (S_ISFIFO (statbuf.st_mode))
			s2 = _("FIFO Pipe");
		else if (S_ISSOCK (statbuf.st_mode))
			s2 = _("Socket");
		else
			s2 = _("Unknown");

		g_string_printf (label_text, "%s %s", _("Type:"), s2);
		rt.description =
		e2_widget_add_mid_label (dialog_vbox, label_text->str, 0, TRUE, 0);
	}

	if (S_ISLNK (statbuf.st_mode))
	{
		gchar target[PATH_MAX];
		gchar *filename = g_strdup (VPSTR (localpath));
		gint len = strlen (filename);
		if (filename[len-1] == G_DIR_SEPARATOR)
			filename[len-1] = '\0';
#ifdef E2_VFS
		VPATH ddata = { filename, localpath->spacedata };
		len = e2_fs_readlink (&ddata, target, sizeof (target) E2_ERR_NONE());
#else
		len = e2_fs_readlink (filename, target, sizeof (target) E2_ERR_NONE());
#endif
		if (len > 0)
		{
			target[len] = '\0';
			s1 = F_DISPLAYNAME_FROM_LOCALE (target);
			s2 = g_markup_escape_text (s1, -1);
			label_text = g_string_assign (label_text, " ");
			g_string_append_printf (label_text, _("to %s"), s2);
			F_FREE (s1);
			g_free (s2);
			//statting of relative targets fails, so ...
			if (g_str_has_prefix (target, "."G_DIR_SEPARATOR_S))
			{
				memcpy (target, (target + 2 * sizeof (gchar)), len);
			}
			if (g_str_has_prefix (target, ".."G_DIR_SEPARATOR_S))
			{
				s1 = F_FILENAME_FROM_LOCALE (target);
#ifdef E2_VFSTMP
				//FIXME which CWD for the baseline
#else
				s2 = e2_utils_translate_relative_path (curr_view->dir, s1);
#endif
				F_FREE (s1);
				s1 = F_FILENAME_TO_LOCALE (s2);
				g_strlcpy (target, s1, sizeof(target));
				//the translation adds a trailing separator, which lstat assumes to be a dir match ..
				len = strlen (target);
				target[len-1] = '\0';
				F_FREE (s1);
				g_free (s2);
			}
			//FIXME get intelligent absolute path
			if (!g_path_is_absolute (target))
			{
				gchar *local;
#ifdef E2_VFSTMP
				local = ; FIXME
#else
				local = F_FILENAME_TO_LOCALE (curr_view->dir);
#endif
				gchar *t2 = g_build_filename (local, target, NULL);
				g_strlcpy (target, t2, sizeof (target));
				F_FREE (local);
				g_free (t2);
			}
			E2_ERR_DECLARE
			//check the real target, without looking thru chained links
			struct stat statbuf2;	//don't disturb the size etc data for the link itelf
#ifdef E2_VFS
			ddata.localpath = target;
			if (e2_fs_lstat (&ddata, &statbuf2 E2_ERR_PTR()) && E2_ERR_IS (ENOENT))
#else
			if (e2_fs_lstat (target, &statbuf2 E2_ERR_PTR()) && E2_ERR_IS (ENOENT))
#endif
				g_string_append_printf (label_text, "\n %s",_("(which is <b>missing</b>)"));
			else if (g_str_equal (localpath, target))
				g_string_append_printf (label_text, "\n %s",_("(which is <b>itself</b>)"));
			else
			{
				if (e2_fs_walk_link (&filename E2_ERR_NONE()))
				{
					printd (DEBUG, "final target is %s", filename);
					if (!g_str_equal (filename, target))
					{
						s1 = F_FILENAME_FROM_LOCALE (filename);
#ifdef E2_VFSTMP
						//FIXME which CWD for the baseline
#else
						s2 = e2_utils_create_relative_path (s1, curr_view->dir);
#endif
						label_text = g_string_append (label_text, "\n ");
						g_string_append_printf (label_text, _("and ultimately to %s"), s2);
						F_FREE (s1);
						g_free (s2);
					}
				}
			}

			E2_ERR_CLEAR

			rt.target =
			e2_widget_add_mid_label (dialog_vbox, label_text->str, 0, TRUE, 0);
		}
		else
			rt.target =
			e2_widget_add_mid_label (dialog_vbox, _("(Cannot resolve the link)"), 0, TRUE, 0);

		g_free (filename);
	}
	else
		rt.target = NULL;	//don't use this label when copying

	table = e2_widget_add_table (dialog_vbox, 10, 2, FALSE, TRUE, E2_PADDING);
	gtk_table_set_col_spacings (GTK_TABLE (table), E2_PADDING);

	if (mime != NULL)
	{
		if (g_str_has_prefix (mime, "text/"))
		{
			s2 = strstr (mime, " charset");
			if (s2 != NULL)
			{
				s1 = g_utf8_strup (s2 + 9, -1);	//uppercase encoding
				*s2 = '\0';
				s2 = g_strconcat (mime, "\n", s1, NULL);
				g_free (s1);
				s1 = g_strconcat (_("Mime:"), "\n", _("Encoding:"), NULL);
			}
			else
			{
				s1 = _("Mime:");
				s2 = mime;
			}
		}
		else
		{
			s1 = _("Mime:");
			s2 = mime;
		}
		e2_widget_add_mid_label_to_table (table, s1, 0, 0,1,0,1);
		e2_widget_add_mid_label_to_table (table, s2, 0, 1,2,0,1);
		if (s2 != mime)
		{
			g_free (s1);
			g_free (s2);
		}
	}

	e2_widget_add_mid_label_to_table (table, _("Size:"), 0, 0,1,1,2);

	g_string_printf (label_text, "%"PRIu64, statbuf.st_size);
	//expand number to add separator char(s)
	const gchar *comma = nl_langinfo (THOUSEP);
	if (comma == NULL || *comma == '\0')
		comma = ",";
	guint len = label_text->len;
	guint ths = len-1;  //0-based index
	guint i;
	while (ths > 2 && len < label_text->allocated_len)
	{
		for (i = len-1; i > ths-3; i--)
			label_text->str[i+1] = label_text->str[i];
		label_text->str[i+1] = *comma;
		label_text->str[++len] = '\0';
		ths = i;
	}
	label_text->len = len;

	label_text = g_string_append_c (label_text, ' ');
	label_text = g_string_append (label_text, _("bytes"));
	rt.size =
	e2_widget_add_mid_label_to_table (table, label_text->str, 0, 1,2,1,2);

	GtkWidget *sep = gtk_hseparator_new ();
	gtk_table_attach_defaults (GTK_TABLE (table), sep, 0,2,2,3);
	e2_widget_add_mid_label_to_table (table, _("User:"), 0, 0,1,3,4);
	if ((pw_buf = getpwuid (statbuf.st_uid)) != NULL)
		g_string_printf (label_text, "%s", pw_buf->pw_name);
	else
		g_string_printf (label_text, "%d", (guint) statbuf.st_uid);
	rt.user =
	e2_widget_add_mid_label_to_table (table, label_text->str, 0, 1,2,3,4);

	e2_widget_add_mid_label_to_table (table, _("Group:"), 0, 0,1,4,5);
	if ((grp_buf = getgrgid (statbuf.st_gid)) != NULL)
		g_string_printf (label_text, "%s", grp_buf->gr_name);
	else
		g_string_printf (label_text, "%d", (guint) statbuf.st_gid);
	rt.group =
	e2_widget_add_mid_label_to_table (table, label_text->str, 0, 1,2,4,5);
	gchar *perm_string = e2_fs_get_perm_string (statbuf.st_mode);
	g_string_assign (label_text, g_utf8_next_char (perm_string));	//skip the "type code" in 1st byte of string
	g_free (perm_string);
	g_string_insert_c (label_text, e2_utils_get_byte_position (label_text->str, 6), ' ');
	g_string_insert_c (label_text, e2_utils_get_byte_position (label_text->str, 3), ' ');
	e2_widget_add_mid_label_to_table (table, _("Permissions:"), 0, 0,1,5,6);
	rt.permissions =
	e2_widget_add_mid_label_to_table (table, label_text->str, 0, 1,2,5,6);

	sep = gtk_hseparator_new ();
	gtk_table_attach_defaults (GTK_TABLE (table), sep, 0,2,6,7);

	s1 = "%a %Od %b %EY %OI:%OM %p";
	tm_ptr = localtime (&statbuf.st_atime);
	strftime (date_string, sizeof (date_string), s1, tm_ptr);
	s2 = e2_utf8_from_locale (date_string);
	e2_widget_add_mid_label_to_table (table, _("Accessed:"), 0, 0,1,7,8);
	rt.accessdate =
	e2_widget_add_mid_label_to_table (table, s2, 0, 1,2,7,8);
#ifdef USE_GTK2_12TIPS
	gtk_widget_set_tooltip_text (
#else
	e2_widget_set_tooltip (NULL,
#endif
		rt.accessdate, _("Item was last opened, run, read etc"));
	g_free (s2);

	tm_ptr = localtime (&statbuf.st_mtime);
	strftime (date_string, sizeof (date_string), s1, tm_ptr);
	s2 = e2_utf8_from_locale (date_string);
	e2_widget_add_mid_label_to_table (table, _("Modified:"), 0, 0,1,8,9);
	rt.moddate =
	e2_widget_add_mid_label_to_table (table, s2, 0, 1,2,8,9);
#ifdef USE_GTK2_12TIPS
	gtk_widget_set_tooltip_text (
#else
	e2_widget_set_tooltip (NULL,
#endif
		rt.moddate, _("Content of the item was last altered"));
	g_free (s2);

	tm_ptr = localtime (&statbuf.st_ctime);
	strftime (date_string, sizeof (date_string), s1, tm_ptr);
	s2 = e2_utf8_from_locale (date_string);
	e2_widget_add_mid_label_to_table (table, _("Changed:"), 0, 0,1,9,10);
	rt.changedate =
	e2_widget_add_mid_label_to_table (table, s2, 0, 1,2,9,10);
#ifdef USE_GTK2_12TIPS
	gtk_widget_set_tooltip_text (
#else
	e2_widget_set_tooltip (NULL,
#endif
		rt.changedate, _("Property of the item (name, permission, owner etc) was last altered"));
	g_free (s2);

	g_string_free (label_text,TRUE);
	if (mime != NULL)
		g_free (mime);

	if (multi)
	{
		E2_BUTTON_NOTOALL.showflags &= ~E2_BTN_DEFAULT;
		e2_dialog_add_defined_button (dialog, &E2_BUTTON_NOTOALL);
		e2_dialog_set_negative_response (dialog, E2_RESPONSE_NOTOALL);
	}
	E2_BUTTON_CLOSE.showflags |= E2_BTN_DEFAULT;
	e2_dialog_add_defined_button (dialog, &E2_BUTTON_CLOSE);
	//enable menu
	g_signal_connect (G_OBJECT (dialog), "popup-menu",
		G_CALLBACK (_e2_file_info_dialog_popup_menu_cb), &rt);
	g_signal_connect (G_OBJECT (dialog), "button-press-event",
		G_CALLBACK (_e2_file_info_dialog_button_press_cb), &rt);

	e2_dialog_setup (dialog, app.main_window);

	gdk_threads_enter ();
	e2_dialog_run (dialog, app.main_window, 0);
	gtk_window_present (GTK_WINDOW (dialog));	//sometimes, dialog is not focused
	gtk_main ();
	gdk_threads_leave ();

	return choice;
}
