/***************************************************************************/
/***************************************************************************/
/*                                                                         */
/*   (c) 1995-1999.  The Regents of the University of California.  All     */
/*   rights reserved.                                                      */
/*                                                                         */
/*   This work was produced at the University of California, Lawrence      */
/*   Livermore National Laboratory (UC LLNL) under contract no.            */
/*   W-7405-ENG-48 (Contract 48) between the U.S. Department of Energy     */
/*   (DOE) and The Regents of the University of California (University)    */
/*   for the operation of UC LLNL.  Copyright is reserved to the           */
/*   University for purposes of controlled dissemination,                  */
/*   commercialization through formal licensing, or other disposition      */
/*   under terms of Contract 48; DOE policies, regulations and orders;     */
/*   and U.S. statutes.  The rights of the Federal Government are          */
/*   reserved under Contract 48 subject to the restrictions agreed upon    */
/*   by the DOE and University.                                            */
/*                                                                         */
/*                                                                         */
/*                              DISCLAIMER                                 */
/*                                                                         */
/*   This software was prepared as an account of work sponsored by an      */
/*   agency of the United States Government.  Neither the United States    */
/*   Government nor the University of California nor any of their          */
/*   employees, makes any warranty, express or implied, or assumes any     */
/*   liability or responsibility for the accuracy, completeness, or        */
/*   usefulness of any information, apparatus, product, or process         */
/*   disclosed, or represents that its specific commercial products,       */
/*   process, or service by trade name, trademark, manufacturer, or        */
/*   otherwise, does not necessarily constitute or imply its               */
/*   endorsement, recommendation, or favoring by the United States         */
/*   Government or the University of California. The views and opinions    */
/*   of the authors expressed herein do not necessarily state or reflect   */
/*   those of the United States Government or the University of            */
/*   California, and shall not be used for advertising or product          */
/*   endorsement purposes.                                                 */
/*                                                                         */
/*   Permission to use, copy, modify and distribute this software and its  */
/*   documentation for any non-commercial purpose, without fee, is         */
/*   hereby granted, provided that the above copyright notice and this     */
/*   permission notice appear in all copies of the software and            */
/*   supporting documentation, and that all UC LLNL identification in      */
/*   the user interface remain unchanged.  The title to copyright LLNL     */
/*   XDIR shall at all times remain with The Regents of the University     */
/*   of California and users agree to preserve same. Users seeking the     */
/*   right to make derivative works with LLNL XDIR for commercial          */
/*   purposes may obtain a license from the Lawrence Livermore National    */
/*   Laboratory's Technology Transfer Office, P.O. Box 808, L-795,         */
/*   Livermore, CA 94550.                                                  */
/*                                                                         */
/***************************************************************************/
/***************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <Xm/Form.h>
#include <Xm/Separator.h>
#include <Xm/SelectioB.h>
#include <Xm/RowColumn.h>
#include <Xm/Label.h>
#include <Xm/List.h>
#include <Xm/TextF.h>
#include <Xm/Frame.h>
#include <Xm/PushB.h>
#include "list.h"
#include "str.h"

#define MAXLPLINE      200
#define L_IDENTIFIER  "LAUNCHER:"
#define MAXQUESTION    1000

struct sl_struct *launcher_mappings;

extern Display *display;
extern int need_to_save_prefs;
extern int screen;

static struct {
	Widget w_shell;
	Widget w_form;
	Widget w_controlArea;
	Widget w_listLabel;
	Widget w_list;
	Widget w_commandNameLabel;
	Widget w_commandName;
	Widget w_commandLineLabel;
	Widget w_commandLine;
	Widget w_editFrame;
	Widget w_editForm;
	Widget w_addButton;
	Widget w_replaceButton;
	Widget w_deleteButton;
	Widget w_messageFrame;
	Widget w_messageRowcolumn;
	Widget w_message1;
	Widget w_message2;
	Widget w_separator;
	Widget w_actionArea;
	Widget w_okButton;
	Widget w_applyButton;
	Widget w_cancelButton;
	Widget w_helpButton;
} lprefs;

static char *lprefs_help[] = {
	"This dialog enables you to construct commands that can be",
	"launched from the LAUNCH submenu of the OPS menu.  The",
	"idea is to be able to fork and exec child processes with",
	"execute lines based on information about selected",
	"entries.\n",
	"\n",
	"Single-click on an entry in the \"Command Names\" list to",
	"place that command name and its associated command line",
	"into the \"Command Name\" and \"Command Line\" text fields,",
	"respectively.\n",
	"\n",
	"The following placeholders can be specified in a command",
	"line: \"%u\" for the user name, \"%h\" for the host name,",
	"\"%s\" for the full path of a selected entry, and \"%l\" for the",
	"blank-delimited full paths of all selected entries.  To add a",
	"new command to the list, fill in the \"Command Name\" and",
	"\"Command Line\" text fields and press the ADD button.  To",
	"replace the command line associated with a",
	"command name, (1) single-click on the list item to select",
	"it, (2) edit the command line, and (3) press the REPLACE",
	"button.\n",
	"\n",
	"To delete command from the list, single-click on the list",
	"item to select it and then press the DELETE button.\n",
	"\n",
	"Click the APPLY button to cause the new changes to take",
	"effect.  The OK button has the same effect as APPLY, but",
	"the dialog is also closed.  The CANCEL button undoes the",
	"changes made since OK or APPLY were last pressed.\n",
	"\n",
	"The launcher preferences can be preserved across LLNL",
	"XDIR sessions in a text file named \".xdirrc\" in the",
	"user's home directory (select SAVE PREFERENCES in a",
	"directory window's PREFS menu).\n",
	"\n",
	"The names of the commands will be placed in the LAUNCH",
	"submenu of the OPS menu.\n",
	"\n",
	"When a command is launched, placeholders are replaced by",
	"their values as follows: \"%u\" is replaced by the user",
	"name, \"%h\" is replaced by the host name, \"%s\" is replaced",
	"by the full path of a selected entry, and \"%l\" is replaced by",
	"the blank-delimited full paths of all selected entries.  If",
	"\"%s\" is used, then the command is launched once for each",
	"selected entry, with \"%s\" being replaced by the full path",
	"of each entry name.",
	NULL
};

static char *default_mappings[] = {
	"Example #1: script1 -h %h -u %u -e %l",
	"Example #2: script2 %u %h %s"
};
static int ndefault_mappings = XtNumber(default_mappings);

static struct sl_struct *temp_launcher_mappings;
static int initialized_lprefs = False;
static int lprefs_visible = False;

extern Widget w_toplev;

void cb_lprefs_cancel();
void cb_lprefs_help();
void cb_lprefs_add();
void cb_lprefs_delete();
void cb_lprefs_ok();
void cb_lprefs_apply();
void cb_lprefs_single_selection();
void cb_command_modified();
void create_lprefs_window();
void apply_launcher_prefs();
void display_lprefs();
void did_user_forget_lprefs();
struct sl_struct *create_array_list();
int compare_command_names();
struct sl_struct *create_null_array_list();
struct sl_struct *duplicate_array_list();
char *get_launch_command_name();
char *get_launch_command_line();
char *strip_outer_whitespace();


/*
 * cb_launcher_preferences - Callback to pop up Launcher Preferences dialog.
 */
