#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>

#include "../include/string.h"

#include "guiutils.h"

#include "edv_types.h"
#include "cfg.h"
#include "edv_obj.h"
#include "edv_mime_type.h"
#include "edv_mime_types_list.h"
#include "edv_device.h"
#include "edv_device_mount.h"
#include "edv_devices_list.h"
#include "edv_status_bar.h"
#include "imbr.h"
#include "imbr_cb.h"
#include "imbr_op_cb.h"
#include "imbr_tlist.h"
#include "imbr_img_view.h"
#include "imbr_dnd.h"
#include "endeavour2.h"
#include "edv_op.h"
#include "edv_cb.h"
#include "edv_utils.h"
#include "edv_utils_gtk.h"
#include "edv_list_seek.h"
#include "edv_cfg_list.h"
#include "config.h"


/* GTK+ Signal Callbacks */
void EDVImbrTListItemDestroyCB(gpointer data);

gint EDVImbrDeleteEventCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
);
gint EDVImbrKeyEventCB(
	 GtkWidget *widget, GdkEventKey *key, gpointer data
);
gint EDVImbrButtonPressEventCB(
	GtkWidget *widget, GdkEventButton *button, gpointer data
);

void EDVImbrHandleChildAttachedCB(
	GtkHandleBox *handle_box, GtkWidget *child, gpointer data
);
void EDVImbrHandleChildDetachedCB(
	GtkHandleBox *handle_box, GtkWidget *child, gpointer data
);

void EDVImbrTListSelectCB(
	tlist_struct *tlist, GdkEventButton *button, gint thumb_num,
	gpointer data
);
void EDVImbrTListUnselectCB(
	tlist_struct *tlist, GdkEventButton *button, gint thumb_num,
	gpointer data
);

void EDVImbrComboActivateCB(GtkWidget *widget, gpointer data);

gint EDVImbrLoadingTOCB(gpointer data);

/* Menu Item Callbacks */
void EDVImbrMenuItemCB(GtkWidget *widget, gpointer data);
gint EDVImbrMenuItemEnterCB(
	GtkWidget *widget, GdkEventCrossing *crossing, gpointer data
);
gint EDVImbrMenuItemLeaveCB(
	GtkWidget *widget, GdkEventCrossing *crossing, gpointer data
);

/* Paths List Get Callback */
GList *EDVImbrGetSelectedPathsListCB(gpointer data);

/* Go To Directory Callback */
void EDVImbrGotoDirectoryCB(gpointer data, const gchar *path);

/* Mount Bar Callbacks */
void EDVImbrMountBarMountCB(
	edv_mount_bar_struct *mb, gint dev_num, edv_device_struct *dev,
	gpointer data
);
void EDVImbrMountBarEjectCB(
	edv_mount_bar_struct *mb, gint dev_num, edv_device_struct *dev,
	gpointer data
);
void EDVImbrMountBarGoToCB(
	edv_mount_bar_struct *mb, gint dev_num, edv_device_struct *dev,
	gpointer data
);

/* Find Bar Callbacks */
const gchar *EDVImbrFindBarLocationCB(
	edv_findbar_struct *fb, gpointer data
);
void EDVImbrFindBarStartCB(edv_findbar_struct *fb, gpointer data);
void EDVImbrFindBarEndCB(
	edv_findbar_struct *fb, gint total_matches, gpointer data
);
void EDVImbrFindBarMatchCB(
	const gchar *path, const struct stat *lstat_buf,
	const gchar *excerpt, gint line_index,
	gpointer data
);

/* Status Bar Callbacks */
void EDVImbrStatusMessageCB(const gchar *message, gpointer data);
void EDVImbrStatusProgressCB(gfloat progress, gpointer data);

/* Write Protect Changed Callback */
void EDVImbrWriteProtectChangedCB(
	edv_imbr_struct *imbr, gboolean state
);

/* Object Callbacks */
void EDVImbrObjectAddedNotifyCB(
	edv_imbr_struct *imbr, const gchar *path,
	const struct stat *lstat_buf
);
void EDVimbrObjectModifiedNotifyCB(
	edv_imbr_struct *imbr, const gchar *path,
	const gchar *new_path,
	const struct stat *lstat_buf
);
void EDVImbrObjectRemovedNotifyCB(
	edv_imbr_struct *imbr, const gchar *path
);

/* Mount/Unmount Callback */
void EDVImbrMountNotifyCB(
	edv_imbr_struct *imbr,
	gint dev_num, edv_device_struct *dev,
	gboolean is_mounted
);

/* Recycled Object Callbacks */
void EDVImbrRecycledObjectAddedNotifyCB(
	edv_imbr_struct *imbr, guint index
);
void EDVImbrRecycledObjectRemovedNotifyCB(
	edv_imbr_struct *imbr, guint index
);

/* Reconfigured Callback */
void EDVImbrReconfiguredNotifyCB(edv_imbr_struct *imbr);

/* MIME Type Callbacks */
void EDVImbrMimeTypeAddedCB(
	edv_imbr_struct *imbr,
	gint mt_num, edv_mime_type_struct *mt
);
void EDVImbrMimeTypeModifiedCB(
	edv_imbr_struct *imbr,
	gint mt_num, edv_mime_type_struct *mt
);
void EDVImbrMimeTypeRemovedCB(
	edv_imbr_struct *imbr, gint mt_num
);

/* Device Callbacks */
void EDVImbrDeviceAddedCB(
	edv_imbr_struct *imbr,
	gint dev_num, edv_device_struct *dev
);
void EDVImbrDeviceModifiedCB(
	edv_imbr_struct *imbr,
	gint dev_num, edv_device_struct *dev
);
void EDVImbrDeviceRemovedCB(
	edv_imbr_struct *imbr, gint dev_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)


/*
 *	Image Browser thumbs list "destroy" signal callback.
 */
void EDVImbrTListItemDestroyCB(gpointer data)
{
	EDVObjectDelete(EDV_OBJECT(data));
}


/*
 *	Image Browser toplevel GtkWidget "delete_event" signal callback.
 */
gint EDVImbrDeleteEventCB(
	GtkWidget *widget, GdkEvent *event, gpointer data
)
{
	edv_imbr_struct *imbr = EDV_IMBR(data);
	if(imbr == NULL)
	    return(TRUE);

	if(imbr->processing)
	    return(TRUE);

	EDVImbrOPClose(imbr);

	return(TRUE);
}

/*
 *	Image Browser "key_press_event" or "key_release_event" signal
 *	callback.
 */
