#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <signal.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>

#include "../include/string.h"
#include "../include/disk.h"
#include "../include/prochandle.h"

#include "guiutils.h"
#include "cdialog.h"
#include "fb.h"
#include "csd.h"
#include "fsd.h"
#include "progressdialog.h"
#include "pdialog.h"
#include "fprompt.h"

#include "cfg.h"
#include "url.h"
#include "edvtypes.h"
#include "edvobj.h"
#include "edvrecbinfio.h"
#include "edvarchobj.h"
#include "edvrecbin.h"
#include "edvinterps.h"
#include "browser.h"
#include "browsercb.h"
#include "browseropcb.h"
#include "imbr.h"
#include "imbrcb.h"
#include "imbropcb.h"
#include "archiver.h"
#include "archiveropcb.h"
#include "archivercb.h"
#include "archiveropcb.h"
#include "recbin.h"
#include "recbincb.h"
#include "recbinopcb.h"
#include "recbindeskicon.h"
#include "findwin.h"
#include "findwincb.h"
#include "propdlg.h"
#include "historywin.h"
#include "aboutdlg.h"
#include "endeavour.h"
#include "edvcb.h"
#include "edvop.h"    
#include "edvinterpsop.h"
#include "edvopen.h"
#include "edvutils.h"
#include "edvcfglist.h"
#include "config.h"


/* UNIX Signal Callbacks */
void EDVSignalCB(int s);

/* Timeout Callbacks */
gint EDVManageTimeoutCB(gpointer data);

/* New/Map Window Callbacks */
void EDVNewBrowserCB(GtkWidget *widget, gpointer data);
void EDVNewImbrCB(GtkWidget *widget, gpointer data);
void EDVNewArchiverCB(GtkWidget *widget, gpointer data);
void EDVMapRecBinCB(GtkWidget *widget, gpointer data);
void EDVDevicesCB(GtkWidget *widget, gpointer data);
void EDVMIMETypesCB(GtkWidget *widget, gpointer data);
void EDVOptionsCB(GtkWidget *widget, gpointer data);
void EDVCustomizeCB(GtkWidget *widget, gpointer data);

/* GtkCList Sort Callbacks */
static gint EDVCListColumnSortNexus(
	GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2,
	gint sort_code
);
gint EDVCListColumnSortStringCB(
	GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2
);
gint EDVCListColumnSortNumericCB(
	GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2
);

/* Refresh & Reset Callbacks */
void EDVRefreshCB(GtkWidget *widget, gpointer data);
void EDVResetCB(GtkWidget *widget, gpointer data);

/* Clear Callbacks */
void EDVPurgeAllRecycledObjectsCB(GtkWidget *widget, gpointer data);
void EDVClearHistoryCB(GtkWidget *widget, gpointer data);
void EDVClearLocationsHistoryCB(GtkWidget *widget, gpointer data);
void EDVClearRunHistoryCB(GtkWidget *widget, gpointer data);

/* GtkEntry DND Callbacks */
gboolean EDVEntryDragMotionCB(
	GtkWidget *widget, GdkDragContext *dc, gint x, gint y, guint t,
	gpointer data
);
void EDVEntryDragDataReceivedCB(
	GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
	GtkSelectionData *selection_data, guint info, guint t,
	gpointer data
);

/* GtkEntry Complete Path Callback */
gint EDVEntryCompletePathCB(
	GtkWidget *widget, GdkEventKey *key, gpointer data
);

/* Location Bar DND Callbacks */
void EDVLocBarIconDragDataGet(
	GtkWidget *widget, GdkDragContext *context,
	GtkSelectionData *selection_data, guint info, guint time,
	gpointer data
);


/* Write Protect Changed Emitters */
void EDVWriteProtectChangedEmit(
	edv_core_struct *core_ptr, gboolean state
);

/* Object Emitters */
void EDVObjectAddedEmit(
	edv_core_struct *core_ptr, const gchar *path,
	const struct stat *lstat_buf
);
void EDVObjectModifiedEmit(
	edv_core_struct *core_ptr, const gchar *path,
	const gchar *new_path,
	const struct stat *lstat_buf
);
void EDVObjectRemovedEmit(
	edv_core_struct *core_ptr, const gchar *path
);

/* Mount/Unmount Emitters */
void EDVObjectMountEmit(
	edv_core_struct *core_ptr,
	gint dev_num, edv_device_struct *dev_ptr,
	gboolean is_mounted
);

/* Archive Emitters */
void EDVArchiveObjectAddedNotifyCB(
	edv_core_struct *core_ptr, const gchar *arch_path,
	const gchar *path, edv_archive_object_struct *obj
);
void EDVArchiveObjectModifiedNotifyCB(
	edv_core_struct *core_ptr, const gchar *arch_path,
	const gchar *path, const gchar *new_path,
	edv_archive_object_struct *obj
);
void EDVArchiveObjectRemovedNotifyCB(
	edv_core_struct *core_ptr, const gchar *arch_path,
	const gchar *path
);

/* Recycled Object Emitters */
void EDVRecycledObjectAddedEmit(
	edv_core_struct *core_ptr, guint index
);
void EDVRecycledObjectRemovedEmit(
	edv_core_struct *core_ptr, guint index
);

/* Reconfigure Emitters */
void EDVReconfiguredEmit(edv_core_struct *core_ptr);

/* MIME Type Emitters */
void EDVMimeTypeAddedEmit(
	edv_core_struct *core_ptr,
	gint mt_num, edv_mimetype_struct *mt_ptr
);
void EDVMimeTypeModifiedEmit(
	edv_core_struct *core_ptr,
	gint mt_num, edv_mimetype_struct *mt_ptr
);
void EDVMimeTypeRemovedEmit(
	edv_core_struct *core_ptr, gint mt_num
);

/* Device Emitters */
void EDVDeviceAddedEmit(
	edv_core_struct *core_ptr,
	gint dev_num, edv_device_struct *dev_ptr
);
void EDVDeviceModifiedEmit(
	edv_core_struct *core_ptr,
	gint dev_num, edv_device_struct *dev_ptr
);
void EDVDeviceRemovedEmit(
	edv_core_struct *core_ptr, gint dev_num
);

/* History Event Emitters */
void EDVHistoryAddedEmit(
	edv_core_struct *core_ptr,
	gint h_num, edv_history_struct *h_ptr
);
void EDVHistoryRemovedEmit(
	edv_core_struct *core_ptr,
	gint h_num
);


#define ATOI(s)         (((s) != NULL) ? atoi(s) : 0)
#define ATOL(s)         (((s) != NULL) ? atol(s) : 0)
#define ATOF(s)         (((s) != NULL) ? atof(s) : 0.0f)
#define STRDUP(s)       (((s) != NULL) ? g_strdup(s) : NULL)

#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
#define CLIP(a,l,h)     (MIN(MAX((a),(l)),(h)))
#define STRLEN(s)       (((s) != NULL) ? strlen(s) : 0)
#define STRISEMPTY(s)   (((s) != NULL) ? (*(s) == '\0') : TRUE)


/*
 *	Signal callback.
 *
 *	No gtk/gdk calls can be made here, since this is actually
 *	called from a separate thread.
 */
void EDVSignalCB(int s)
{
	edv_core_struct *core_ptr = edv_core;

	if(core_ptr == NULL)
	    return;

	switch(s)
	{
#ifdef SIGHUP
	  case SIGHUP:
	    core_ptr->need_reset = TRUE;
	    break;
#endif
#ifdef SIGINT
	  case SIGINT:
	    core_ptr->need_close_all_windows = TRUE;
	    break;
#endif
#ifdef SIGTERM
	  case SIGTERM:
	    exit(0);	/* Explicit exit */
	    break;
#endif
#ifdef SIGQUIT
	  case SIGQUIT:
	    core_ptr->need_close_all_windows = TRUE;
	    break;
#endif
#ifdef SIGSEGV
	  case SIGSEGV:
	    exit(1);	/* Explicit exit with error */
	    break;
#endif
#ifdef SIGUSR1
	  case SIGUSR1:
	    /* Increment InterPS signal count */
	    core_ptr->interps_signal_count++;
	    break;
#endif

#ifdef SIGUSR2
	  case SIGUSR2:
	    break;
#endif
#ifdef SIGPIPE
	  case SIGPIPE: 
	    break;
#endif
	}
}