void
cb_launcher_preferences(widget, client_data, call_data)
Widget widget;
XtPointer client_data;
XtPointer call_data;
{
	int nitems;

    /* Clear error flag */
    raise_okflag();

	/* Create the Launcher Preferences Dialog */
    create_lprefs_window();

	/* Make temporary copy of command name/command line mappings */
	temp_launcher_mappings = duplicate_array_list(launcher_mappings);

	/* Update the Launcher Prefs dialog */
	display_lprefs(launcher_mappings);
	XmTextFieldSetString(lprefs.w_commandName, "");
	XmTextFieldSetString(lprefs.w_commandLine, "");

	/* Select first item (if there is a first item) */
	XtVaGetValues(lprefs.w_list, XmNitemCount, &nitems, NULL);
	if (nitems) {
		XmListSelectPos(lprefs.w_list, 1, True);
		XtSetSensitive(lprefs.w_deleteButton, True);
	} else
		XtSetSensitive(lprefs.w_deleteButton, False);

	/* Pop up launcher preferences window */
	XtPopup(lprefs.w_shell, XtGrabNone);
	add_dialog_to_list(lprefs.w_shell);
	traverse_to_widget(lprefs.w_commandName);
	XMapRaised(display, XtWindow(lprefs.w_shell));
	lprefs_visible = True;
}


/*
 * create_lprefs_window - Create window for specifying which launcher 
 *                        should be used with each command name.
 */