gint EDVImbrKeyEventCB(
	 GtkWidget *widget, GdkEventKey *key, gpointer data
)
{
	gint status = FALSE;
	gboolean is_press;
	gint etype;
	guint keyval, state;
	GtkWidget *w, *focus_widget;
	const cfg_item_struct *cfg_list;
	tlist_struct *tlist;
 	edv_core_struct *core;
	edv_imbr_struct *imbr = EDV_IMBR(data);
	if((widget == NULL) || (key == NULL) || (imbr == NULL))
	    return(status);

	if(imbr->processing)
	    return(status);

	tlist = imbr->tlist;
	core = imbr->core;
	if((tlist == NULL) || (core == NULL))
	    return(status);

	w = imbr->toplevel;
	focus_widget = (w != NULL) ? GTK_WINDOW(w)->focus_widget : NULL;
	cfg_list = core->cfg_list;
	etype = key->type;
	is_press = (etype == GDK_KEY_PRESS) ? TRUE : FALSE;
	keyval = key->keyval;
	state = key->state;

/* Stop emit of signal */
#define DO_STOP_KEY_SIGNAL_EMIT	{		\
 gtk_signal_emit_stop_by_name(			\
  GTK_OBJECT(widget),				\
  is_press ?					\
   "key_press_event" : "key_release_event"	\
 );						\
}

	/* If the focus_widget is not a GtkEditable then check if the
	 * keyval is an accelerator key before all subsequence checks
	 */
	if((focus_widget != NULL) ?
	    !GTK_IS_EDITABLE(focus_widget) : TRUE
	)
	{
	    edv_imbr_op op = (edv_imbr_op)EDVMatchAccelKeyOPID(
		cfg_list, EDV_CFG_PARM_IMBR_ACCELERATOR_KEYS,
		keyval, state
	    );
	    if(op > 0)
	    {
		if(is_press)
		{
		    edv_imbr_opid_struct *opid = EDVImbrMatchOPID(
			imbr, op
		    );
		    if(opid != NULL)
			EDVImbrOPCB(NULL, -1, opid);
		}
		DO_STOP_KEY_SIGNAL_EMIT
		status = TRUE;
		return(status);
	    }
	}

	/* Check which widget this signal is for */

	/* Thumbs List */
	if(widget == tlist->list_da)
	{
	    switch(keyval)
	    {
	      default:
		/* For all other alphanumeric character keys and while
		 * no modifier keys are held, attempt to seek to the
		 * item who's name starts with the letter of the key
		 * that was pressed
		 */
		if(isalnum((int)keyval) && is_press &&
		   !(state & GDK_CONTROL_MASK) &&
		   !(state & GDK_SHIFT_MASK)    
		)
		{
		    TListFreeze(tlist);
		    EDVTListSeekCharacter(
			tlist, 0, keyval
		    );
		    TListThaw(tlist);
		    DO_STOP_KEY_SIGNAL_EMIT
		    status = TRUE;
		}
		break;
	    }
	}

	return(status);
#undef DO_STOP_KEY_SIGNAL_EMIT
}

/*
 *	Image Browser GtkWidget "button_press_event" signal callback.
 */
gint EDVImbrButtonPressEventCB(
	GtkWidget *widget, GdkEventButton *button, gpointer data
)
{
	gint status = FALSE;
	gint etype;
	guint state;
	const cfg_item_struct *cfg_list;
	edv_core_struct *core;
	tlist_struct *tlist;
	edv_imbr_struct *imbr = EDV_IMBR(data);
	if((widget == NULL) || (button == NULL) || (imbr == NULL))
	    return(status);

	if(imbr->processing)
	    return(status);

	tlist = imbr->tlist;
	core = imbr->core;
	if((tlist == NULL) || (core == NULL))
	    return(status);

	cfg_list = core->cfg_list;

	/* Get event type */
	etype = button->type;

	state = button->state;

	/* Event occured on the thumbs list? */
	if(widget == tlist->list_da)
	{
	    gint	thumb_num = -1, total_selected_thumbs = 0,
			single_selected_thumb = -1;
	    GList *glist = tlist->selection;


	    /* Get total number of selected thumbs and set
	     * single_selected_thumb to be the only single selected
	     * thumb or -1 of there are none or multiple thumbs
	     * selected
	     */
	    while(glist != NULL)
	    {
		single_selected_thumb = (gint)glist->data;
		total_selected_thumbs++;
		glist = g_list_next(glist);
	    }
	    if(total_selected_thumbs != 1)
		single_selected_thumb = -1;

	    /* Get thumb that button event occured over */
	    TListGetSelection(
		tlist, button->x, button->y, &thumb_num, NULL, NULL
	    );

	    /* Handle by button number */
	    switch(button->button)
	    {
	      case 3:
		if(etype == GDK_BUTTON_PRESS)
		{
		    GtkMenu *menu;

		    /* Select item before mapping menu? */
		    if(EDV_GET_B(EDV_CFG_PARM_RIGHT_CLICK_MENU_SELECTS) &&
		       (thumb_num > -1)
		    )
		    {
			/* Select the thumb that the button was pressed over */
			TListFreeze(tlist);
			if(!(button->state & GDK_CONTROL_MASK) &&
			   !(button->state & GDK_SHIFT_MASK) &&
			   (thumb_num != single_selected_thumb)
			)
			    TListUnselectAll(tlist);
			tlist->focus_thumb = thumb_num;
			TListSelectThumb(tlist, thumb_num);
			TListThaw(tlist);
		    }

		    /* Update all menus and map right click menu */
		    EDVImbrUpdateMenus(imbr);
		    menu = (GtkMenu *)imbr->tlist_menu;
		    if(menu != NULL)
			gtk_menu_popup(
			    menu, NULL, NULL,
			    NULL, NULL,
			    button->button, button->time
			);
		}
		status = TRUE;
		break;

	      case 2:
		if(etype == GDK_BUTTON_PRESS)
		{
		    if(thumb_num > -1)
			EDVImbrTListDoFPromptRename(imbr, thumb_num);
		}
		status = TRUE;
		break;

	      case 1:
		/* Double click? */
		if(etype == GDK_2BUTTON_PRESS)
		{
		    if(thumb_num > -1)
		    {
			EDVImbrTListOpen(
			    imbr,
			    thumb_num,
			    state		/* Modifier keys */
			);
			status = TRUE;
		    }
		}
		break;
	    }
	}
	/* Event occured on the image viewer? */
	else if(widget == (GtkWidget *)ImgViewGetViewWidget(imbr->imgview))
	{
	    switch(button->button)
	    {
	      case 3:
		if(etype == GDK_BUTTON_PRESS)
		{
#if 0
		    GtkMenu *menu;

		    /* Update all menus and map right click menu */
		    EDVImbrUpdateMenus(imbr);
		    menu = (GtkMenu *)imbr->tlist_menu;
		    if(menu != NULL)
			gtk_menu_popup(
			    menu, NULL, NULL,
			    NULL, NULL,
			    button->button, button->time
			);
#endif
		}
		status = TRUE;
		break;

	      case 1:
		/* Double click? */
		if(etype == GDK_2BUTTON_PRESS)
		{
		    gint thumb_num = imbr->tlist_selected_thumb;
		    if(thumb_num > -1)
		    {
			EDVImbrTListOpen(
			    imbr,
			    thumb_num,
			    state		/* Modifier keys */
			);
			status = TRUE;
		    }
		}
		break;
	    }
	}

	return(status);
}