/*
 *	Core timeout callback.
 */
gint EDVManageTimeoutCB(gpointer data)
{
	gboolean	conserve_memory,
			has_changes,
			still_processing,
			all_windows_unmapped;
	gint i;
	const cfg_item_struct *cfg_list;
	edv_browser_struct *browser;
	edv_imbr_struct *imbr;
	edv_archiver_struct *archiver;
	edv_archive_info_struct *arch_info;
	edv_recbin_struct *recbin;
	edv_findwin_struct *findwin;
	edv_propdlg_struct *propdlg;
	edv_history_listwin_struct *histwin;
	edv_run_dlg_struct *rundlg;
	edv_device_listwin_struct *deviceswin;
	edv_mimetype_listwin_struct *mimetypeswin;
	optwin_struct *optwin;
	about_dlg_struct *aboutdlg;
	edv_core_struct *core_ptr = EDV_CORE(data);
	if(core_ptr == NULL)
	    return(FALSE);

/* Removes the timeout ID and sets it to 0 */
#define DO_REMOVE_TOID(_id_)		\
{ if(*(_id_) > 0) {			\
  gtk_timeout_remove(*(_id_));		\
  *(_id_) = 0;				\
} }

/* Removes all timeout callbacks and breaks out of the GTK main loop */
#define DO_REMOVE_TIMEOUTS_AND_BREAK_MAIN {	\
 DO_REMOVE_TOID(&core_ptr->manage_toid);	\
						\
 gtk_main_quit();				\
}

/* Reschedual timeout callback to this function and return FALSE, since
 * this function need not be called again because a new timeout has
 * been set for this function
 */
#define DO_RETURN_RESCHEDUAL	{		\
 core_ptr->manage_toid = gtk_timeout_add(	\
  1000l,		/* 1 second interval */	\
  EDVManageTimeoutCB, core_ptr			\
 );						\
						\
 return(FALSE);					\
}

/* Mark timeout callback to this function as no longer valid and
 * return FALSE to never call this function again
 */
#define DO_RETURN_NO_RESCHEDUAL		{	\
 core_ptr->manage_toid = 0;			\
						\
 return(FALSE);					\
}