void
create_lprefs_window()
{
	Arg args[2];
	int i;

    /* Create dialog only once */
    if (initialized_lprefs)
        return;
    initialized_lprefs = True;

	/* Create toplevel shell for directory window */
	lprefs.w_shell = XtVaCreatePopupShell("launchPrefs",
		topLevelShellWidgetClass, w_toplev, NULL);

	/* Attach custom icon */
	attach_wm_icon(lprefs.w_shell);

	/* Create form */
	lprefs.w_form = XtVaCreateWidget("form", xmFormWidgetClass, lprefs.w_shell,
		NULL);

	/* Add callback for the WM_DELETE_WINDOW protocol */
	add_wm_delete_window_cb(lprefs.w_shell, cb_lprefs_cancel, NULL, True);

	/* Create form for action area */
    lprefs.w_actionArea = XtVaCreateWidget(
        "actionArea",
        xmFormWidgetClass,
        lprefs.w_form,
		XmNmarginHeight,		10,
		XmNbottomAttachment,	XmATTACH_FORM,
		XmNleftAttachment,		XmATTACH_FORM,
		XmNrightAttachment,		XmATTACH_FORM,
        NULL
    );

	/* Create OK pushbutton */
    lprefs.w_okButton = XtVaCreateManagedWidget(
        "okButton",
        xmPushButtonWidgetClass,
        lprefs.w_actionArea,
		XmNdefaultButtonShadowThickness,	1,
		XmNmarginHeight,					4,
		XmNtopAttachment,					XmATTACH_FORM,
        XmNbottomAttachment,    			XmATTACH_FORM,
        XmNleftAttachment,      			XmATTACH_POSITION,
        XmNleftPosition,        			3,
        XmNrightAttachment,     			XmATTACH_POSITION,
        XmNrightPosition,       			25,
        NULL
    );
    XtAddCallback(lprefs.w_okButton, XmNactivateCallback, cb_lprefs_ok,
		(XtPointer)NULL);

	/* Create Apply pushbutton */
    lprefs.w_applyButton = XtVaCreateManagedWidget(
        "applyButton",
        xmPushButtonWidgetClass,
        lprefs.w_actionArea,
		XmNdefaultButtonShadowThickness,	1,
		XmNmarginHeight,					4,
		XmNtopAttachment,					XmATTACH_FORM,
        XmNbottomAttachment,    			XmATTACH_FORM,
        XmNleftAttachment,      			XmATTACH_POSITION,
        XmNleftPosition,        			27,
        XmNrightAttachment,     			XmATTACH_POSITION,
        XmNrightPosition,       			49,
        NULL
    );
    XtAddCallback(lprefs.w_applyButton, XmNactivateCallback, cb_lprefs_apply,
		(XtPointer)NULL);

	/* Create Cancel pushbutton */
    lprefs.w_cancelButton = XtVaCreateManagedWidget(
        "cancelButton",
        xmPushButtonWidgetClass,
        lprefs.w_actionArea,
		XmNdefaultButtonShadowThickness,	1,
		XmNmarginHeight,					4,
		XmNtopAttachment,					XmATTACH_FORM,
        XmNbottomAttachment,    			XmATTACH_FORM,
        XmNleftAttachment,      			XmATTACH_POSITION,
        XmNleftPosition,        			51,
        XmNrightAttachment,     			XmATTACH_POSITION,
        XmNrightPosition,       			73,
        NULL
    );
    XtAddCallback(lprefs.w_cancelButton, XmNactivateCallback, cb_lprefs_cancel,
		(XtPointer)NULL);
	XtVaSetValues(lprefs.w_form, XmNdefaultButton, lprefs.w_cancelButton,
		NULL);

	/* Create Help pushbutton */
    lprefs.w_helpButton = XtVaCreateManagedWidget(
        "helpButton",
        xmPushButtonWidgetClass,
        lprefs.w_actionArea,
		XmNdefaultButtonShadowThickness,	1,
		XmNmarginHeight,					4,
		XmNtopAttachment,					XmATTACH_FORM,
        XmNbottomAttachment,    			XmATTACH_FORM,
        XmNleftAttachment,      			XmATTACH_POSITION,
        XmNleftPosition,        			75,
        XmNrightAttachment,     			XmATTACH_POSITION,
        XmNrightPosition,       			97,
        NULL
    );
    XtAddCallback(lprefs.w_helpButton, XmNactivateCallback, cb_lprefs_help,
		(XtPointer)NULL);

	XtManageChild(lprefs.w_actionArea);

    /* Create separator */
    lprefs.w_separator = XtVaCreateManagedWidget(
        "separator",
        xmSeparatorWidgetClass,
        lprefs.w_form,
        XmNbottomAttachment,    XmATTACH_WIDGET,
        XmNbottomWidget,        lprefs.w_actionArea,
        XmNleftAttachment,      XmATTACH_FORM,
        XmNrightAttachment,     XmATTACH_FORM,
        NULL
    );

    /* Create form for control area */
    lprefs.w_controlArea = XtVaCreateWidget(
        "controlArea",
        xmFormWidgetClass,
        lprefs.w_form,
		XmNtopAttachment,		XmATTACH_FORM,
		XmNleftAttachment,		XmATTACH_FORM,
		XmNrightAttachment,		XmATTACH_FORM,
		XmNbottomAttachment,	XmATTACH_WIDGET,
		XmNbottomWidget,		lprefs.w_separator,
		XmNmarginHeight,		10,
		XmNmarginWidth,			10,
        NULL
    );

    /* Create label for command name */
    lprefs.w_commandNameLabel = XtVaCreateManagedWidget(
        "commandNameLabel",
        xmLabelWidgetClass,
        lprefs.w_controlArea,
        XmNalignment,       	XmALIGNMENT_BEGINNING,
        XmNtopAttachment,   	XmATTACH_FORM,
        XmNleftAttachment,  	XmATTACH_POSITION,
		XmNleftPosition,		45,
		XmNrightAttachment, 	XmATTACH_FORM,
        NULL
    );

    /* Create textfield for command name */
    lprefs.w_commandName = XtVaCreateManagedWidget(
        "commandName",
        xmTextFieldWidgetClass,
        lprefs.w_controlArea,
        XmNtopAttachment,   XmATTACH_WIDGET,
        XmNtopWidget,       lprefs.w_commandNameLabel,
        XmNleftAttachment,  XmATTACH_OPPOSITE_WIDGET,
		XmNleftWidget,		lprefs.w_commandNameLabel,
        XmNrightAttachment, XmATTACH_FORM,
        NULL
    );
    XtAddCallback(lprefs.w_commandName, XmNvalueChangedCallback,
        cb_command_modified, (XtPointer)NULL);

    /* Create label for command line */
    lprefs.w_commandLineLabel = XtVaCreateManagedWidget(
        "commandLineLabel",
        xmLabelWidgetClass,
        lprefs.w_controlArea,
        XmNalignment,       	XmALIGNMENT_BEGINNING,
        XmNtopAttachment,   	XmATTACH_WIDGET,
		XmNtopWidget,			lprefs.w_commandName,
		XmNtopOffset,			10,
        XmNleftAttachment,  	XmATTACH_OPPOSITE_WIDGET,
		XmNleftWidget,			lprefs.w_commandName,
		XmNrightAttachment, 	XmATTACH_FORM,
        NULL
    );

    /* Create textfield for command line */
    lprefs.w_commandLine = XtVaCreateManagedWidget(
        "commandLine",
        xmTextFieldWidgetClass,
        lprefs.w_controlArea,
        XmNtopAttachment,   	XmATTACH_WIDGET,
        XmNtopWidget,       	lprefs.w_commandLineLabel,
        XmNleftAttachment,  	XmATTACH_OPPOSITE_WIDGET,
		XmNleftWidget,			lprefs.w_commandLineLabel,
        XmNrightAttachment, 	XmATTACH_FORM,
        NULL
    );
    XtAddCallback(lprefs.w_commandLine, XmNvalueChangedCallback,
        cb_command_modified, (XtPointer)NULL);

    /* Create frame for edit buttons */
    lprefs.w_editFrame = XtVaCreateManagedWidget(
        "editFrame",
        xmFrameWidgetClass,
        lprefs.w_controlArea,
		XmNtopAttachment,		XmATTACH_WIDGET,
		XmNtopWidget,			lprefs.w_commandLine,
		XmNtopOffset,			10,
        XmNleftAttachment,      XmATTACH_OPPOSITE_WIDGET,
		XmNleftWidget,			lprefs.w_commandLine,
        XmNrightAttachment,     XmATTACH_FORM,
        NULL
    );

    /* Create form for edit buttons */
    lprefs.w_editForm = XtVaCreateManagedWidget(
        "editForm",
        xmFormWidgetClass,
        lprefs.w_editFrame,
		XmNmarginHeight,		2,
		XmNmarginWidth,			2,
        NULL
    );

    /* Create "Add" pushbutton */
    lprefs.w_addButton = XtVaCreateManagedWidget(
        "addButton",
        xmPushButtonWidgetClass,
        lprefs.w_editForm,
		XmNtopAttachment,		XmATTACH_FORM,
        XmNleftAttachment,      XmATTACH_FORM,
        XmNrightAttachment,     XmATTACH_POSITION,
        XmNrightPosition,       50,
        NULL
    );
    XtAddCallback(lprefs.w_addButton, XmNactivateCallback, cb_lprefs_add,
		(XtPointer)NULL);

    /* Create "Replace" pushbutton */
    lprefs.w_replaceButton = XtVaCreateManagedWidget(
        "replaceButton",
        xmPushButtonWidgetClass,
        lprefs.w_editForm,
		XmNtopAttachment,		XmATTACH_FORM,
        XmNleftAttachment,      XmATTACH_POSITION,
		XmNleftPosition,		50,
        XmNrightAttachment,     XmATTACH_FORM,
        NULL
    );
    XtAddCallback(lprefs.w_replaceButton, XmNactivateCallback, cb_lprefs_add,
		(XtPointer)NULL);

    /* Create "Delete" pushbutton */
    lprefs.w_deleteButton = XtVaCreateManagedWidget(
        "deleteButton",
        xmPushButtonWidgetClass,
        lprefs.w_editForm,
		XmNtopAttachment,		XmATTACH_WIDGET,
		XmNtopWidget,			lprefs.w_addButton,
        XmNbottomAttachment,    XmATTACH_FORM,
        XmNleftAttachment,      XmATTACH_POSITION,
		XmNleftPosition,		25,
        XmNrightAttachment,     XmATTACH_POSITION,
        XmNrightPosition,       75,
        NULL
    );
    XtAddCallback(lprefs.w_deleteButton, XmNactivateCallback, cb_lprefs_delete,
		(XtPointer)NULL);

    /* Create label for scrolled list of command names */
    lprefs.w_listLabel = XtVaCreateManagedWidget(
        "listLabel",
        xmLabelWidgetClass,
        lprefs.w_controlArea,
        XmNalignment,       	XmALIGNMENT_BEGINNING,
        XmNtopAttachment,   	XmATTACH_FORM,
        XmNleftAttachment,  	XmATTACH_FORM,
        NULL
    );

    /* Create scrolled list of quoted expressions */
    i = 0;
    XtSetArg(args[i], XmNselectionPolicy, XmSINGLE_SELECT); i++;
    XtSetArg(args[i], XmNlistSizePolicy, XmCONSTANT); i++;
    lprefs.w_list = XmCreateScrolledList(lprefs.w_controlArea, "list", args, i);
    XtVaSetValues(
        XtParent(lprefs.w_list),
        XmNtopAttachment,   	XmATTACH_WIDGET,
        XmNtopWidget,       	lprefs.w_listLabel,
        XmNleftAttachment,  	XmATTACH_OPPOSITE_WIDGET,
		XmNleftWidget,			lprefs.w_listLabel,
        XmNrightAttachment, 	XmATTACH_POSITION,
		XmNrightPosition,		40,
		XmNbottomAttachment,	XmATTACH_OPPOSITE_WIDGET,
		XmNbottomWidget,		lprefs.w_editFrame,
        NULL
    );
    XtManageChild(lprefs.w_list);

    /* Add single-selection callback */
    XtAddCallback(lprefs.w_list, XmNsingleSelectionCallback,
        cb_lprefs_single_selection, (XtPointer)NULL);

    /* Create frame for message */
    lprefs.w_messageFrame = XtVaCreateManagedWidget(
        "messageFrame",
        xmFrameWidgetClass,
        lprefs.w_controlArea,
		XmNtopAttachment,		XmATTACH_WIDGET,
		XmNtopWidget,			lprefs.w_editFrame,
		XmNtopOffset,			15,
        XmNbottomAttachment,    XmATTACH_FORM,
        XmNleftAttachment,      XmATTACH_FORM,
        XmNrightAttachment,     XmATTACH_FORM,
        NULL
    );

    /* Create rowcolumn to hold warning message */
    lprefs.w_messageRowcolumn = XtVaCreateManagedWidget(
        "messageRowcolumn",
        xmRowColumnWidgetClass,
        lprefs.w_messageFrame,
        NULL
    );

    /* Create label for first line of message */
    lprefs.w_message1 = XtVaCreateManagedWidget(
        "message1",
        xmLabelWidgetClass,
        lprefs.w_messageRowcolumn,
        NULL
    );

    /* Create label for second line of message */
    lprefs.w_message2 = XtVaCreateManagedWidget(
        "message2",
        xmLabelWidgetClass,
        lprefs.w_messageRowcolumn,
        NULL
    );

	XtManageChild(lprefs.w_controlArea);
	XtManageChild(lprefs.w_form);
}