/*
 *	GtkHandleBox "child_attached" signal callback.
 */
void EDVImbrHandleChildAttachedCB(
	GtkHandleBox *handle_box, GtkWidget *child, gpointer data
)
{
	edv_imbr_struct *imbr = EDV_IMBR(data);
	if((handle_box == NULL) || (imbr == NULL))
	    return;

	gtk_widget_queue_resize(
	    gtk_widget_get_toplevel(GTK_WIDGET(handle_box))
	);
}

/*
 *	GtkHandleBox "child_detached" signal callback.
 */
void EDVImbrHandleChildDetachedCB(
	GtkHandleBox *handle_box, GtkWidget *child, gpointer data
)
{
	edv_imbr_struct *imbr = EDV_IMBR(data);
	if((handle_box == NULL) || (imbr == NULL))
	    return;

	gtk_widget_queue_resize(
	    gtk_widget_get_toplevel(GTK_WIDGET(handle_box))
	);
}


/*
 *	Image Browser thumbs list select thumb callback.
 */
void EDVImbrTListSelectCB(
	tlist_struct *tlist, GdkEventButton *button, gint thumb_num,
	gpointer data
)
{
	tlist_thumb_struct *thumb;
	edv_core_struct *core;
	edv_imbr_struct *imbr = EDV_IMBR(data);
	if((tlist == NULL) || (imbr == NULL))
	    return;

	if(imbr->processing)
	    return;

	core = imbr->core;
	if(core == NULL)
	    return;

	thumb = ((thumb_num >= 0) && (thumb_num < tlist->total_thumbs)) ?
	    tlist->thumb[thumb_num] : NULL;

	if(tlist == imbr->tlist)
	{
	    /* Change in selection? */
	    if(imbr->tlist_selected_thumb != thumb_num)
	    {
		const gint nselected = g_list_length(tlist->selection);
		edv_object_struct *obj = EDV_OBJECT(
		    TListGetThumbData(tlist, thumb_num)
		);

		/* Scroll if selected thumb is not visible */
		if(TListIsThumbVisible(tlist, thumb_num) !=
		    GTK_VISIBILITY_FULL
		)
		    TListMoveTo(tlist, thumb_num, 0.5f);

		/* Update current selected thumb on the tlist */
		imbr->tlist_selected_thumb = thumb_num;

		/* Match device index number from core structure's list
		 * of devices who's mount path matches the selected
		 * object's path
		 */
		if(obj != NULL)
		    EDVDevicesListMatchMountPath(
			core->device, core->total_devices,
			&imbr->selected_dev_num,
			obj->full_path
		    );

		/* Update DND icon for thumbs list */
		EDVImbrTListDNDSetIcon(imbr, thumb_num);

		/* Update title and status bar if selected object is
		 * valid
		 */
		if((obj != NULL) ? !STRISEMPTY(obj->name) : FALSE)
		{
		    gchar *s, *size_str = NULL;
		    const gchar	*type_str = NULL,
				*full_path = obj->full_path;
		    edv_mime_type_struct *m = EDVMimeTypesListMatchPath(
			core->mimetype, core->total_mimetypes,
			obj->full_path
		    );

		    /* If only one thumb is selected then display it
		     * on the Image Browser's ImgView
		     */
		    if((nselected == 1) &&
		       (thumb != NULL)
		    )
		    {
			gboolean need_reload_thumb = FALSE;

			if(thumb->load_state != TLIST_LOAD_STATE_LOADED)
			{
			    need_reload_thumb = TRUE;
			}
			else if(!STRISEMPTY(full_path))
			{
			    struct stat stat_buf;
			    if(!stat(full_path, &stat_buf))
			    {
				if((gulong)stat_buf.st_mtime != obj->modify_time)
				{
				    obj->modify_time = (gulong)stat_buf.st_mtime;
				    need_reload_thumb = TRUE;
				}
				if((gulong)stat_buf.st_size != obj->size)
				{
				    obj->size = (gulong)stat_buf.st_size;
				    need_reload_thumb = TRUE;
				}
			    }
			}

			EDVImbrSetBusy(imbr, TRUE);
			GUIBlockInput(imbr->toplevel, TRUE);
			imbr->processing = TRUE;

			EDVImbrImgViewLoad(
			    imbr,
			    full_path,
			    need_reload_thumb ? thumb_num : -1
			);

			imbr->processing = FALSE;
			GUIBlockInput(imbr->toplevel, FALSE);
			EDVImbrSetBusy(imbr, FALSE);
		    }

		    /* Update the open to menus */
		    EDVImbrOpenToMenuRegenerate(imbr, m);

		    /* Get object type string and size string */
		    switch(obj->type)
		    {
		      case EDV_OBJECT_TYPE_UNKNOWN:
			type_str = "Object";
			size_str = NULL;
			break;
		      case EDV_OBJECT_TYPE_FILE:
			type_str = "File";
			size_str = g_strdup_printf(
			    " (%s byte%s)",
			    EDVGetObjectSizeStr(
				imbr->core,
				obj->size
			    ),
			    (obj->size == 1) ? "" : "s"
			);
			break;
		      case EDV_OBJECT_TYPE_DIRECTORY:
			type_str = "Directory";
			size_str = NULL;
			break;
		      case EDV_OBJECT_TYPE_LINK:
			type_str = "Link";
			size_str = NULL;
			break;
		      case EDV_OBJECT_TYPE_DEVICE_BLOCK:
			type_str = "Block device";
			size_str = NULL;
			break;
		      case EDV_OBJECT_TYPE_DEVICE_CHARACTER:
			type_str = "Character device";
			size_str = NULL;
			break;
		      case EDV_OBJECT_TYPE_FIFO:
			type_str = "FIFO pipe";
			size_str = NULL;
			break;
		      case EDV_OBJECT_TYPE_SOCKET:
			type_str = "Socket";
			size_str = NULL;
			break;
		      case EDV_OBJECT_TYPE_ERROR:
			type_str = "Error";
			size_str = NULL;
			break;
		    }

		    /* Set status bar message */
		    if(nselected > 1)
			s = g_strdup_printf(
			    "%s objects selected",
			    EDVGetObjectSizeStr(core, nselected)
			);
		    else if(!strcmp(obj->name, ".."))
			s = g_strdup_printf(
			    "Parent directory selected"
			);
		    else
			s = g_strdup_printf(
			    "%s \"%s\" selected%s",
			    type_str, obj->name,
			    (size_str != NULL) ? size_str : ""
			);
		    EDVStatusBarMessage(
			imbr->status_bar, s, FALSE
		    );
		    g_free(s);
		    g_free(size_str);
		}

		EDVImbrUpdateMenus(imbr);
	    }
	}
}

/*
 *	Image Browser thumbs list unselect thumb callback.
 */