/* Returns TRUE, keeping the current timeout callback to this function */
#define DO_RETURN_CURRENT_SCHEDUAL	{	\
 return(TRUE);					\
}

	/* Get configuration */
	cfg_list = core_ptr->cfg_list;
	conserve_memory = CFGItemListGetValueI(
	    cfg_list, EDV_CFG_PARM_CONSERVE_MEMORY
	);


	/* Need to close all windows? */
	if(core_ptr->need_close_all_windows)
	{
	    core_ptr->need_close_all_windows = FALSE;

	    /* Check if any windows/dialogs are in query or are still
	     * processing
	     *
	     * Let next call to this function handle rest of closing
	     * all windows
	     */
	    if(CDialogIsQuery())
	    {
		CDialogBreakQuery();
		DO_RETURN_CURRENT_SCHEDUAL
	    }
	    if(FileBrowserIsQuery())
	    {
		FileBrowserBreakQuery();
		DO_RETURN_CURRENT_SCHEDUAL
	    }
	    if(ProgressDialogIsQuery())
	    {
		ProgressDialogBreakQuery(FALSE);
		DO_RETURN_CURRENT_SCHEDUAL
	    }
	    if(FSDIsQuery())
	    {
		FSDBreakQuery();
		DO_RETURN_CURRENT_SCHEDUAL
	    }
	    if(CSDIsQuery())
	    {
		CSDBreakQuery();
		DO_RETURN_CURRENT_SCHEDUAL
	    }
	    if(PDialogIsQuery())
	    {
		PDialogBreakQuery();
		DO_RETURN_CURRENT_SCHEDUAL
	    }
	    if(FPromptIsQuery())
	    {
		FPromptBreakQuery();
		DO_RETURN_CURRENT_SCHEDUAL
	    }
	    /* All dialogs not in query */

	    /* Reset still processing marker, which indicates that
	     * there are still windows that are processing and cannot
	     * be closed
	     */
	    still_processing = FALSE;

	    /* Reset has changes marker, which indicates there is
	     * atleast one window with unsaved modified data
	     */
	    has_changes = FALSE;

	    /* Begin checking through all windows to see if any are
	     * still processing or have changes
	     */

	    /* File Browsers */
	    for(i = 0; i < core_ptr->total_browsers; i++)
	    {
		browser = core_ptr->browser[i];
		if(browser == NULL)
		    continue;

		if(browser->processing)
		    still_processing = TRUE;
	    }
	    /* Image Browsers */
	    for(i = 0; i < core_ptr->total_imbrs; i++)
	    {
		imbr = core_ptr->imbr[i];
		if(imbr == NULL)
		    continue;

		if(imbr->processing)
		    still_processing = TRUE;
	    }
	    /* Archivers */
	    for(i = 0; i < core_ptr->total_archivers; i++)
	    {
		archiver = core_ptr->archiver[i];
		if(archiver == NULL)
		    continue;

		if(archiver->processing)
		    still_processing = TRUE;

		arch_info = archiver->arch_info;
		if(arch_info != NULL)
		{
		    if(arch_info->processing)
			still_processing = TRUE;
		    if(arch_info->has_changes)
			has_changes = TRUE;
		}
	    }
	    /* Recycle Bin */
	    recbin = core_ptr->recbin;
	    if(recbin != NULL)
	    {
		if(recbin->processing)
		    still_processing = TRUE;
	    }
	    /* Find Window */
	    findwin = core_ptr->findwin;
	    if(findwin != NULL)
	    {
		if(findwin->processing)
		    still_processing = TRUE;
	    }
	    /* Property Dialogs */
	    for(i = 0; i < core_ptr->total_propdlgs; i++)
	    {
		propdlg = core_ptr->propdlg[i];
		if(propdlg == NULL)
		    continue;

		if(propdlg->processing)
		    still_processing = TRUE;
		if(propdlg->has_changes)
		    has_changes = TRUE;
	    }
	    /* History List Window */
	    histwin = core_ptr->history_listwin;
	    if(histwin != NULL)
	    {
		if(histwin->processing)
		    still_processing = TRUE;
	    }
	    /* Run Dialog */
	    rundlg = core_ptr->run_dlg;
	    if(rundlg != NULL)
	    {
		if(rundlg->processing)
		    still_processing = TRUE;
	    }
	    /* Devices List Window */
	    deviceswin = core_ptr->device_listwin;
	    if(deviceswin != NULL)
	    {
		if(deviceswin->processing)
		    still_processing = TRUE;
	    }
	    /* MIME Types List Window */
	    mimetypeswin = core_ptr->mimetype_listwin;
	    if(mimetypeswin != NULL)
	    {
		if(mimetypeswin->processing)
		    still_processing = TRUE;
	    }
	    /* Options Window */
	    optwin = core_ptr->options_window;
	    if(optwin != NULL)
	    {
		if(optwin->processing)
		    still_processing = TRUE;
		if(optwin->has_changes)
		    has_changes = TRUE;
	    }
	    /* Customize Window */
	    optwin = core_ptr->customize_window;
	    if(optwin != NULL)
	    {
		if(optwin->processing)
		    still_processing = TRUE;
		if(optwin->has_changes)
		    has_changes = TRUE;
	    }
	    /* About Dialog */
	    aboutdlg = core_ptr->about_dlg;
	    if(aboutdlg != NULL)
	    {
		if(aboutdlg->processing)
		    still_processing = TRUE;
	    }

	    /* Begin checking results, to see if any windows are still
	     * processing or have unsaved changed data
	     */

	    /* Any still processing? */
	    if(still_processing)
	    {
		/* One or more window is still processing, cannot
		 * close now so we need to return
		 *
		 * The next call to this function won't close all
		 * windows since we set the global marker for that
		 * to FALSE
		 */
		DO_RETURN_CURRENT_SCHEDUAL
	    }


	    /* Any have changed data? */
	    if(has_changes)
	    {
/* TODO */

	    }

	    /* Remove all timeout callbacks and break out of the GTK
	     * main loop
	     */
	    DO_REMOVE_TIMEOUTS_AND_BREAK_MAIN

	    /* Return and do not call this function again */
	    DO_RETURN_NO_RESCHEDUAL
	}


	/* Need to reset? */
	if(core_ptr->need_reset)
	{
	    core_ptr->need_reset = FALSE;
	    EDVReset(core_ptr);
	}


	/* Begin checking to see if at least one window is unmapped
	 * and delete some unmapped windows
	 *
	 * Reset all_windows_unmapped to TRUE, it will be set to FALSE
	 * if at least one window is mapped
	 */
	all_windows_unmapped = TRUE;

	/* File Browsers */
	for(i = 0; i < core_ptr->total_browsers; i++)
	{
	    browser = core_ptr->browser[i];
	    if(browser == NULL)
		continue;

	    /* Mapped? */
	    if(EDVBrowserIsMapped(browser))
	    {
		all_windows_unmapped = FALSE;
	    }
	    else
	    {
		/* Unmapped, so delete it */
		core_ptr->browser[i] = NULL;
		EDVBrowserDelete(browser);
		browser = NULL;
	    }
	}
	/* Image Browsers */
	for(i = 0; i < core_ptr->total_imbrs; i++)
	{
	    imbr = core_ptr->imbr[i];
	    if(imbr == NULL)
		continue;

	    /* Mapped? */
	    if(EDVImbrIsMapped(imbr))
	    {
		all_windows_unmapped = FALSE;
	    }
	    else
	    {
		/* Unmapped, so delete it */
		core_ptr->imbr[i] = NULL;
		EDVImbrDelete(imbr);
		imbr = NULL;
	    }
	}
	/* Archivers */
	for(i = 0; i < core_ptr->total_archivers; i++)
	{
	    archiver = core_ptr->archiver[i];
	    if(archiver == NULL)
		continue;

	    /* Mapped? */
	    if(EDVArchiverIsMapped(archiver))
	    {
		all_windows_unmapped = FALSE;
	    }
	    else
	    {
		/* Unmapped, so delete it */
		core_ptr->archiver[i] = NULL;
		EDVArchiverDelete(archiver);
		archiver = NULL;
	    }
	}
	/* Recycle Bin */
	recbin = core_ptr->recbin;
	if(recbin != NULL)
	{
	    /* Initialized and mapped? */
	    if(EDVRecBinIsMapped(recbin))
	    {
		all_windows_unmapped = FALSE;
	    }
	    else if(conserve_memory)
	    {
		/* Unmapped and need to conserve memory, so delete it */
		core_ptr->recbin = NULL;
		EDVRecBinDelete(recbin);
		recbin = NULL;
	    }
	}
	/* Find Window */
	findwin = core_ptr->findwin;
	if(findwin != NULL)
	{
	    /* Initialized and mapped? */
	    if(EDVFindWinIsMapped(findwin))
	    {
		all_windows_unmapped = FALSE;
	    }
	    else
	    {
		/* Never delete the find window since it may hold
		 * dynamic data (i.e. find results) that needs to be
		 * preserved
		 */
	    }
	}
	/* Property Dialogs */
	for(i = 0; i < core_ptr->total_propdlgs; i++)
	{
	    propdlg = core_ptr->propdlg[i];
	    if(propdlg == NULL)
		continue;

	    /* Initialized and mapped? */
	    if(EDVPropDlgIsMapped(propdlg))
	    {
		all_windows_unmapped = FALSE;
	    }
	    else
	    {
		/* Unmapped, so delete it */
		core_ptr->propdlg[i] = NULL;
		EDVPropDlgDelete(propdlg);
		propdlg = NULL;
	    }
	}
	/* History List Window */
	histwin = core_ptr->history_listwin;
	if(histwin != NULL)
	{
	    /* Initialized and mapped? */
	    if(EDVHistoryListWinIsMapped(histwin))
	    {
		all_windows_unmapped = FALSE;
	    }
	    else if(conserve_memory)
	    {
		/* Unmapped and need to conserve memory, so delete it */
		core_ptr->history_listwin = NULL;
		EDVHistoryListWinDelete(histwin);
		histwin = NULL;
	    }
	}
	/* Run Dialog */
	rundlg = core_ptr->run_dlg;
	if(rundlg != NULL)
	{
	    if(EDVRunDlgIsMapped(rundlg))
	    {
		all_windows_unmapped = FALSE;
	    }
	    else if(conserve_memory)
	    {
		/* Unmapped and need to conserve memory, so delete it */
		core_ptr->run_dlg = NULL;
		EDVRunDlgDelete(rundlg);
		rundlg = NULL;
	    }
	}

	/* Devices List Window */
	deviceswin = core_ptr->device_listwin;
	if(deviceswin != NULL)
	{
	    if(EDVDevicesListWinIsMapped(deviceswin))
	    {
		all_windows_unmapped = FALSE;
	    }
	    else if(conserve_memory)
	    {
		/* Unmapped and need to conserve memory, so delete it */
		core_ptr->device_listwin = NULL;
		EDVDevicesListWinDelete(deviceswin);
		deviceswin = NULL;
	    }
	}
	/* MIME Types List Window */
	mimetypeswin = core_ptr->mimetype_listwin;
	if(mimetypeswin != NULL)
	{
	    if(EDVMimeTypesListWinIsMapped(mimetypeswin))
	    {
		all_windows_unmapped = FALSE;
	    }
	    else if(conserve_memory)
	    {
		/* Unmapped and need to conserve memory, so delete it */
		core_ptr->mimetype_listwin = NULL;
		EDVMimeTypesListWinDelete(mimetypeswin);
		mimetypeswin = NULL;
	    }
	}
	/* Options Window */
	optwin = core_ptr->options_window;
	if(optwin != NULL)
	{
	    if(OptWinIsMapped(optwin))
	    {
		all_windows_unmapped = FALSE;
	    }
	    else if(conserve_memory)
	    {
		/* Unmapped and need to conserve memory, so delete it */
		core_ptr->options_window = NULL;
		OptWinDelete(optwin);
		optwin = NULL;
	    }
	}
	/* Customize Window */
	optwin = core_ptr->customize_window;
	if(optwin != NULL)
	{
	    if(OptWinIsMapped(optwin))
	    {
		all_windows_unmapped = FALSE;
	    }
	    else if(conserve_memory)
	    {
		/* Unmapped and need to conserve memory, so delete it */
		core_ptr->customize_window = NULL;
		OptWinDelete(optwin);
		optwin = NULL;
	    }
	}
	/* About Dialog */
	aboutdlg = core_ptr->about_dlg;
	if(aboutdlg != NULL)
	{
	    /* Initialized and mapped? */
	    if(AboutDlgIsMapped(aboutdlg))
	    {
		all_windows_unmapped = FALSE;
	    }
	    else
	    {
		/* Unmapped, so delete it */
		core_ptr->about_dlg = NULL;
		AboutDlgDelete(aboutdlg);
		aboutdlg = NULL;
	    }
	}


	/* Were all windows unmapped? */
	if(all_windows_unmapped)
	{
	    /* All windows are unmapped, this implies that the
	     * application must begin exiting
	     */

	    /* Removes all timeout callbacks and breaks out of one GTK
	     * main loop
	     */
	    DO_REMOVE_TIMEOUTS_AND_BREAK_MAIN

	    /* Return and do not call this function again */
	    DO_RETURN_NO_RESCHEDUAL
	}



	/* Check and handle any InterPS commands if the InterPS
	 * signal count is positive
	 */
	if(core_ptr->interps_signal_count > 0)
	{
	    /* Get InterPS commands from file */
	    gint total_cmds;
	    gchar **cmd = EDVInterPSGetCommand(cfg_list, &total_cmds);

	    /* Remove InterPS command file */
	    EDVInterPSRemoveCommand(cfg_list);

	    /* Process InterPS commands */
	    EDVInterPSOPProcessCommands(core_ptr, cmd, total_cmds);

	    /* Delete InterPS commands */
	    strlistfree(cmd, total_cmds);

	    /* Reduce InterPS signal count */
	    core_ptr->interps_signal_count--;
	    if(core_ptr->interps_signal_count < 0)
		core_ptr->interps_signal_count = 0;
	}


	DO_RETURN_RESCHEDUAL
