/*
 * selection.c : Code that the X and Curses browsers use, but that
 *	others might be able to do without, or do better.
 *
 * Note that we need to maintain the selections for those levels
 * of the browser that aren't displayed right now. Again, you may
 * get better mileage from your window system.
 *
 * George Ferguson, ferguson@cs.rochester.edu, 23 Apr 1993.
 * 13 May 1993: Store selections in order they're made.
 */
#include <stdio.h>
#include "xtypes.h"
#include "sysdefs.h"
#include "db.h"
#include "display.h"
#include "browser.h"
#include "selection.h"
#include "debug.h"

/*
 * Functions defined here:
 */
void resetSelections();
void redrawSelectionsForPane(),resetSelectionsForPane();
void addSelection(),addSelectionInPane(),removeSelection();
SelectedItem *getSelection();
Boolean isSelected(),isSelectedInPane();
Boolean hasSelection(),hasSelectionInPane();
void forEachSelectedItemAtDepth(),forEachSelectedItem();

/*
 * Data defined here:
 */
static SelectedItem *selectedItems[MAX_DEPTH];
static int deepest;

/*	-	-	-	-	-	-	-	-	*/

void
resetSelections(depth)
int depth;
{
    SelectedItem *item,*nextItem;
    int i;

    DEBUG1("clearing selections for depth >= %d\n",depth);
    for (i=depth; i < MAX_DEPTH; i++) {
	for (item=selectedItems[i]; item != NULL; item = nextItem) {
	    nextItem = item->next;
	    XtFree((char *)item);
	}
	selectedItems[i] = NULL;
    }
    deepest = depth-1;
    DEBUG1("deepest now = %d\n",deepest);
}

void
redrawSelectionsForPane(pane)
int pane;
{
    SelectedItem *item;

    DEBUG1("redrawing selections for pane %d\n",pane);
    for (item=selectedItems[paneDepth(pane)]; item != NULL;
	 item=item->next) {
	highlightBrowserItem(pane,item->list_index);
    }
}

void
resetSelectionsForPane(pane)
int pane;
{
    DEBUG1("clearing selections for pane %d\n",pane);
    resetSelections(paneDepth(pane));
}

/*	-	-	-	-	-	-	-	-	*/

void
addSelection(depth,dbp,list_index)
int depth;
DbEntry *dbp;
int list_index;
{
    SelectedItem *last;

    DEBUG3("adding selection \"%s\"(0x%x) at depth %d\n",dbp->name,dbp,depth);
    if (selectedItems[depth] == NULL) {
	selectedItems[depth] = XtNew(SelectedItem);
	selectedItems[depth]->prev = NULL;
	selectedItems[depth]->next = NULL;
	last = selectedItems[depth];
    } else {
	for (last=selectedItems[depth]; last->next != NULL; last=last->next)
	    /*EMPTY*/;
	last->next = XtNew(SelectedItem);
	last->next->prev = last;
	last->next->next = NULL;
	last = last->next;
    }
    last->entry = dbp;
    last->list_index = list_index;
    dbp->selected = depth;
    if (depth > deepest) {
	deepest = depth;
	DEBUG1("deepest now = %d\n",deepest);
    }
}

void
addSelectionInPane(pane,dbp,list_index)
int pane;
DbEntry *dbp;
int list_index;
{
    DEBUG3("adding selection \"%s\"(0x%x) in pane %d\n",dbp->name,dbp,pane);
    addSelection(paneDepth(pane),dbp,list_index);
}

void
removeSelection(depth,dbp,list_index)
int depth;
DbEntry *dbp;
int list_index;
{
    SelectedItem *item;

    DEBUG3("removing selection \"%s\"(0x%x) at depth %d\n",
	   (dbp?dbp->name:"<NIL>"),dbp,depth);
    for (item=selectedItems[depth]; item != NULL; item = item->next)
	if ((dbp == NULL || item->entry == dbp) &&
	    (list_index == -1  || item->list_index == list_index))
	    break;
    if (item == NULL) {
	fprintf(stderr,"removeSelection: Can't find entry 0x%x (list_index=%d) at depth %d\n",dbp,list_index,depth);
	return;
    }
    if (item == selectedItems[depth]) {
	selectedItems[depth] = item->next;
	if (item->next != NULL)
	    item->next->prev = NULL;
    } else {
	item->prev->next = item->next;
	if (item->next != NULL)
	    item->next->prev = item->prev;
    }
    XtFree((char *)item);
    if (selectedItems[depth] == NULL) {
	deepest = depth-1;
	DEBUG1("deepest now = %d\n",deepest);
    }
}

SelectedItem *
getSelection(depth)
int depth;
{
    return(selectedItems[depth]);
}

/*	-	-	-	-	-	-	-	-	*/

Boolean
isSelected(depth,list_index)
int depth,list_index;
{
    SelectedItem *item;

    for (item=selectedItems[depth];
	 item != NULL && item->list_index != list_index; item=item->next)
	/*EMPTY*/;
    return(item != NULL);
}

Boolean
isSelectedInPane(pane,list_index)
int pane,list_index;
{
    return(isSelected(paneDepth(pane),list_index));
}

Boolean
hasSelection(depth)
int depth;
{
    return(selectedItems[depth] != NULL);
}

Boolean
hasSelectionInPane(pane)
int pane;
{
    return(hasSelection(paneDepth(pane)));
}

/*	-	-	-	-	-	-	-	-	*/
/*
 * Calls "func" for each selected item at depth "depth", passing DbEntry
 * and list_index.
 */
void
forEachSelectedItemAtDepth(depth,func)
int depth;
void (*func)();
{
    SelectedItem *item;

    for (item=selectedItems[depth]; item != NULL; item=item->next)
	(*func)(item->entry,item->list_index);
}

/*
 * Like above, but calls "func" for each item selected at the deepest
 * level.
 */
void
forEachSelectedItem(func)
void (*func)();
{
    SelectedItem *item;

    if (deepest == -1) {
	DEBUG0("forEachSelectedItem: nothing selected\n");
       return;
    }
    for (item=selectedItems[deepest]; item != NULL; item=item->next)
	(*func)(item->entry,item->list_index);
}