void EDVImbrTListUnselectCB(
	tlist_struct *tlist, GdkEventButton *button, gint thumb_num,
	gpointer data
)
{
	edv_core_struct *core;
	edv_imbr_struct *imbr = EDV_IMBR(data);
	if((tlist == NULL) || (imbr == NULL))
	    return;

	if(imbr->processing)
	    return;

	core = imbr->core;
	if(core == NULL)
	    return;

	if(tlist == imbr->tlist)
	{
	    gchar *s;
	    const gint nselected = g_list_length(tlist->selection);
	    GList *glist = tlist->selection_end;
	    const gint thumb_num = (glist != NULL) ? (gint)glist->data : -1;
	    edv_mime_type_struct *m = NULL;
	    if(thumb_num > -1)
	    {
		/* Last selected thumb changed? */
		if(imbr->tlist_selected_thumb != thumb_num)
		{
		    edv_object_struct *obj = EDV_OBJECT(
			TListGetThumbData(tlist, thumb_num)
		    );
		    if(obj != NULL)
		    {
			const gchar *path = obj->full_path;

			m = EDVMimeTypesListMatchPath(
			    core->mimetype, core->total_mimetypes,
			    path
			);

			/* Update last selected thumb and device */
			imbr->tlist_selected_thumb = thumb_num;
			EDVDevicesListMatchMountPath(
			    core->device, core->total_devices,
			    &imbr->selected_dev_num,
			    path
			);
		    }
		}

		/* Only one thumb remained selected? */
		if(nselected == 1)
		{
/* TODO */
		}
	    }
	    else
	    {
		/* Mark that no thumb and device are selected */
		imbr->tlist_selected_thumb = -1;
		imbr->selected_dev_num = -1;
	    }

	    /* Update the open to menus */
	    EDVImbrOpenToMenuRegenerate(imbr, m);

	    /* Update the status bar message */
	    if(nselected > 0)
		s = g_strdup_printf(
		    "%s object%s selected",
		    EDVGetObjectSizeStr(core, nselected),
		    (nselected == 1) ? "" : "s"
		);
	    else
		s = STRDUP("No objects selected");
	    EDVStatusBarMessage(
		imbr->status_bar, s, FALSE
	    );
	    g_free(s);

	    EDVImbrUpdateMenus(imbr);
	}
}

/*
 *	Image Browser Location Combo activate callback.
 */
void EDVImbrComboActivateCB(GtkWidget *widget, gpointer data)
{
	GtkWidget *toplevel;
	edv_imbr_struct *imbr = EDV_IMBR(data);
	if((widget == NULL) || (imbr == NULL))
	    return;

	if(imbr->processing)
	    return;

	toplevel = imbr->toplevel;

	/* Check which widget was activated */
	if(widget == imbr->location_combo)
	{
	    GtkCombo *combo = GTK_COMBO(widget);
	    GtkEntry *entry = GTK_ENTRY(combo->entry);
	    gchar *path = EDVEvaluatePath(
		NULL, gtk_entry_get_text(entry)
	    );
	    if(path != NULL)
	    {
		EDVImbrSetBusy(imbr, TRUE);
		GUIBlockInput(toplevel, TRUE);

		/* Update location combo, this is really just to record
		 * the new value as history. The combo will be updated
		 * again (without recording history) if the new path is
		 * valid and selected in the callbacks triggered by
		 * EDVImbrSelectPath() farther below
		 */
		EDVImbrSetLocation(imbr, path, TRUE);

		/* Reset loading procedure values and get new directory
		 * listing, then initialize loading procedure values for
		 * the loading of images from the directory specified by
		 * path
		 */
		EDVImbrSelectPath(imbr, path);

		EDVImbrUpdateMenus(imbr);

		g_free(path);

		GUIBlockInput(toplevel, FALSE);
		EDVImbrSetBusy(imbr, FALSE);
	    }
	}
}


/*
 *	Loading procedure timeout callback.
 */
gint EDVImbrLoadingTOCB(gpointer data)
{
	gint status;
	const cfg_item_struct *cfg_list;
	edv_core_struct *core;
	edv_imbr_struct *imbr = EDV_IMBR(data);
	if(imbr == NULL)
	    return(FALSE);

	if(gtk_events_pending() > 0)
	    return(TRUE);

	if(imbr->processing)
	    return(TRUE);

#define DO_FINAL_RETURN		{	\
					\
 /* Reset loading timeout id */		\
 imbr->loading_tocb = 0;		\
					\
 EDVImbrSetPassiveBusy(imbr, FALSE);	\
					\
 return(FALSE);				\
}

#define DO_RESCHEDUAL_RETURN	{	\
					\
 /* Set new loading timeout callback */	\
 imbr->loading_tocb = gtk_timeout_add(	\
  100l,					\
  EDVImbrLoadingTOCB, imbr		\
 );					\
					\
 /* Return FALSE since we reschedualed	\
  * a timeout callback to this function	\
  */					\
 return(FALSE);				\
}

	core = imbr->core;
	if(core == NULL)
	{
	    DO_FINAL_RETURN
	}

	cfg_list = core->cfg_list;

	/* Stop loading? */
	if(imbr->stop_count > 0)
	{
	    imbr->stop_count = 0;
	    imbr->loading_tocb = 0;

	    EDVStatusBarMessage(
		imbr->status_bar, "Loading interrupted", FALSE
	    );
	    EDVStatusBarProgress(imbr->status_bar, 0.0f, FALSE);
	    EDVImbrUpdateMenus(imbr);
	    EDVImbrSetPassiveBusy(imbr, FALSE);

	    return(FALSE);
	}

	/* Load the next thumb */
	status = EDVImbrTListLoadIterate(
	    imbr, TRUE,
	    EDV_GET_B(EDV_CFG_PARM_IMBR_THUMB_NO_ENLARGE)
	);
#if 0
g_print(
 "EDVImbrTListLoadIterate(): status=%i\n",
 status
);
#endif
	switch(status)
	{
	  case 0:	/* All thumb loaded */
	    imbr->loading_tocb = 0;
	    EDVImbrUpdateMenus(imbr);
	    EDVImbrSetPassiveBusy(imbr, FALSE);
	    return(FALSE);
	    break;

	  case 1:
	    /* Thumb loaded successfully, but there still may be more
	     * thumbs that need to be loaded
	     */
	    DO_RESCHEDUAL_RETURN
	    break;

	  case -3:	/* System error */
	    DO_FINAL_RETURN
	    break;

	  default:	/* General error */
	    /* It is safe to continue again after this error since
	     * this thumb that failed to load will not be attempted
	     * to be loaded again
	     */
	    DO_RESCHEDUAL_RETURN
	    break;
	}
#undef DO_FINAL_RETURN
#undef DO_RESCHEDUAL_RETURN
}


/*
 *	Menu item "activate" signal callback.
 *
 *	The data must be a edv_imbr_opid_struct *.
 */
void EDVImbrMenuItemCB(GtkWidget *widget, gpointer data)
{
	EDVImbrOPCB(NULL, -1, data);
}