#undef DO_RETURN_RESCHEDUAL
#undef DO_RETURN_NO_RESCHEDUAL
#undef DO_RETURN_CURRENT_SCHEDUAL
#undef DO_REMOVE_TIMEOUTS_AND_BREAK_MAIN
#undef DO_REMOVE_TOID
}


/*
 *	New File Browser callback.
 */
void EDVNewBrowserCB(GtkWidget *widget, gpointer data)
{
	EDVNewBrowser(EDV_CORE(data));
}

/*
 *	New Image Browser callback.
 */
void EDVNewImbrCB(GtkWidget *widget, gpointer data)
{
	EDVNewImbr(EDV_CORE(data));
}

/*
 *	New Archiver callback.
 */
void EDVNewArchiverCB(GtkWidget *widget, gpointer data)
{
	EDVNewArchiver(EDV_CORE(data));
}

/*
 *	Map Recycle Bin callback.
 */
void EDVMapRecBinCB(GtkWidget *widget, gpointer data)
{
	EDVMapRecBin(EDV_CORE(data));
}

/*
 *	Map Devices List callback.
 */
void EDVDevicesCB(GtkWidget *widget, gpointer data)
{
	GtkWidget *toplevel = (widget != NULL) ?
	    gtk_widget_get_toplevel(widget) : NULL;

	EDVMapDevicesListWin(EDV_CORE(data), toplevel);
}

/*
 *	Map MIME Types List callback.
 */
void EDVMIMETypesCB(GtkWidget *widget, gpointer data)
{
	GtkWidget *toplevel = (widget != NULL) ?
	    gtk_widget_get_toplevel(widget) : NULL;

	EDVMapMIMETypesListWin(EDV_CORE(data), toplevel);
}

/*
 *	Map Options Window callback.
 */
void EDVOptionsCB(GtkWidget *widget, gpointer data)
{
	GtkWidget *toplevel = (widget != NULL) ?
	    gtk_widget_get_toplevel(widget) : NULL;

	EDVMapOptionsWin(EDV_CORE(data), toplevel);
}

/*
 *	Map Customize Window callback.
 */
void EDVCustomizeCB(GtkWidget *widget, gpointer data)
{
	GtkWidget *toplevel = (widget != NULL) ?
	    gtk_widget_get_toplevel(widget) : NULL;

	EDVMapCustomizeWin(EDV_CORE(data), toplevel);
}


/*
 *	GtkCList column sort callback nexus.
 *
 *	These only take the text value from the cell that corresesponds
 *	to the sort column of the given rows. The row data is ignored.
 *
 *	Only cells of type text and pixtext are supported, all other 
 *	cell types will return -1.
 */
static gint EDVCListColumnSortNexus(
	GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2,
	gint sort_code
)
{
	gint sort_column;
	GtkSortType sort_type;
	const gchar	*text1 = NULL,
			*text2 = NULL;
	const GtkCListRow	*row1 = (const GtkCListRow *)ptr1,
				*row2 = (const GtkCListRow *)ptr2;

	if((clist == NULL) || (row1 == NULL) || (row2 == NULL))
	    return(-1);

	/* Get the column number that we are to sort by and what
	 * type of sorting (ascending or descending)
	 */
	sort_column = clist->sort_column;
	if((sort_column < 0) || (sort_column >= clist->columns))
	    return(-1);
	sort_type = clist->sort_type;

	/* Get the pointer to the text of the first cell */
	switch(row1->cell[sort_column].type)
	{
	  case GTK_CELL_TEXT:
	    text1 = GTK_CELL_TEXT(row1->cell[sort_column])->text;
	    break;
	  case GTK_CELL_PIXTEXT:
	    text1 = GTK_CELL_PIXTEXT(row1->cell[sort_column])->text;
	    break;
	  default:
	    break;
	}

	/* Get the pointer to the text of the second cell */
	switch(row2->cell[sort_column].type)
	{
	  case GTK_CELL_TEXT:
	    text2 = GTK_CELL_TEXT(row2->cell[sort_column])->text;
	    break;
	  case GTK_CELL_PIXTEXT:
	    text2 = GTK_CELL_PIXTEXT(row2->cell[sort_column])->text;
	    break;
	  default:
	    break;
	}

	if(text2 == NULL)
	    return((text1 != NULL) ? 1 : -1);
	if(text1 == NULL)
	    return(-1);

	/* Sort by the given sort_code */
	switch(sort_code)
	{
	  case 0:	/* By text */
	    return(strcmp(text1, text2));
	    break;

	  case 1:	/* By numeric */
	    {
		/* Number strings may contain ',' characters, need
		 * to copy them to tempory strings and strip them
		 */
		gchar	ns1[80], ns2[80];
		gint	n1, n2;

		/* Copy strings to tempory and modifyable number
		 * strings
		 */
		strncpy(ns1, text1, sizeof(ns1));
		ns1[sizeof(ns1) - 1] = '\0';
		strncpy(ns2, text2, sizeof(ns2));
		ns2[sizeof(ns2) - 1] = '\0';

		/* Strip ',' from coppied tempory number strings */
		substr(ns1, ",", "");
		substr(ns2, ",", "");

		n1 = strtod(ns1, NULL),
		n2 = strtod(ns2, NULL);

		if(n1 >= n2)
		    return((gint)(n1 > n2));
		else
		    return(-1);
	    }
	    break;

	  default:
	    return(-1);
	    break;
	}
}

/*
 *	GtkCList column sort by string callback.
 */
gint EDVCListColumnSortStringCB(
	GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2
)
{
	return(EDVCListColumnSortNexus(clist, ptr1, ptr2, 0));
}

/*
 *	GtkCList column sort by numeric callback.
 */
gint EDVCListColumnSortNumericCB(
	GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2
)
{
	return(EDVCListColumnSortNexus(clist, ptr1, ptr2, 1));
}


/*
 *	Refresh Callback.
 */
void EDVRefreshCB(GtkWidget *widget, gpointer data)
{
	EDVRefresh(EDV_CORE(data));
}


/*
 *	Purge Recycled Objects callback.
 */
void EDVPurgeAllRecycledObjectsCB(GtkWidget *widget, gpointer data)
{
	edv_core_struct *core_ptr = EDV_CORE(data);
	if(core_ptr == NULL)
	    return;

	/* Map Recycle Bin */
	EDVMapRecBin(core_ptr);

	/* Purge all objects in the Recycle Bin */
	EDVRecBinOPPurgeAll(core_ptr->recbin);
}

/*
 *	Clear History Events callback.
 */
void EDVClearHistoryCB(GtkWidget *widget, gpointer data)
{
	GtkWidget *toplevel = (widget != NULL) ?
	    gtk_widget_get_toplevel(widget) : NULL;
	edv_core_struct *core_ptr = EDV_CORE(data);
	if(core_ptr == NULL)
	    return;

	EDVClearHistoryEvents(core_ptr, toplevel);
}

/*
 *	Clear locations history callback.
 */