/*
 * cb_lprefs_ok - Callback that applies current launcher preferences and
 *                then closes Launch Preferences dialog.
 */
void
cb_lprefs_ok(widget, client_data, call_data)
Widget widget;
XtPointer client_data;
XtPointer call_data;
{
	did_user_forget_lprefs();
	apply_launcher_prefs();
	release_array_list(temp_launcher_mappings);
	XtUnmapWidget(lprefs.w_shell);
	lprefs_visible = False;
	update_all_launch_menus();
}


/*
 * cb_lprefs_apply - Callback that applies current launcher preferences.
 */
void
cb_lprefs_apply(widget, client_data, call_data)
Widget widget;
XtPointer client_data;
XtPointer call_data;
{
	did_user_forget_lprefs();
	apply_launcher_prefs();
	update_all_launch_menus();
}


/*
 * cb_lprefs_cancel - Callback that restores old launcher preferences 
 *                    and then the closes Launcher Preferences dialog.
 */
void
cb_lprefs_cancel(widget, client_data, call_data)
Widget widget;
XtPointer client_data;
XtPointer call_data;
{
	release_array_list(temp_launcher_mappings);
	XtUnmapWidget(lprefs.w_shell);
	lprefs_visible = False;
}


/*
 * cb_lprefs_help - Callback that displays help info for the Launcher
 *                       Preferences dialog.
 */