/*
 *	Menu item "enter_notify_event" signal callback.
 *
 *	The data must be a edv_imbr_opid_struct *.
 */
gint EDVImbrMenuItemEnterCB(
	GtkWidget *widget, GdkEventCrossing *crossing, gpointer data
)
{
	EDVImbrOPEnterCB(NULL, -1, data);
	return(TRUE);
}

/*
 *	Menu item "leave_notify_event" signal callback.
 *
 *	The data must be a edv_imbr_opid_struct *.
 */
gint EDVImbrMenuItemLeaveCB(
	GtkWidget *widget, GdkEventCrossing *crossing, gpointer data
)
{
	EDVImbrOPLeaveCB(NULL, -1, data);
	return(TRUE);
}


/*
 *	Open paths List Get callback.
 */
GList *EDVImbrGetSelectedPathsListCB(gpointer data)
{
	GList *paths_list = NULL;
	edv_imbr_struct *imbr = EDV_IMBR(data);
	if(imbr == NULL)
	    return(paths_list);

	if(TRUE)
	{
	    tlist_struct *tlist = imbr->tlist;
	    if(tlist != NULL)
	    {
		gint thumb_num;
		GList *glist = tlist->selection;
		edv_object_struct *obj;
		while(glist != NULL)
		{
		    thumb_num = (gint)glist->data;
		    obj = EDV_OBJECT(TListGetThumbData(tlist, thumb_num));
		    if(obj != NULL)
			paths_list = g_list_append(
			    paths_list, STRDUP(obj->full_path)
			);
		    glist = g_list_next(glist);
		}
	    }
	}

	return(paths_list);
}

/*
 *	Go to directory callback.
 */
void EDVImbrGotoDirectoryCB(gpointer data, const gchar *path)
{
	GtkWidget *toplevel;
	edv_imbr_struct *imbr = EDV_IMBR(data);
	if((imbr == NULL) || (path == NULL))         
	    return;

	toplevel = imbr->toplevel;

	EDVImbrSetBusy(imbr, TRUE);
	GUIBlockInput(toplevel, TRUE);

	EDVImbrSelectPath(imbr, path);
	EDVImbrUpdateMenus(imbr);

	GUIBlockInput(toplevel, FALSE);
	EDVImbrSetBusy(imbr, FALSE);
}

/*
 *	Mount Bar mount callback.
 */
void EDVImbrMountBarMountCB(
	edv_mount_bar_struct *mb, gint dev_num, edv_device_struct *dev,
	gpointer data
)
{
	gboolean original_mount_state;
	gint status;
	GtkWidget *toplevel;
	edv_core_struct *core;
	edv_imbr_struct *imbr = EDV_IMBR(data);
	if((imbr == NULL) || (dev == NULL))
	    return;

	if(imbr->processing)
	    return;

	toplevel = imbr->toplevel;
	core = imbr->core;
	if(core == NULL)
	    return;

	EDVImbrSetBusy(imbr, TRUE);

	/* Record original mount state */
	original_mount_state = EDV_DEVICE_IS_MOUNTED(dev);

	/* Unmount or mount? */
	if(EDV_DEVICE_IS_MOUNTED(dev))
	    status = EDVDeviceUnmount(
		core, dev,
		TRUE, TRUE,
		toplevel
	    );
	else
	    status = EDVDeviceMount(
		core, dev,
		TRUE, TRUE,
		toplevel
	    );

	/* Update all device mount states and stats */
	EDVDevicesListUpdateMountStates(
	    core->device, core->total_devices
	);
	EDVDevicesListUpdateStats(
	    core->device, core->total_devices
	);

	EDVImbrSetBusy(imbr, FALSE);

	/* Mount error? */
	if(status)
	{
	    const gchar *last_error = EDVDeviceUnmountGetError();
	    if(!STRISEMPTY(last_error))
	    {
		EDVPlaySoundError(core);
		EDVMessageError(
		    original_mount_state ?
			"Unmount Failed" : "Mount Failed",
		    last_error,
		    NULL,
		    toplevel
		);
	    }
	}
	else
	{
	    /* Report mount signal to all of endeavour's resources */
	    EDVObjectMountEmit(core, dev_num, dev, EDV_DEVICE_IS_MOUNTED(dev));
	}
}

/*
 *	Mount Bar eject callback.
 */
void EDVImbrMountBarEjectCB(
	edv_mount_bar_struct *mb, gint dev_num, edv_device_struct *dev,
	gpointer data
)
{
	gboolean original_mount_state;
	gint status;
	GtkWidget *toplevel;
	edv_core_struct *core;
	edv_imbr_struct *imbr = EDV_IMBR(data);
	if((imbr == NULL) || (dev == NULL))
	    return;

	if(imbr->processing)
	    return;

	toplevel = imbr->toplevel;
	core = imbr->core;
	if(core == NULL)
	    return;

	EDVImbrSetBusy(imbr, TRUE);

	/* Record original mount state */
	original_mount_state = EDV_DEVICE_IS_MOUNTED(dev);

	/* Need to unmount first? */
	if(EDV_DEVICE_IS_MOUNTED(dev))
	    status = EDVDeviceUnmount(
		core, dev,
		TRUE, TRUE,
		toplevel
	    );

	/* Eject */
	status = EDVDeviceEject(
	    core, dev,
	    TRUE, TRUE,
	    toplevel
	);

	/* Update all device mount states and stats */
	EDVDevicesListUpdateMountStates(
	    core->device, core->total_devices
	);
	EDVDevicesListUpdateStats(
	    core->device, core->total_devices
	);

	EDVImbrSetBusy(imbr, FALSE);

	/* Mount error? */
	if(status)
	{
	    const gchar *last_error = EDVDeviceUnmountGetError();
	    if(!STRISEMPTY(last_error))
	    {
		EDVPlaySoundError(core);
		EDVMessageError(
		    "Eject Failed",
		    last_error,
		    NULL,
		    toplevel
		);
	    }
	}
	else
	{
	    /* Report eject (unmount) signal to all of endeavour's
	     * resources
	     */
	    EDVObjectMountEmit(core, dev_num, dev, EDV_DEVICE_IS_MOUNTED(dev));
	}
}

/*
 *	Mount Bar go to mount path callback.
 */
extern void EDVImbrMountBarGoToCB(
	edv_mount_bar_struct *mb, gint dev_num, edv_device_struct *dev,
	gpointer data
)
{
	gchar *path;
	GtkWidget *toplevel;
	edv_imbr_struct *imbr = EDV_IMBR(data);
	if((imbr == NULL) || (dev == NULL))
	    return;

	if(imbr->processing)
	    return;

	toplevel = imbr->toplevel;

	/* Get copy of mount path */
	path = STRDUP(dev->mount_path);

	EDVImbrSetBusy(imbr, TRUE);
	GUIBlockInput(toplevel, TRUE);

	/* Go to mount path */
	EDVImbrSelectPath(imbr, path);
	EDVImbrUpdateMenus(imbr);

	g_free(path);

	GUIBlockInput(toplevel, FALSE);
	EDVImbrSetBusy(imbr, FALSE);
}