void EDVClearLocationsHistoryCB(GtkWidget *widget, gpointer data)
{
	GtkWidget *toplevel = (widget != NULL) ?
	    gtk_widget_get_toplevel(widget) : NULL;
	edv_core_struct *core_ptr = EDV_CORE(data);
	if(core_ptr == NULL)
	    return;

	EDVClearLocationsHistory(core_ptr, toplevel);
}

/*
 *	Clear run history callback.
 */
void EDVClearRunHistoryCB(GtkWidget *widget, gpointer data)
{
	GtkWidget *toplevel = (widget != NULL) ?
	    gtk_widget_get_toplevel(widget) : NULL;
	edv_core_struct *core_ptr = EDV_CORE(data);
	if(core_ptr == NULL)
	    return;

	EDVClearRunHistory(core_ptr, toplevel);
}


/*
 *      GtkEntry DND "drag_motion" signal callback.
 *
 *      This is used to constrain all drags (regardless of its type
 *      or source data type) to be a drag action of copy.
 */
gboolean EDVEntryDragMotionCB(
	GtkWidget *widget, GdkDragContext *dc, gint x, gint y, guint t,
	gpointer data
)
{
	if(dc == NULL)
	    return(FALSE);

	if(dc->actions & GDK_ACTION_COPY)
	    gdk_drag_status(dc, GDK_ACTION_COPY, t);
	else
	    gdk_drag_status(dc, 0, t);

	return(TRUE);
}

/*
 *	GtkEntry DND "drag_data_received" signal callback.
 *
 *	This is used as an opaque callback to support the dragging of any
 *	disk object to any GtkEntry widget. The value in the GtkEntry
 *	will be replaced with a space separated list of absolute disk
 *	object paths.
 *
 *	Note that only the text is updated, no "activate" signal will
 *	be emitted.
 */
void EDVEntryDragDataReceivedCB(
	GtkWidget *widget, GdkDragContext *dc, gint x, gint y,
	GtkSelectionData *selection_data, guint info, guint t,
	gpointer data
)
{
	GList *url_list = NULL;
	GtkEntry *entry;
	edv_core_struct *core_ptr = EDV_CORE(data);
	if((widget == NULL) || (dc == NULL) || (selection_data == NULL) ||
	   (core_ptr == NULL)
	)
	    return;

#define DO_FREE_LOCALS	{				\
 g_list_foreach(url_list, (GFunc)URLDelete, NULL);	\
 g_list_free(url_list);					\
 url_list = NULL;					\
}

	/* Given widget must have or be a GtkEntry */
	if(GTK_IS_COMBO(widget))
	{
	    GtkCombo *combo = GTK_COMBO(widget);
	    entry = GTK_ENTRY(combo->entry);
	}
	else if(GTK_IS_ENTRY(widget))
	{
	    entry = GTK_ENTRY(widget);
	}
	else
	{
	    DO_FREE_LOCALS
	    return;
	}

	/* Decode buffer into a list of URLs */
	url_list = URLDecode(
	    (const guint8 *)selection_data->data,
	    selection_data->length
	);
	if(url_list == NULL)
	{
	    DO_FREE_LOCALS
	    return;
	}

	/* Drag data is a disk object path? */
	if((info == EDV_DND_TYPE_INFO_TEXT_PLAIN) ||
	   (info == EDV_DND_TYPE_INFO_TEXT_URI_LIST) ||
	   (info == EDV_DND_TYPE_INFO_STRING)
	)
	{
	    gchar *s = STRDUP("");
	    GList *glist;
	    const url_struct *url;

	    /* Generate a new text value as a space separated list
	     * of paths obtained from the URL list
	     */
	    for(glist = url_list;
		glist != NULL;
		glist = g_list_next(glist)
	    )
	    {
		url = URL(glist->data);
		if(url == NULL)
		    continue;

		if(STRISEMPTY(url->path))
		    continue;

		/* Prefix a space if this is not the first path */
		if(*s != '\0')
		    s = strinschr(s, -1, ' ');

		/* Append this path to the string */
		s = strinsstr(s, -1, url->path);
	    }

	    /* Generated new text value? */
	    if(s != NULL)
	    {
		gtk_entry_set_text(entry, s);
		gtk_entry_set_position(entry, -1);
	    }

	    g_free(s);
	}

	DO_FREE_LOCALS
#undef DO_FREE_LOCALS
}


/*
 *	GtkEntry complete path "key_press_event" or "key_release_event"
 *	signal callback.
 */
gint EDVEntryCompletePathCB(
	GtkWidget *widget, GdkEventKey *key, gpointer data
)
{
	gint status = 0;
	gboolean press;
	GtkEntry *entry;
	edv_core_struct *core_ptr = EDV_CORE(data);
	if((widget == NULL) || (key == NULL) || (core_ptr == NULL))
	    return(status);

	press = (key->type == GDK_KEY_PRESS) ? TRUE : FALSE;

	/* Given widget must have or be a GtkEntry */
	if(GTK_IS_COMBO(widget))
	{
	    GtkCombo *combo = GTK_COMBO(widget);
	    entry = GTK_ENTRY(combo->entry);
	}
	else if(GTK_IS_ENTRY(widget))
	{
	    entry = GTK_ENTRY(widget);
	}
	else
	{
	    return(status);
	}

#define SIGNAL_EMIT_STOP        {               \
 gtk_signal_emit_stop_by_name(                  \
  GTK_OBJECT(widget),                           \
  press ?                                       \
   "key_press_event" : "key_release_event"      \
 );                                             \
}

	switch(key->keyval)
	{
	  case GDK_Tab:
	    if(press)
	    {
		gchar *s = STRDUP(gtk_entry_get_text(entry));
		if(s != NULL)
		{
		    gint status;

		    s = CompletePath(s, &status);
		    gtk_entry_set_text(entry, s);
		    gtk_entry_set_position(entry, -1);
		    g_free(s);

		    switch(status)
		    {
		      case COMPLETE_PATH_NONE:
		      case COMPLETE_PATH_AMBIGUOUS:
			gdk_beep();
			break;
		    }
		}
	    }
	    SIGNAL_EMIT_STOP
	    status = TRUE;
	    break;
	}

#undef SIGNAL_EMIT_STOP

	return(status);
}


/*
 *	Location bar icon "drag_data_get" signal callback.
 *
 *	The data is a GtkCombo.
 */
void EDVLocBarIconDragDataGet(
	GtkWidget *widget, GdkDragContext *context,
	GtkSelectionData *selection_data, guint info, guint time,
	gpointer data
)
{
	gboolean data_sent = FALSE;
	guint8 *buf;
	gint buf_len;
	GList *url_list = NULL;
	GtkEntry *entry;
	GtkCombo *combo = (GtkCombo *)data;
	url_struct *url;
	if((selection_data == NULL) || (combo == NULL))
	    return;

	entry = GTK_ENTRY(combo->entry);
	if(entry == NULL)
	    return;

	/* Generate a URL list from the current entry text */
	url = URLNew();
	url->path = STRDUP(gtk_entry_get_text(entry));
	url_list = g_list_append(url_list, url);

	/* Encode the buffer from the URL list */
	buf = URLEncode(url_list, &buf_len);
	if(buf != NULL)
	{
	    gtk_selection_data_set(
		selection_data,
		GDK_SELECTION_TYPE_STRING,
		8,		/* 8 bits per character */
		buf,
		buf_len
	    );
	    data_sent = TRUE;
	}

	g_free(buf);

	g_list_foreach(url_list, (GFunc)URLDelete, NULL);
	g_list_free(url_list);

	/* If failed to send data then send error response */
	if(!data_sent)
	{
	    const gchar *s = "Error";
	    gtk_selection_data_set(
		selection_data,
		GDK_SELECTION_TYPE_STRING,
		8,		/* 8 bits per character */
		s,
		STRLEN(s)
	    );
	    data_sent = TRUE;
	}
}


/*
 *	Emits a write protect state change signal to all of endeavour's
 *	windows who are interested.
 */