void
cb_lprefs_help(widget, client_data, call_data)
Widget widget;
XtPointer client_data;
XtPointer call_data;
{
	help_dialog(widget, False, "Launcher Preferences", lprefs_help);
}


/*
 * add_to_launch_list - Add command name/command line pair to launcher
 *                      list.  If an entry in the list has the same
 *                      command name, it is first deleted.
 */
void
add_to_launch_list()
{
	char *command_line;
	char *command_name;
	char *mapping;
	int i;
	XmString string;

	/* Get command name */
	command_name = get_launch_command_name();
	if (strlen(command_name) == 0)
		fatal_error("Trouble in add_to_launch_list()");

	/* Get command line */
	command_line = get_launch_command_line();

	/* Form command name/command line pair */
	mapping = XtMalloc(strlen(command_name)+strlen(command_line)+3);
    strcpy(mapping, command_name);
	strcat(mapping, ": ");
	strcat(mapping, command_line);

	/* Add to temporary mappings (but first check for existing command name) */
    for (i=0; i<temp_launcher_mappings->nentries; i++)
        if (!compare_command_names(mapping,
				temp_launcher_mappings->entries[i]))  {
            delete_from_array_list(&temp_launcher_mappings, i);
            break;
        }
    add_to_array_list(&temp_launcher_mappings, mapping);
    XtFree(mapping);

	/* Sort and redisplay entries */
	quicksort(temp_launcher_mappings->entries, temp_launcher_mappings->nentries,
		compare_command_names);
	display_lprefs(temp_launcher_mappings);

	/* Select newly added item */
	string = XmStringCreateSimple(command_name);
	XmListSelectItem(lprefs.w_list, string, True);
	XmStringFree(string);
	XtSetSensitive(lprefs.w_deleteButton, True);

    XtFree(command_name);
    XtFree(command_line);
}