/*
 *	Find Bar get current location callback.
 */
const gchar *EDVImbrFindBarLocationCB(
	edv_findbar_struct *fb, gpointer data
)
{
	return(EDVImbrCurrentLocation(EDV_IMBR(data)));
}

/*
 *	Find Bar start find callback.
 */
void EDVImbrFindBarStartCB(edv_findbar_struct *fb, gpointer data)
{
	tlist_struct *tlist;
	edv_imbr_struct *imbr = EDV_IMBR(data);
	if(imbr == NULL)
	    return;

	tlist = imbr->tlist;
	if(tlist == NULL)
	    return;

	EDVImbrSetBusy(imbr, TRUE);

	TListFreeze(tlist);
	TListUnselectAll(tlist);
	TListThaw(tlist);

	EDVImbrUpdateMenus(imbr);
}

/*
 *	Find Bar end find callback.
 */
void EDVImbrFindBarEndCB(
	edv_findbar_struct *fb, gint total_matches, gpointer data
)
{
	edv_imbr_struct *imbr = EDV_IMBR(data);
	if(imbr == NULL)
	    return;

	EDVImbrSetBusy(imbr, FALSE);
}


/*
 *	Find Bar match callback.
 */
void EDVImbrFindBarMatchCB(
	const gchar *path, const struct stat *lstat_buf,
	const gchar *excerpt, gint line_index,
	gpointer data
)
{
	gint thumb_num;
	tlist_struct *tlist;
	edv_imbr_struct *imbr = EDV_IMBR(data);
	if((path == NULL) || (lstat_buf == NULL) || (imbr == NULL))
	    return;

	tlist = imbr->tlist;
	if(tlist == NULL)
	    return;

	thumb_num = EDVImbrTListFindThumbByPath(imbr, path);
	if((thumb_num >= 0) && (thumb_num < tlist->total_thumbs))
	{
	    TListFreeze(tlist);
	    TListSelectThumb(tlist, thumb_num);
	    TListThaw(tlist);
	}
}


/*
 *	Status message callback.
 */
void EDVImbrStatusMessageCB(const gchar *message, gpointer data)
{
	edv_imbr_struct *imbr = EDV_IMBR(data);
	if(imbr == NULL)
	    return;

	EDVStatusBarMessage(imbr->status_bar, message, FALSE);
}

/*
 *	Status progress callback.
 */
void EDVImbrStatusProgressCB(gfloat progress, gpointer data)
{
	edv_imbr_struct *imbr = EDV_IMBR(data);
	if(imbr == NULL)
	    return;

	EDVStatusBarProgress(imbr->status_bar, progress, FALSE);
}


/*
 *	Write protect changed callback.
 */
void EDVImbrWriteProtectChangedCB(
	edv_imbr_struct *imbr, gboolean state
)
{
	if(imbr == NULL)
	    return;

	if(imbr->processing)
	    return;

	EDVImbrUpdateMenus(imbr);
}

/*
 *	Object added callback.
 */
void EDVImbrObjectAddedNotifyCB(
	edv_imbr_struct *imbr, const gchar *path,
	const struct stat *lstat_buf
)
{
	if(imbr == NULL)
	    return;

	if(imbr->processing)
	    return;

	/* Thumbs List */
	EDVImbrTListObjectAddedNotify(
	    imbr, path, lstat_buf
	);

	/* Mount Bar */
	EDVMountBarUpdateMenus(imbr->mount_bar);

	/* Need to update menus because a new loading process may be
	 * queued
	 */
	EDVImbrUpdateMenus(imbr);
}

/*
 *	Object modified callback.
 */
void EDVImbrObjectModifiedNotifyCB(
	edv_imbr_struct *imbr, const gchar *path,
	const gchar *new_path,
	const struct stat *lstat_buf
)
{
	if(imbr == NULL)
	    return;

	if(imbr->processing)
	    return;

	/* Check current location first, if the old path matches
	 * the current location then the current location needs to be
	 * updated  to reflect the new path before notifying the
	 * Thumbs List
	 */
	if(!STRISEMPTY(path))
	{
	    gchar *cur_loc = STRDUP(EDVImbrCurrentLocation(imbr));

	    if(new_path == NULL)
		new_path = path;

	    /* Check if the old path matches the current location, if
	     * it does then the current location needs to be updated to
	     * the new path
	     */
	    if((cur_loc != NULL) ? !strcmp(cur_loc, path) : FALSE)
	    {
		/* Old path matches current location, change values
		 * reflecting the current location to the value of
		 * new_path
		 */
		EDVImbrSetTitle(imbr, new_path);
		EDVImbrSetLocation(imbr, new_path, FALSE);
		EDVImbrUpdateLocationIcon(imbr, new_path);
	    }

	    g_free(cur_loc);
	}

	/* Thumbs List */
	EDVImbrTListObjectModifiedNotify(
	    imbr, path, new_path, lstat_buf
	);

	/* Mount Bar */
	EDVMountBarUpdateMenus(imbr->mount_bar);

/*	EDVImbrUpdateMenus(imbr); */
}

/*
 *	Object removed callback.
 */
void EDVImbrObjectRemovedNotifyCB(
	edv_imbr_struct *imbr, const gchar *path
)
{
	if(imbr == NULL)
	    return;

	if(imbr->processing)
	    return;

	/* Thumbs List */
	EDVImbrTListObjectRemovedNotify(imbr, path);

	/* Mount Bar */
	EDVMountBarUpdateMenus(imbr->mount_bar);

	/* Check if the path that was removed is the current location,
	 * if it is then set the current location to be the parent of
	 * the path that was removed
	 */
	if(!STRISEMPTY(path))
	{
	    gchar *cur_loc = STRDUP(EDVImbrCurrentLocation(imbr));

	    /* Check if the removed object is the current location */
	    if((cur_loc != NULL) ? !strcmp(cur_loc, path) : FALSE)
	    {
		/* Current location has been removed, change the
		 * current location to the parent
		 */
		gchar *parent_path = g_dirname(path);
		if(parent_path != NULL)
		{
		    EDVImbrSetTitle(imbr, parent_path);
		    EDVImbrSetLocation(imbr, parent_path, FALSE);
		    EDVImbrUpdateLocationIcon(imbr, parent_path);
		    g_free(parent_path);
		}
	    }

	    g_free(cur_loc);
	}

/*	EDVImbrUpdateMenus(imbr); */
}


/*
 *	Device mount/unmount callback.
 */