void EDVWriteProtectChangedEmit(
	edv_core_struct *core_ptr, gboolean state
)
{
	gint i;

	if(core_ptr == NULL)
	    return;

	/* Begin emitting signal */

	/* Browsers */
	for(i = 0; i < core_ptr->total_browsers; i++)
	    EDVBrowserWriteProtectChangedCB(
		core_ptr->browser[i], state
	    );

	/* Image Browsers */
	for(i = 0; i < core_ptr->total_imbrs; i++)
	    EDVImbrWriteProtectChangedCB(
		core_ptr->imbr[i], state
	    );

	/* Archivers */
	for(i = 0; i < core_ptr->total_archivers; i++)
	    EDVArchiverWriteProtectChangedCB(
		core_ptr->archiver[i], state
	    );

	/* Recycle Bin Desktop Icon */
	EDVRecBinDeskIconUpdate(core_ptr->recbin_deskicon);

	/* Recycle Bin */
	EDVRecBinWriteProtectChangedCB(core_ptr->recbin, state);

	/* Find Window */
	EDVFindWinWriteProtectChangedCB(core_ptr->findwin, state);

	/* Property Dialogs */
	for(i = 0; i < core_ptr->total_propdlgs; i++)
	    EDVPropDlgWriteProtectChangedCB(
		core_ptr->propdlg[i], state
	    );

	/* History window */
	EDVHistoryListWinWriteProtectChangedCB(
	    core_ptr->history_listwin, state
	);
}

/*
 *	Emits an object added signal.
 */
void EDVObjectAddedEmit(
	edv_core_struct *core_ptr, const gchar *path,
	const struct stat *lstat_buf
)
{
	gint i;
	gchar *dpath;


	if((core_ptr == NULL) || STRISEMPTY(path))
	    return;

	/* Create a local copy of the path */
	dpath = STRDUP(path);

	/* Begin emitting signal */

	/* Browsers */
	for(i = 0; i < core_ptr->total_browsers; i++)
	    EDVBrowserObjectAddedNotifyCB(
		core_ptr->browser[i], dpath, lstat_buf
	    );

	/* Image Browsers */
	for(i = 0; i < core_ptr->total_imbrs; i++)
	    EDVImbrObjectAddedNotifyCB(
		core_ptr->imbr[i], dpath, lstat_buf
	    );

	/* Do not emit object added signal to Archivers */




	g_free(dpath);
}

/*
 *	Emits an object modified signal.
 */
void EDVObjectModifiedEmit(
	edv_core_struct *core_ptr, const gchar *path,
	const gchar *new_path,
	const struct stat *lstat_buf
)
{
	gint i;
	gchar *dpath;

	if((core_ptr == NULL) || STRISEMPTY(path))
	    return;

	/* Create a local copy of the path */
	dpath = STRDUP(path);

	/* Begin emitting signal */

	/* Browsers */
	for(i = 0; i < core_ptr->total_browsers; i++)
	    EDVBrowserObjectModifiedNotifyCB(
		core_ptr->browser[i], dpath, new_path, lstat_buf
	    );

	/* Image browsers */
	for(i = 0; i < core_ptr->total_imbrs; i++)
	    EDVImbrObjectModifiedNotifyCB(
		core_ptr->imbr[i], dpath, new_path, lstat_buf
	    );

	/* Archivers */
	for(i = 0; i < core_ptr->total_archivers; i++)
	    EDVArchiverObjectModifiedNotifyCB(
		core_ptr->archiver[i], dpath, new_path, lstat_buf
	    );

	/* Property Dialogs */
	for(i = 0; i < core_ptr->total_propdlgs; i++)
	    EDVPropDlgObjectModifiedNotifyCB(
		core_ptr->propdlg[i], dpath, new_path, lstat_buf
	    );



	g_free(dpath);
}

/*
 *	Emits an object removed signal.
 */
void EDVObjectRemovedEmit(edv_core_struct *core_ptr, const gchar *path)
{
	gint i;
	gchar *dpath;


	if((core_ptr == NULL) || STRISEMPTY(path))
	    return;

	/* Create a local copy of the path */
	dpath = STRDUP(path);

	/* Begin emitting signal */

	/* Browsers */
	for(i = 0; i < core_ptr->total_browsers; i++)
	    EDVBrowserObjectRemovedNotifyCB(
		core_ptr->browser[i], dpath
	    );

	/* Image Browsers */
	for(i = 0; i < core_ptr->total_imbrs; i++)
	    EDVImbrObjectRemovedNotifyCB(
		core_ptr->imbr[i], dpath
	    );

	/* Archivers */
	for(i = 0; i < core_ptr->total_archivers; i++)
	    EDVArchiverObjectRemovedNotifyCB(
		core_ptr->archiver[i], dpath
	    );

	/* Property Dialogs */
	for(i = 0; i < core_ptr->total_propdlgs; i++)
	    EDVPropDlgObjectRemovedNotifyCB(
		core_ptr->propdlg[i], dpath
	    );


	g_free(dpath);
}

/*
 *	Emits a mount/unmount signal.
 */
void EDVObjectMountEmit(
	edv_core_struct *core_ptr,
	gint dev_num, edv_device_struct *dev_ptr,
	gboolean is_mounted
)
{
	gint i;

	if(core_ptr == NULL)
	    return;

	/* Begin emitting signal */

	/* Devices list window */
	EDVDevicesListWinMountNotifyCB(
	    core_ptr->device_listwin, dev_num, dev_ptr, is_mounted
	);

	/* Browsers */
	for(i = 0; i < core_ptr->total_browsers; i++)
	    EDVBrowserMountNotifyCB(
		core_ptr->browser[i], dev_num, dev_ptr, is_mounted
	    );

	/* Image Browsers */
	for(i = 0; i < core_ptr->total_imbrs; i++)
	    EDVImbrMountNotifyCB(
		core_ptr->imbr[i], dev_num, dev_ptr, is_mounted
	    );

	/* Property Dialogs */
	for(i = 0; i < core_ptr->total_propdlgs; i++)
	    EDVPropDlgMountNotifyCB(
		core_ptr->propdlg[i], dev_num, dev_ptr, is_mounted
	    );
}


/*
 *      Emits an archive object added signal to all of endeavour's
 *      windows who are interested.
 *
 *	The arch_path specifies the location of the archive that the
 *	archive object specified by path was added to. The obj contains
 *	the newly added archive object's statistics.
 */
void EDVArchiveObjectAddedNotifyCB(
	edv_core_struct *core_ptr, const gchar *arch_path,
	const gchar *path, edv_archive_object_struct *obj
)
{
	gint i;

	if(core_ptr == NULL)
	    return;

	/* Begin emitting signal */

	/* Archivers */
	for(i = 0; i < core_ptr->total_archivers; i++)
	    EDVArchiverArchiveObjectAddedNotifyCB(
		core_ptr->archiver[i], arch_path, path, obj
	    );
}

/*
 *      Emits an archive object modified signal to all of endeavour's
 *      windows who are interested.
 *
 *      The arch_path specifies the location of the archive that the
 *      archive object specified by path was modified. The obj contains
 *      the newly modified archive object's statistics and path reffers
 *	to the archive object's path.
 */
void EDVArchiveObjectModifiedNotifyCB(
	edv_core_struct *core_ptr, const gchar *arch_path,
	const gchar *path, const gchar *new_path,
	edv_archive_object_struct *obj
)
{
	gint i;

	if(core_ptr == NULL)
	    return;

	/* Begin emitting signal */

	/* Archivers */
	for(i = 0; i < core_ptr->total_archivers; i++)
	    EDVArchiverArchiveObjectModifiedNotifyCB(
		core_ptr->archiver[i], arch_path, path, new_path, obj
	    );
}

/*
 *      Emits an archive object removed signal to all of endeavour's
 *      windows who are interested.
 *
 *      The arch_path specifies the location of the archive that the
 *      archive object specified by path was removed.
 */