/*
 * cb_lprefs_add - Callback that adds new command name/command pair
 *                 to list.  If an entry in the list has the same 
 *                 command name, it is deleted.
 */
void
cb_lprefs_add(widget, client_data, call_data)
Widget widget;
XtPointer client_data;
XtPointer call_data;
{
	add_to_launch_list();
}


/*
 * cb_lprefs_delete - Callback that deletes new command name/command
 *                    pair from list.
 */
void
cb_lprefs_delete(widget, client_data, call_data)
Widget widget;
XtPointer client_data;
XtPointer call_data;
{
	int *position_list;
	int position_count;
	int retval;
	int nitems;

	/* Get position of selected item */
	retval = XmListGetSelectedPos(lprefs.w_list, &position_list,
		&position_count);

	/* Sanity check */
	if (!retval || (position_count != 1))
		fatal_error("Trouble in cb_lprefs_delete()");

	/* Delete corresponding item from temporary command name/line list */
	delete_from_array_list(&temp_launcher_mappings, position_list[0]-1);

	/* Update the command names list */
	display_lprefs(temp_launcher_mappings);

	/* Select first item (if there is a first item) */
	XtVaGetValues(lprefs.w_list, XmNitemCount, &nitems, NULL);
	if (nitems)
		XmListSelectPos(lprefs.w_list, 1, True);
	else {
		XmTextFieldSetString(lprefs.w_commandName, "");
		XmTextFieldSetString(lprefs.w_commandLine, "");
		XtSetSensitive(lprefs.w_deleteButton, False);
	}
}

/*
 * apply_launcher_prefs - Set current launcher preferences to displayed
 *                        values.
 */
void
apply_launcher_prefs()
{
	need_to_save_prefs = True;

	/* Replace current mappings with temporary mappings */
	release_array_list(launcher_mappings);
	launcher_mappings = duplicate_array_list(temp_launcher_mappings);
}


/*
 * read_lprefs_from_file - Initializes current launcher preferences with
 *                         values from .xdirrc (hich is pointed to by "fp").
 *                         If "fp" is NULL, then use default values.
 */
void
read_lprefs_from_file(fp)
FILE *fp;
{
	char lprefs_line[MAXLPLINE+1];
	char *q;
	char *command_name;
	char *command_line;
	char *mapping;
	int i;
	int j;

	/* Create empty list of mappings */
	launcher_mappings = create_null_array_list();

    /* Try to open launcher preferences file.  If failure, use defaults */
    if (fp == NULL) {
		for (i=0; i<ndefault_mappings; i++) {
			for (j=0; j<launcher_mappings->nentries; j++)
				if (!compare_command_names(default_mappings[i],
						launcher_mappings->entries[j]))  {
					delete_from_array_list(&launcher_mappings, j);
					break;
				}
			add_to_array_list(&launcher_mappings, default_mappings[i]);
		}
		quicksort(launcher_mappings->entries, launcher_mappings->nentries,
			compare_command_names);
		return;
	}

	/* Process launcher preferences from file */
	while (fgets(lprefs_line, MAXLPLINE+1, fp) != NULL) {
		if (lprefs_line[strlen(lprefs_line)-1] != '\n')
			continue;
		if ((q=strtok(lprefs_line, " \t")) == NULL)
			continue;
		if (strcmp(q, L_IDENTIFIER))
			continue;
		if ((q=strtok(NULL, ":")) == NULL)
			continue;
		command_name = strip_outer_whitespace(q);
		if (strlen(command_name) == 0) {
			XtFree(command_name);
			continue;
		}
		if ((q = strtok(NULL, "\n")) == NULL)
			continue;
		command_line = strip_outer_whitespace(q);
		if (strlen(command_line) == 0) {
			XtFree(command_line);
			continue;
		}
		mapping = XtMalloc(strlen(command_name)+strlen(command_line)+3);
		strcpy(mapping, command_name);
		strcat(mapping, ": ");
		strcat(mapping, command_line);
		XtFree(command_name);
		XtFree(command_line);
		for (j=0; j<launcher_mappings->nentries; j++)
			if (!compare_command_names(mapping,
					launcher_mappings->entries[j]))  {
				delete_from_array_list(&launcher_mappings, j);
				break;
			}
		add_to_array_list(&launcher_mappings, mapping);
		XtFree(mapping);
	}

	/* Sort array of mappings */
	quicksort(launcher_mappings->entries, launcher_mappings->nentries,
		compare_command_names);
}