void EDVImbrMountNotifyCB(
	edv_imbr_struct *imbr,
	gint dev_num, edv_device_struct *dev,
	gboolean is_mounted
)
{
	gchar *cur_loc, *mount_path;
	GtkWidget *toplevel;

	if(imbr == NULL)
	    return;

	if(imbr->processing)
	    return;

	toplevel = imbr->toplevel;

	mount_path = STRDUP(dev->mount_path);

	/* Thumbs List */
	EDVImbrTListMountNotify(imbr, dev, is_mounted);

	/* Check if the current location is within the mount path */ 
	cur_loc = STRDUP(EDVImbrCurrentLocation(imbr));
	if((cur_loc != NULL) ? strpfx(cur_loc, mount_path) : FALSE)
	{
	    /* Switch to the mount location, reget listing, and
	     * restart the loading procedure
	     */
	    EDVImbrSetBusy(imbr, TRUE);
	    GUIBlockInput(toplevel, TRUE);
	    EDVImbrSelectPath(imbr, mount_path);
	    GUIBlockInput(toplevel, FALSE);
	    EDVImbrSetBusy(imbr, FALSE);
	}
  
	EDVImbrUpdateMenus(imbr);

	g_free(mount_path);
	g_free(cur_loc);
}


/*
 *	Recycled object added callback.
 */
void EDVImbrRecycledObjectAddedNotifyCB(
	edv_imbr_struct *imbr, guint index
)
{
	edv_core_struct *core;


	if(imbr == NULL)
	    return;

	core = imbr->core;
	if(core == NULL)
	    return;

	/* There is not much interest when a recycled object has been
	 * added or removed. Only the menus need to be updated if there
	 * is a change in the number of recycled objects
	 */
	if(core->last_recbin_items != imbr->last_recbin_items)
	    EDVImbrUpdateMenus(imbr);
}

/*
 *	Recycled object removed callback.
 */
void EDVImbrRecycledObjectRemovedNotifyCB(
	edv_imbr_struct *imbr, guint index
)
{
	edv_core_struct *core;

	if(imbr == NULL)
	    return;

	core = imbr->core;
	if(core == NULL)
	    return;

	/* There is not much interest when a recycled object has been
	 * added or removed. Only the menus need to be updated if there
	 * is a change in the number of recycled objects
	 */
	if(core->last_recbin_items != imbr->last_recbin_items)
	    EDVImbrUpdateMenus(imbr);
}


/*
 *	Reconfigured callback.
 */
void EDVImbrReconfiguredNotifyCB(edv_imbr_struct *imbr)
{
	gchar *cur_loc;
	GtkRcStyle *standard_rcstyle, *lists_rcstyle;
	GtkWidget *w;
	const cfg_item_struct *cfg_list;
	edv_status_bar_struct *status_bar;
	edv_core_struct *core;
	tlist_struct *tlist;
	imgview_struct *iv;


	if(imbr == NULL)
	    return;

	if(imbr->processing)
	    return;

	core = imbr->core;
	if(core == NULL)
	    return;

	cfg_list = core->cfg_list;
	standard_rcstyle = core->standard_rcstyle;
	lists_rcstyle = core->lists_rcstyle;


	/* Reset last state markers so that resources get explicitly
	 * checked due to reconfigure
	 */
	imbr->last_recbin_items = -1;
	imbr->last_write_protect_state = -1;


	/* Get copy of current location path */
	cur_loc = STRDUP(EDVImbrCurrentLocation(imbr));


	/* Begin updating */

	/* Update Title */
	EDVImbrSetTitle(imbr, cur_loc);

	/* Recreate Accelkey Labels */
	EDVImbrAccelkeysRegenerate(imbr);

	/* Recreate Tool Bar */
	EDVImbrToolbarRegenerate(imbr);

	/* Show/hide Tool Bar */
	w = imbr->tool_bar_handle;
	if(w != NULL)
	{
	    imbr->tool_bar_map_state = EDV_GET_B(EDV_CFG_PARM_IMBR_SHOW_TOOL_BAR);
	    if(imbr->tool_bar_map_state)
		gtk_widget_show(w);
	    else
		gtk_widget_hide(w);
	}

	/* Show/hide Location Bar */
	w = imbr->location_bar_handle;
	if(w != NULL)
	{
	    imbr->location_bar_map_state = EDV_GET_B(EDV_CFG_PARM_IMBR_SHOW_LOCATION_BAR);
	    if(imbr->location_bar_map_state)
		gtk_widget_show(w);
	    else
		gtk_widget_hide(w);
	}

	/* Show/hide Mount Bar */
	w = imbr->mount_bar_handle;
	if(w != NULL)
	{
	    imbr->mount_bar_map_state = EDV_GET_B(EDV_CFG_PARM_IMBR_SHOW_MOUNT_BAR);
	    if(imbr->mount_bar_map_state)
		gtk_widget_show(w);
	    else
		gtk_widget_hide(w);
	}

	/* Show/hide Find Bar */
	w = imbr->find_bar_handle;
	if(w != NULL)
	{
	    imbr->find_bar_map_state = EDV_GET_B(EDV_CFG_PARM_IMBR_SHOW_FIND_BAR);
	    if(imbr->find_bar_map_state)
		gtk_widget_show(w);
	    else
		gtk_widget_hide(w);
	}

	/* Show/hide Status Bar */
	status_bar = imbr->status_bar;
	if(status_bar != NULL)
	{
	    imbr->status_bar_map_state = EDV_GET_B(EDV_CFG_PARM_IMBR_SHOW_STATUS_BAR);
	    if(imbr->status_bar_map_state)
		EDVStatusBarMap(status_bar);
	    else
		EDVStatusBarUnmap(status_bar);
	}

	/* Thumbs List */
	tlist = imbr->tlist;
	if(tlist != NULL)
	{
	    TListFreeze(tlist);

	    TListThumbGeometry(
		tlist,
		EDV_GET_I(EDV_CFG_PARM_IMBR_THUMB_WIDTH),
		EDV_GET_I(EDV_CFG_PARM_IMBR_THUMB_HEIGHT),
		EDV_GET_I(EDV_CFG_PARM_IMBR_THUMB_BORDER)
	    );
	    TListDoubleBuffer(
		tlist, EDV_GET_B(EDV_CFG_PARM_LISTS_DOUBLE_BUFFER)
	    );
	    TListOrientation(
		tlist, EDV_GET_B(EDV_CFG_PARM_IMBR_THUMB_LIST_HORIZONTAL)
	    );
	    TListShowThumbFrames(
		tlist, EDV_GET_B(EDV_CFG_PARM_IMBR_THUMB_SHOW_FRAMES)
	    );
	    TListShowThumbLabels(
		tlist, EDV_GET_B(EDV_CFG_PARM_IMBR_THUMB_SHOW_LABELS)
	    );
	    TListShowTextTips(
		tlist, EDV_GET_B(EDV_CFG_PARM_SHOW_TEXTTIPS)
	    );

	    /* Update geometry of thumb size */
	    TListResize(tlist, -1, -1);

	    /* Realize listing */
	    EDVImbrTListRealizeListing(imbr);

	    TListThaw(tlist);
	}

	/* Image Viewer */
	iv = imbr->imgview;
	if(iv != NULL)
	{
	    const cfg_color_struct *color;

	    /* Image quality */
	    switch(EDV_GET_I(EDV_CFG_PARM_IMAGE_QUALITY))
	    {
	      case 0:
		iv->quality = 0;
		break;
	      case 1:
		iv->quality = 1;
		break;
	      case 2:
		iv->quality = 2;
		break;
	      default:
		iv->quality = 1;
		break;
	    }

	    /* Background color */
	    color = EDV_GET_COLOR(EDV_CFG_PARM_IMBR_COLOR_BG);
	    if(color != NULL)
	    {
		GdkColor gdk_color[5];
		gint i, m = sizeof(gdk_color) / sizeof(GdkColor);
		const cfg_color_struct *sc = color;

		for(i = 0; i < m; i++)
		{
		    GDK_COLOR_SET_COEFF(
			&gdk_color[i],
			sc->r, sc->g, sc->b
		    )
		}
		ImgViewSetViewBG(iv, gdk_color);
	    }

	    /* Redraw image viewer and update menus */
	    ImgViewDraw(iv);
	    ImgViewUpdateMenus(iv);
	}

	/* Recreate New Object Submenu */
	EDVImbrNewObjectMenuRegenerate(imbr);

	/* Update RC styles */
	w = imbr->toplevel;
	if((w != NULL) && (standard_rcstyle != NULL))
	    gtk_widget_modify_style_recursive(w, standard_rcstyle);
	tlist = imbr->tlist;
	w = (tlist != NULL) ? tlist->list_da : NULL;
	if((w != NULL) && (lists_rcstyle != NULL))
	    gtk_widget_modify_style_recursive(w, lists_rcstyle);
	w = ImgViewGetToplevelWidget(imbr->imgview);
	if((w != NULL) && (lists_rcstyle != NULL))
	    gtk_widget_modify_style_recursive(w, lists_rcstyle);
	w = (GtkWidget *)ImgViewGetMenuWidget(imbr->imgview);
	if((w != NULL) && (standard_rcstyle != NULL))
	    gtk_widget_modify_style_recursive(
		w, standard_rcstyle
	    );
	w = imbr->tlist_menu;
	if((w != NULL) && (standard_rcstyle != NULL))
	    gtk_widget_modify_style_recursive(w, standard_rcstyle);


	/* Update menus */
	EDVImbrUpdateMenus(imbr);


	/* Notify the Image Browser's toplevel widget to resize */
	w = imbr->toplevel;
	if(w != NULL)
	    gtk_widget_queue_resize(w);


	/* Delete copy of current location path */
	g_free(cur_loc);
}