void EDVArchiveObjectRemovedNotifyCB(
	edv_core_struct *core_ptr, const gchar *arch_path,
	const gchar *path
)
{
	gint i;

	if(core_ptr == NULL)
	    return;

	/* Begin emitting signal */

	/* Archivers */
	for(i = 0; i < core_ptr->total_archivers; i++)
	    EDVArchiverArchiveObjectRemovedNotifyCB(
		core_ptr->archiver[i], arch_path, path
	    );
}


/*
 *	Emits a recycled object added signal.
 */
void EDVRecycledObjectAddedEmit(
	edv_core_struct *core_ptr, guint index
)
{
	gint i;
	const gchar *recycled_index_file;


	if(core_ptr == NULL)
	    return;

	/* Get path of recycled object index file */
	recycled_index_file = CFGItemListGetValueS(
	    core_ptr->cfg_list, EDV_CFG_PARM_FILE_RECYCLED_INDEX
	);

	/* Update number of recycled objects on the core structure */
#if 1
	core_ptr->last_recbin_items = EDVRecBinFIOTotalItems(
	    recycled_index_file
	);
#else
	/* Use simplified method by just checking of the index file
	 * exists which implies there is at least one object
	 *
	 * This limits the result to be 0 or 1 items
	 */
	if(recycled_index_file != NULL)
	{

	    struct stat stat_buf;
	    if(stat(recycled_index_file, &stat_buf))
		core_ptr->last_recbin_items = 0;
	    else
		core_ptr->last_recbin_items = (stat_buf.st_size > 0) ?
		    1 : 0;
	}
	else
	{
	    core_ptr->last_recbin_items = 0;
	}
#endif

	/* Begin emitting signal */

	/* Browsers */
	for(i = 0; i < core_ptr->total_browsers; i++)
	    EDVBrowserRecycledObjectAddedNotifyCB(
		core_ptr->browser[i], index
	    );

	/* Image Browsers */
	for(i = 0; i < core_ptr->total_imbrs; i++)
	    EDVImbrRecycledObjectAddedNotifyCB(
		core_ptr->imbr[i], index
	    );

	/* Archivers */
	for(i = 0; i < core_ptr->total_archivers; i++)
	    EDVArchiverRecycledObjectAddedNotifyCB(
		core_ptr->archiver[i], index
	    );

	/* Recycle Bin Desktop Icon */
	EDVRecBinDeskIconUpdate(core_ptr->recbin_deskicon);

	/* Recycle Bin */
	EDVRecBinRecycledObjectAddedNotifyCB(
	    core_ptr->recbin, index
	);
}

/*
 *	Emits a recycled object removed signal.
 */
void EDVRecycledObjectRemovedEmit(
	edv_core_struct *core_ptr, guint index
)
{
	gint i;
	const gchar *recycled_index_file;

	if(core_ptr == NULL)
	    return;

	/* Get path of recycled object index file */
	recycled_index_file = CFGItemListGetValueS(
	    core_ptr->cfg_list, EDV_CFG_PARM_FILE_RECYCLED_INDEX
	);

	/* Update number of recycled objects on the core structure */
#if 1
	core_ptr->last_recbin_items = EDVRecBinFIOTotalItems(
	    recycled_index_file
	);
#else
	/* Use simplified method by just checking of the index file
	 * exists which implies there is at least one object
	 *
	 * This limits the result to be 0 or 1 items
	 */
	if(recycled_index_file != NULL)
	{
	    struct stat stat_buf;
	    if(stat(recycled_index_file, &stat_buf))
		core_ptr->last_recbin_items = 0;
	    else
		core_ptr->last_recbin_items = (stat_buf.st_size > 0) ?
		    1 : 0;
	}
	else
	{
	    core_ptr->last_recbin_items = 0;
	}
#endif

	/* Begin emitting signal */

	/* Browsers */
	for(i = 0; i < core_ptr->total_browsers; i++)
	    EDVBrowserRecycledObjectRemovedNotifyCB(
		core_ptr->browser[i], index
	    );

	/* Image Browsers */
	for(i = 0; i < core_ptr->total_imbrs; i++)
	    EDVImbrRecycledObjectRemovedNotifyCB(
		core_ptr->imbr[i], index
	    );

	/* Archivers */
	for(i = 0; i < core_ptr->total_archivers; i++)
	    EDVArchiverRecycledObjectRemovedNotifyCB(
		core_ptr->archiver[i], index
	    );

	/* Recycle Bin Desktop Icon */
	EDVRecBinDeskIconUpdate(core_ptr->recbin_deskicon);

	/* Recycle Bin */
	EDVRecBinRecycledObjectRemovedNotifyCB(
	    core_ptr->recbin, index
	);
}


/*
 *	Emits a reconfigured notify signal.
 */
void EDVReconfiguredEmit(edv_core_struct *core_ptr)
{
	gint i;
	GtkRcStyle *standard_rcstyle, *lists_rcstyle;
	const cfg_item_struct *cfg_list;

	if(core_ptr == NULL)
	    return;

	cfg_list = core_ptr->cfg_list;

	/* Update RC styles */
	EDVUpdateRCStyles(core_ptr);
	standard_rcstyle = core_ptr->standard_rcstyle;
	lists_rcstyle = core_ptr->lists_rcstyle;

	/* Update global Tool Tips state */
	GUISetGlobalTipsState(EDV_GET_B(EDV_CFG_PARM_SHOW_TOOLTIPS));


	/* Update Popup Lists */

	/* Users & Groups Popup Lists */
	EDVUpdateIDPULists(core_ptr);

	/* Devices Update Popup List */
	EDVUpdateDevicesPUList(core_ptr);

	/* Open With Update Popup List */
	EDVUpdateOpenWithPUList(core_ptr);


	/* Begin emitting signal */

	/* File Browsers */
	for(i = 0; i < core_ptr->total_browsers; i++)
	    EDVBrowserReconfiguredNotifyCB(
		core_ptr->browser[i]
	    );

	/* Image Browsers */
	for(i = 0; i < core_ptr->total_imbrs; i++)
	    EDVImbrReconfiguredNotifyCB(
		core_ptr->imbr[i]
	    );

	/* Archivers */
	for(i = 0; i < core_ptr->total_archivers; i++)
	    EDVArchiverReconfiguredNotifyCB(
		core_ptr->archiver[i]
	    );

	/* Recycle Bin Desktop Icon */
	if(CFGItemListGetValueI(
	    core_ptr->cfg_list, EDV_CFG_PARM_RECBIN_SHOW_DESKTOP_ICON
	))
	    EDVMapRecBinDeskIcon(core_ptr);
	else
	    EDVRecBinDeskIconUnmap(core_ptr->recbin_deskicon);

	/* Recycle Bin */
	EDVRecBinReconfiguredNotifyCB(core_ptr->recbin);

	/* Find Window */
	EDVFindWinReconfiguredNotifyCB(core_ptr->findwin);

	/* Property Dialogs */
	for(i = 0; i < core_ptr->total_propdlgs; i++)
	    EDVPropDlgReconfiguredNotifyCB(
		core_ptr->propdlg[i]
	    );

	/* History List Window */
	EDVHistoryListWinReconfiguredNotifyCB(
	    core_ptr->history_listwin
	);

	/* Devices List Window */
	EDVDevicesListWinReconfiguredNotifyCB(
	    core_ptr->device_listwin
	);

	/* MIME Types List Window */
	EDVMimeTypesListWinReconfiguredNotifyCB(
	    core_ptr->mimetype_listwin
	);

}


/*
 *	Emits a MIME Type added signal.
 */