/*
 * compare_command_names - Compare two "string1" and "string2" by comparing
 *                         their first colon-delimited substrings.  If an
 *                         input string does not contain a colon then that
 *                         entire string is compareed.  Returns -1 if
 *                         "string1" is less than "string2", 0 if "string1"
 *                         equals "string2", and 1 if  "string1" is greater
 *                         than "string2".
 */
compare_command_names(string1, string2)
char *string1;
char *string2;
{
	char *command_name1;
	char *command_name2;
	char *temp_string1 = XtNewString(string1);
	char *temp_string2 = XtNewString(string2);
	int retval;

	/* Strip off command name from first string */
	if ((command_name1 = strtok(temp_string1, ":\n")) == NULL)
		fatal_error("Trouble in compare_command_names()");
	command_name1 = strip_outer_whitespace(command_name1);
	XtFree(temp_string1);

	/* Strip off command name from second string */
	if ((command_name2 = strtok(temp_string2, ":\n")) == NULL)
		fatal_error("Trouble in compare_command_names()");
	command_name2 = strip_outer_whitespace(command_name2);
	XtFree(temp_string2);

	retval = strcmp(command_name1, command_name2);
	XtFree(command_name1);
	XtFree(command_name2);

	return retval;
}


/*
 * save_lprefs - Write current launcher preferences to .xdirrc using file
 *               pointer "fp".
 */
void
save_lprefs(fp)
FILE *fp;
{
	int i;

	for (i=0; i<launcher_mappings->nentries; i++)
		fprintf(fp, "%s %s\n", L_IDENTIFIER, launcher_mappings->entries[i]);
}


/*
 * display_lprefs - Update the Launcher Preferences dialog with the
 *                  specified command name/command line mappings
 *                  "mappings".
 */
void
display_lprefs(mappings)
struct sl_struct *mappings;
{
	char *temp_buf;
	char *command_name;
	XmString string;
	int i;

	/* Update Command Names list */
	reset_list(lprefs.w_list);
	for (i=0; i<mappings->nentries; i++) {
		temp_buf = XtNewString(mappings->entries[i]);
		command_name = strtok(temp_buf, ":");
		string = XmStringCreateSimple(command_name);
		XtFree(temp_buf);
		XmListAddItem(lprefs.w_list, string, 0);
		XmStringFree(string);
	}
}


/*
 * cb_lprefs_single_selection - This callback is invoked when an item
 *                              in the command names list is selected.
 */
void
cb_lprefs_single_selection(widget, client_data, call_data)
Widget widget;
XtPointer client_data;
XtPointer call_data;
{
    XmListCallbackStruct *cbs = (XmListCallbackStruct *)call_data;
    int nselected_items;
	char *command_line;
	char *ptr;

    /* Don't let user deselect item */
    XtVaGetValues(lprefs.w_list, XmNselectedItemCount, &nselected_items,
		NULL);
    if (nselected_items == 0)
        XmListSelectPos(lprefs.w_list, cbs->item_position, False);

	/* Update text fields */
	command_line = XtNewString(
		temp_launcher_mappings->entries[cbs->item_position-1]);
	if ((ptr = strchr(command_line, ':'))) {
		*ptr = '\0';
		ptr += 2;
		XmTextFieldSetString(lprefs.w_commandLine, ptr);
	} else
		fatal_error("Bug in cb_lprefs_single_selection()");
	XmTextFieldSetString(lprefs.w_commandName, command_line);
	XtFree(command_line);
}


/*
 * get_launch_command_name - Returns the current value of the command
 *                           name field with white space removed.  Use
 *                           XtFree() to release returned string.
 */
char *
get_launch_command_name()
{
	char *command_name;
	char *text;

	text = XmTextFieldGetString(lprefs.w_commandName);
	command_name = strip_outer_whitespace(text);
	XtFree(text);
	return command_name;
}


/*
 * get_launch_command_line - Returns the current value of the command line
 *                           field in the launcher preferences dialog.
 *                           Leading and trailing white space removed.
 *                           Use XtFree() to release returned string.
 */