/*
 *	MIME Type added callback.
 */
void EDVImbrMimeTypeAddedCB(
	edv_imbr_struct *imbr,
	gint mt_num, edv_mime_type_struct *mt
)
{
	/* Treat a MIME Type added the same as it would be for a MIME
	 * Type modified, forward signal to the MIME Type modified
	 * callback
	 */
	EDVImbrMimeTypeModifiedCB(imbr, mt_num, mt);
}

/*
 *	MIME Type modified callback.
 */
void EDVImbrMimeTypeModifiedCB(
	edv_imbr_struct *imbr,
	gint mt_num, edv_mime_type_struct *mt
)
{
	gchar *cur_loc;
	tlist_struct *tlist;
	edv_core_struct *core;

	if(imbr == NULL)
	    return;

	if(imbr->processing)
	    return;

	tlist = imbr->tlist;
	core = imbr->core;
	if((tlist == NULL) || (core == NULL))
	    return;

	/* Get current location */
	cur_loc = STRDUP(EDVImbrCurrentLocation(imbr));

	/* Realize listing */
	TListFreeze(tlist);
	EDVImbrTListRealizeListing(imbr);
	TListThaw(tlist);

	g_free(cur_loc);
}

/*
 *	MIME Type removed callback.
 */
void EDVImbrMimeTypeRemovedCB(
	edv_imbr_struct *imbr, gint mt_num
)
{
	gchar *cur_loc;
	tlist_struct *tlist;
	edv_core_struct *core;

	if(imbr == NULL)
	    return;

	if(imbr->processing)
	    return;

	tlist = imbr->tlist;
	core = imbr->core;
	if((tlist == NULL) || (core == NULL))
	    return;

	/* Get current location */
	cur_loc = STRDUP(EDVImbrCurrentLocation(imbr));

	/* Realize listing */
	TListFreeze(tlist);
	EDVImbrTListRealizeListing(imbr);
	TListThaw(tlist);

	g_free(cur_loc);
}


/*
 *	Device added callback.
 */
void EDVImbrDeviceAddedCB(
	edv_imbr_struct *imbr,
	gint dev_num, edv_device_struct *dev
)
{
	/* Treat a device added the same as it would be for a device
	 * modified, forward signal to the device modified callback
	 */
	EDVImbrDeviceModifiedCB(imbr, dev_num, dev);
}

/*
 *	Device modified callback.
 */
void EDVImbrDeviceModifiedCB(
	edv_imbr_struct *imbr,
	gint dev_num, edv_device_struct *dev
)
{
	gchar *cur_loc;
	tlist_struct *tlist;
	edv_core_struct *core;

	if(imbr == NULL)
	    return;

	if(imbr->processing)
	    return;

	tlist = imbr->tlist;
	core = imbr->core;
	if((tlist == NULL) || (core == NULL))
	    return;

	/* Get current location */
	cur_loc = STRDUP(EDVImbrCurrentLocation(imbr));

	/* Realize listing */
	TListFreeze(tlist);
	EDVImbrTListRealizeListing(imbr);
	TListThaw(tlist);

	/* Update menus */
	EDVImbrUpdateMenus(imbr);

	g_free(cur_loc);
}

/*
 *	Device removed callback.
 */
void EDVImbrDeviceRemovedCB(
	edv_imbr_struct *imbr, gint dev_num
)
{
	gchar *cur_loc;
	tlist_struct *tlist;
	edv_mount_bar_struct *mb;
	edv_core_struct *core;

	if(imbr == NULL)
	    return;

	if(imbr->processing)
	    return;

	tlist = imbr->tlist;
	core = imbr->core;
	if((tlist == NULL) || (core == NULL))
	    return;

	/* Get current location */
	cur_loc = STRDUP(EDVImbrCurrentLocation(imbr));

	/* Check if the Mount Bar is currently referencing this device,
	 * if it is then its selected device needs to be set to -1
	 */
	mb = imbr->mount_bar;
	if(mb != NULL)
	{
	    if(mb->selected_dev_num == dev_num)
		mb->selected_dev_num = -1;

	    /* Mount Bar will be updated further below when menus are
	     * updated
	     */
	}

	/* Realize listing */
	TListFreeze(tlist);
	EDVImbrTListRealizeListing(imbr);
	TListThaw(tlist);

	/* Update menus */
	EDVImbrUpdateMenus(imbr);

	g_free(cur_loc);
}