void EDVMimeTypeAddedEmit(
	edv_core_struct *core_ptr,
	gint mt_num, edv_mimetype_struct *mt_ptr
)
{
	gint i;

	if(core_ptr == NULL)
	    return;

	/* Update MIME Types class list index hints due to change in
	 * the MIME Types list ordering
	 */
	EDVUpdateMIMETypeHintIndices(core_ptr);

	/* Reget list of application MIME Types on the `open with'
	 * popup list
	 */
	EDVUpdateOpenWithPUList(core_ptr);


	/* Begin emitting signal */

	/* Mime Types list window */
	EDVMimeTypesListWinMimeTypeAddedCB(
	    core_ptr->mimetype_listwin, mt_num, mt_ptr
	);

	/* Browsers */
	for(i = 0; i < core_ptr->total_browsers; i++)
	    EDVBrowserMimeTypeAddedCB(
		core_ptr->browser[i], mt_num, mt_ptr
	    );

	/* Image Browsers */
	for(i = 0; i < core_ptr->total_imbrs; i++)
	    EDVImbrMimeTypeAddedCB(
		core_ptr->imbr[i], mt_num, mt_ptr
	    );

	/* Archivers */
	for(i = 0; i < core_ptr->total_archivers; i++)
	    EDVArchiverMimeTypeAddedCB(
		core_ptr->archiver[i], mt_num, mt_ptr
	    );

	/* Recycle Bin */
	EDVRecBinMimeTypeAddedCB(
	    core_ptr->recbin, mt_num, mt_ptr
	);
}

/*
 *	Emits a MIME Type modified signal.
 */
void EDVMimeTypeModifiedEmit(
	edv_core_struct *core_ptr,
	gint mt_num, edv_mimetype_struct *mt_ptr
)
{
	gint i;

	if(core_ptr == NULL)
	    return;

	/* Update MIME Types class list index hints due to change in
	 * the MIME Types list ordering
	 */
	EDVUpdateMIMETypeHintIndices(core_ptr);

	/* Reget list of application MIME Types on the `open with'
	 * popup list
	 */
	EDVUpdateOpenWithPUList(core_ptr);


	/* Begin emitting signal */

	/* MIME Types list window */
	EDVMimeTypesListWinMimeTypeModifiedCB(
	    core_ptr->mimetype_listwin, mt_num, mt_ptr
	);

	/* Browsers */
	for(i = 0; i < core_ptr->total_browsers; i++)
	    EDVBrowserMimeTypeModifiedCB(
		core_ptr->browser[i], mt_num, mt_ptr
	    );

	/* Image browsers */
	for(i = 0; i < core_ptr->total_imbrs; i++)
	    EDVImbrMimeTypeModifiedCB(
		core_ptr->imbr[i], mt_num, mt_ptr
	    );

	/* Archivers */
	for(i = 0; i < core_ptr->total_archivers; i++)
	    EDVArchiverMimeTypeModifiedCB(
		core_ptr->archiver[i], mt_num, mt_ptr
	    );

	/* Recycle Bin */
	EDVRecBinMimeTypeModifiedCB(
	    core_ptr->recbin, mt_num, mt_ptr
	);
}

/*
 *      Emits a MIME Type removed signal to all of endeavour's windows who
 *      are interested.
 */
void EDVMimeTypeRemovedEmit(
	edv_core_struct *core_ptr, gint mt_num
)
{
	gint i;

	if(core_ptr == NULL)
	    return;

	/* Update MIME Types class list index hints due to change in
	 * the MIME Types list ordering
	 */
	EDVUpdateMIMETypeHintIndices(core_ptr);

	/* Reget list of application MIME Types on the `open with'
	 * popup list
	 */
	EDVUpdateOpenWithPUList(core_ptr);


	/* Begin emitting signal */

	/* MIME Types list window */
	EDVMimeTypesListWinMimeTypeRemovedCB(
	    core_ptr->mimetype_listwin, mt_num
	);

	/* Browsers */
	for(i = 0; i < core_ptr->total_browsers; i++)
	    EDVBrowserMimeTypeRemovedCB(
		core_ptr->browser[i], mt_num
	    );

	/* Image Browsers */
	for(i = 0; i < core_ptr->total_imbrs; i++)
	    EDVImbrMimeTypeRemovedCB(
		core_ptr->imbr[i], mt_num
	    );

	/* Archivers */
	for(i = 0; i < core_ptr->total_archivers; i++)
	    EDVArchiverMimeTypeRemovedCB(
		core_ptr->archiver[i], mt_num
	    );

	/* Recycle Bin */
	EDVRecBinMimeTypeRemovedCB(
	    core_ptr->recbin, mt_num
	);
}


/*
 *	Emits a device reference added signal.
 */
void EDVDeviceAddedEmit(
	edv_core_struct *core_ptr,
	gint dev_num, edv_device_struct *dev_ptr
)
{
	gint i;

	if(core_ptr == NULL)
	    return;

	/* Reget devices list on devices popup list widget */
	EDVUpdateDevicesPUList(core_ptr);


	/* Begin emitting signal */

	/* Devices list window */
	EDVDevicesListWinDeviceAddedCB(
	    core_ptr->device_listwin, dev_num, dev_ptr
	);

	/* Browsers */
	for(i = 0; i < core_ptr->total_browsers; i++)
	    EDVBrowserDeviceAddedCB(
		core_ptr->browser[i], dev_num, dev_ptr
	    );

	/* Image Browsers */
	for(i = 0; i < core_ptr->total_imbrs; i++)
	    EDVImbrDeviceAddedCB(
		core_ptr->imbr[i], dev_num, dev_ptr
	    );
}

/*
 *	Emits a device reference modified signal.
 */
void EDVDeviceModifiedEmit(
	edv_core_struct *core_ptr,
	gint dev_num, edv_device_struct *dev_ptr
)
{
	gint i;

	if(core_ptr == NULL)
	    return;

	/* Reget devices list on devices popup list widget */
	EDVUpdateDevicesPUList(core_ptr);


	/* Begin emitting signal */

	/* Devices list window */
	EDVDevicesListWinDeviceModifiedCB(
	    core_ptr->device_listwin, dev_num, dev_ptr
	);

	/* Browsers */
	for(i = 0; i < core_ptr->total_browsers; i++)
	    EDVBrowserDeviceModifiedCB(
		core_ptr->browser[i], dev_num, dev_ptr
	    );

	/* Image Browsers */
	for(i = 0; i < core_ptr->total_imbrs; i++)
	    EDVImbrDeviceModifiedCB(
		core_ptr->imbr[i], dev_num, dev_ptr
	    );
}

/*
 *	Emits a device reference removed signal.
 */
void EDVDeviceRemovedEmit(
	edv_core_struct *core_ptr, gint dev_num
)
{
	gint i;

	if(core_ptr == NULL)
	    return;

	/* Reget devices list on devices popup list widget */
	EDVUpdateDevicesPUList(core_ptr);


	/* Begin emitting signal */

	/* Devices list window */
	EDVDevicesListWinDeviceRemovedCB(
	    core_ptr->device_listwin, dev_num
	);

	/* Browsers */
	for(i = 0; i < core_ptr->total_browsers; i++)
	    EDVBrowserDeviceRemovedCB(
		core_ptr->browser[i], dev_num
	    );

	/* Image Browsers */
	for(i = 0; i < core_ptr->total_imbrs; i++)
	    EDVImbrDeviceRemovedCB(
		core_ptr->imbr[i], dev_num
	    );
}


/*
 *	Emits a history event added signal.
 */
void EDVHistoryAddedEmit(
	edv_core_struct *core_ptr,
	gint h_num, edv_history_struct *h_ptr
)
{
	if(core_ptr == NULL)
	    return;

	/* Begin emitting signal */

	/* History window */
	EDVHistoryListWinHistoryAddedCB(
	    core_ptr->history_listwin,
	    h_num, h_ptr
	);
}

/*
 *	Emits a history event removed signal.
 */
void EDVHistoryRemovedEmit(
	edv_core_struct *core_ptr,
	gint h_num
)
{
	if(core_ptr == NULL)
	    return;

	/* Begin emitting signal */

	/* History window */
	EDVHistoryListWinHistoryRemovedCB(
	    core_ptr->history_listwin,
	    h_num
	);
}