char *
get_launch_command_line()
{
	char *command_line;
	char *text;

	text = XmTextFieldGetString(lprefs.w_commandLine);
	command_line = strip_outer_whitespace(text);
	XtFree(text);
	return command_line;
}


/*
 * did_user_forget_lprefs - Make sure that user did not intend to press
 *                          "Add" or "Replace" button in the launcher
 *                          preferences dialog.
 */
void
did_user_forget_lprefs()
{
	char *command_name = get_launch_command_name();
	char question[MAXQUESTION];
	int i;
	char *ptr;
	char *command_line;
	char *cmd_line;

	/* No problem if command name is null */
	if (strlen(command_name) == 0) {
		XtFree(command_name);
		return;
	}

	/* If command_name is already in list, did user intend to replace? */
    for (i=0; i<temp_launcher_mappings->nentries; i++)
        if (!compare_command_names(command_name,
				temp_launcher_mappings->entries[i])) {
			command_line = get_launch_command_line();
			if ((ptr = strchr(temp_launcher_mappings->entries[i], ':'))) {
				ptr += 2;
				cmd_line = strip_outer_whitespace(ptr);
			} else
				fatal_error("Bug in did_user_forget_lprefs()");
			if (strcmp(command_line, cmd_line)) {
				strcpy(question, "You edited the command line associated ");
				strcat(question, "with an\nexisting command name, but did ");
				strcat(question, "not press the \"Replace\"\nbutton.\n\n");
				strcat(question, "Do you wish to replace the command line ");
				strcat(question, "associated\nwith \"");
				strcat(question, command_name);
				strcat(question, "\"?");
				if (question_dialog(question, lprefs.w_shell))
					add_to_launch_list();
			}
			XtFree(command_line);
			XtFree(cmd_line);
			XtFree(command_name);
			return;
        }

	/* Extension is not in list, did user intend to add? */
	strcpy(question, "You specified a new command name, but did not add it ");
	strcat(question, "to the list.\n\nDo you wish to add \"");
	strcat(question, command_name);
	strcat(question, "\" to the list?");
	if (question_dialog(question, lprefs.w_shell))
		add_to_launch_list();
	XtFree(command_name);
}


/*
 * strip_outer_whitespace - Strips off leading and trailing white space
 *                          from string.  Returns pointer to reduced
 *                          string.  Use XtFree() to release string.
 *                          A string of zero length will be returned
 *                          if input string is all white space.
 */
char *
strip_outer_whitespace(str)
char *str;
{
	char *temp = XtNewString(str);
	char *q = temp;
	char *new_str;
	int i;

	/* Remove leading white space */
	while (*q && isspace(*q))
		q++;

	/* Remove trailing white space */
	for (i=strlen(q)-1; i>=0; i--)
		if (isspace(q[i]))
			q[i] = '\0';
		else
			break;

	/* Return reduced string */
	new_str = XtNewString(q);
	XtFree(temp);
	return new_str;
}


/*
 * cb_command_modified - Callback that is invoked when the value of the
 *                       launch preferences dialog's command name or
 *                       command line has changed.  The purpose of this
 *                       callback is to make the appropriate edit buttons
 *                       selectable.
 */
void
cb_command_modified(widget, client_data, call_data)
Widget widget;
XtPointer client_data;
XtPointer call_data;
{
	int i;
	int len;
	char *command_name;
	char *command_line;

	/* Can't add or replace if command line is null */
	command_line = get_launch_command_line();
	len = strlen(command_line);
	XtFree(command_line);
	if (len == 0) {
		XtSetSensitive(lprefs.w_addButton, False);
		XtSetSensitive(lprefs.w_replaceButton, False);
		return;
	}

	/* Determine whether add or replace is allowed */
	command_name = get_launch_command_name();
	if (strlen(command_name)) {
	    for (i=0; i<temp_launcher_mappings->nentries; i++)
   	    	if (!compare_command_names(command_name,
					temp_launcher_mappings->entries[i]))  {
   	         	break;
   	    	}
		if (i == temp_launcher_mappings->nentries) {
			XtSetSensitive(lprefs.w_addButton, True);
			XtSetSensitive(lprefs.w_replaceButton, False);
		} else {
			XtSetSensitive(lprefs.w_addButton, False);
			XtSetSensitive(lprefs.w_replaceButton, True);
		}
	} else {
		XtSetSensitive(lprefs.w_addButton, False);
		XtSetSensitive(lprefs.w_replaceButton, False);
	}

	XtFree(command_name);
}


/*
 * iconify_lprefs_window - Iconify launch preferences window.
 */
void
iconify_lprefs_window()
{
	if (lprefs_visible)
		XIconifyWindow(display, XtWindow(lprefs.w_shell), screen);
}


/*
 * deiconify_lprefs_window - Deiconify launch preferences window.
 */
void
deiconify_lprefs_window()
{
	if (lprefs_visible)
		XMapWindow(display, XtWindow(lprefs.w_shell));
}



syntax highlighted by Code2HTML, v. 0.9.1