/***************************************************************************/
/***************************************************************************/
/*                                                                         */
/*   (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 <Xm/SelectioB.h>
#include <Xm/TextF.h>
#include <Xm/Form.h>
#include <Xm/Label.h>
#include <Xm/Frame.h>
#include <Xm/ToggleBG.h>
#include <Xm/RowColumn.h>
#include <Xm/Text.h>
#include "xdir.h"
#include "view.h"
#include "str.h"

static struct {
    Widget w_dialog;
    Widget w_form;
    Widget w_patternLabel;
    Widget w_pattern;
	Widget w_matchCaseLabel;
    Widget w_matchRadioBoxFrame;
    Widget w_matchRadioBox;
    Widget w_matchCaseYesToggle;
	Widget w_matchCaseNoToggle;
	Widget w_directionLabel;
	Widget w_directionRadioBoxFrame;
	Widget w_directionRadioBox;
	Widget w_searchForwardToggle;
	Widget w_searchBackwardToggle;
} find;

static struct find_params_st prev_dialog_values;

static char *find_help[] = {
	"This dialog allows you to search the displayed text for a simple",
	"text string.\n",
	"\n",
	"The \"Case Sensitivity\" toggles control whether case (upper or",
	"lower) should be considered when looking for a match.\n",
	"\n",
	"The \"Search Direction\" toggles control the direction of the",
	"search.  Regardless of the direction, the search will automatically",
	"wrap past the end (or beginning) of the text until a match is",
	"found, or until the entire text has been searched.\n",
	"\n",
	"Use general preferences INITIAL SEARCH DEPTH and INITIAL SEARCH",
	"CASE MATCH to change the dialog's default values.",
	NULL
};

static int saved_prev_dialog_values = False;

extern int initial_match_case;
extern int initial_search_direction;

void cb_find_cancel();
void cb_find_ok();
void cb_find_help();
void cb_map_dialog();
void create_find_dialog();
void save_find_dialog_values();
void init_find_dialog_values();
void find_pattern();


/*
 * cb_internal_viewer_find - Callback that pops up Find dialog for
 *                           internal viewer.
 */
void
cb_internal_viewer_find(widget, client_data, call_data)
Widget widget;
XtPointer client_data;
XtPointer call_data;
{
    struct view_dialog *view = (struct view_dialog*)client_data;
	char *pattern;

    /* Clear error flag */
    raise_okflag();

    /* Create "Find" dialog */
    create_find_dialog(view);

	/* Initialize settings in find dialog */
	init_find_dialog_values();

    /* Pop up dialog */
    XtVaSetValues(find.w_dialog, XmNuserData, (XtPointer)view, NULL);
    XtManageChild(find.w_dialog);
    traverse_to_widget(find.w_pattern);

	/* If pattern is already entered, select it */
	pattern = XmTextFieldGetString(find.w_pattern);
	XmTextFieldSetSelection(find.w_pattern, (XmTextPosition)0,
		(XmTextPosition)strlen(pattern), CurrentTime);
	XtFree(pattern);
}


/*
 * create_find_dialog - Create dialog for finding text pattern in an
 *                      internal viewer (represented by "view").
 */
void
create_find_dialog(view)
struct view_dialog *view;
{
    int i;
    Arg args[3];
    Widget widget;

    /* Create prompt dialog for searching */
    i = 0;
    XtSetArg(args[i], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); i++;
    XtSetArg(args[i], XmNdefaultPosition, False); i++;
	XtSetArg(args[i], XmNautoUnmanage, False); i++;
    find.w_dialog = XmCreatePromptDialog(view->w_shell, "find", args, i);
    XtAddCallback(find.w_dialog, XmNokCallback, cb_find_ok, (XtPointer)view);
	XtAddCallback(find.w_dialog, XmNcancelCallback, cb_find_cancel,
		(XtPointer)NULL);
    XtAddCallback(find.w_dialog, XmNhelpCallback, cb_find_help,
		(XtPointer)NULL);
    XtAddCallback(find.w_dialog, XmNmapCallback, cb_map_dialog, 
		(XtPointer)XtWindow(view->w_shell));

    /* Don't show prompt dialog's selection label and text */
    widget = XmSelectionBoxGetChild(find.w_dialog, XmDIALOG_SELECTION_LABEL);
    XtUnmanageChild(widget);
    widget = XmSelectionBoxGetChild(find.w_dialog, XmDIALOG_TEXT);
    XtUnmanageChild(widget);

	/* Add callback for the WM_DELETE_WINDOW protocol */
	add_wm_delete_window_cb(find.w_dialog, cb_find_cancel, NULL, False);

    /* Create form for control area */
    find.w_form = XtVaCreateWidget(
        "form",
        xmFormWidgetClass,
        find.w_dialog,
        NULL
    );

    /* Create label for search expression */
    find.w_patternLabel = XtVaCreateManagedWidget(
        "patternLabel",
        xmLabelWidgetClass,
        find.w_form,
        XmNalignment,       XmALIGNMENT_BEGINNING,
        XmNtopAttachment,   XmATTACH_FORM,
        XmNleftAttachment,  XmATTACH_FORM,
        XmNrightAttachment, XmATTACH_FORM,
        NULL
    );

    /* Create textfield for search expression */
    find.w_pattern = XtVaCreateManagedWidget(
        "text",
        xmTextFieldWidgetClass,
        find.w_form,
        XmNtopAttachment,   XmATTACH_WIDGET,
        XmNtopWidget,       find.w_patternLabel,
        XmNleftAttachment,  XmATTACH_FORM,
        XmNrightAttachment, XmATTACH_FORM,
        NULL
    );

    /* Create label for match case */
    find.w_matchCaseLabel = XtVaCreateManagedWidget(
        "matchCaseLabel",
        xmLabelWidgetClass,
        find.w_form,
        XmNtopAttachment,   XmATTACH_WIDGET,
		XmNtopWidget,		find.w_pattern,
		XmNtopOffset,		15,
		XmNleftAttachment,	XmATTACH_FORM,
        XmNrightAttachment, XmATTACH_POSITION,
		XmNrightPosition,	48,
        NULL
    );

    /* Create radio box frame for match case */
    find.w_matchRadioBoxFrame = XtVaCreateManagedWidget(
        "matchRadioBoxFrame",
        xmFrameWidgetClass,
        find.w_form,
        XmNtopAttachment,       XmATTACH_WIDGET,
        XmNtopWidget,           find.w_matchCaseLabel,
		XmNbottomAttachment,	XmATTACH_FORM,
        XmNleftAttachment,      XmATTACH_OPPOSITE_WIDGET,
		XmNleftWidget,			find.w_matchCaseLabel,
        XmNrightAttachment,     XmATTACH_OPPOSITE_WIDGET,
		XmNrightWidget,			find.w_matchCaseLabel,
        NULL
    );

    /* Create radio box for match criteria */
	i = 0;
	XtSetArg(args[i], XmNspacing, 0); i++;
    find.w_matchRadioBox = XmCreateRadioBox(find.w_matchRadioBoxFrame,
		"matchRadioBox", args, i);

    /* Create match case "Yes" toggle */
    find.w_matchCaseYesToggle = XtVaCreateManagedWidget(
        "matchCaseYesToggle",
        xmToggleButtonGadgetClass,
        find.w_matchRadioBox,
        XmNmarginHeight,    0,
		XmNindicatorType,	XmONE_OF_MANY,
		NULL
    );

    /* Create match case "No" toggle */
    find.w_matchCaseNoToggle = XtVaCreateManagedWidget(
        "matchCaseNoToggle",
        xmToggleButtonGadgetClass,
        find.w_matchRadioBox,
        XmNmarginHeight,    0,
		XmNindicatorType,	XmONE_OF_MANY,
		NULL
    );

    XtManageChild(find.w_matchRadioBox);

    /* Create label for search direction */
    find.w_directionLabel = XtVaCreateManagedWidget(
        "directionLabel",
        xmLabelWidgetClass,
        find.w_form,
        XmNtopAttachment,   XmATTACH_WIDGET,
		XmNtopWidget,		find.w_pattern,
		XmNtopOffset,		15,
        XmNleftAttachment,  XmATTACH_POSITION,
		XmNleftPosition,	52,
		XmNrightAttachment,	XmATTACH_FORM,
        NULL
    );

    /* Create radio box frame for search direction */
    find.w_directionRadioBoxFrame = XtVaCreateManagedWidget(
        "directionRadioBoxFrame",
        xmFrameWidgetClass,
        find.w_form,
        XmNtopAttachment,       XmATTACH_WIDGET,
        XmNtopWidget,           find.w_directionLabel,
		XmNbottomOffset,		XmATTACH_FORM,
        XmNleftAttachment,      XmATTACH_OPPOSITE_WIDGET,
		XmNleftWidget,			find.w_directionLabel,
        XmNrightAttachment,     XmATTACH_OPPOSITE_WIDGET,
		XmNrightWidget,			find.w_directionLabel,
        NULL
    );

    /* Create radio box for search direction */
	i = 0;
	XtSetArg(args[i], XmNspacing, 0); i++;
    find.w_directionRadioBox = XmCreateRadioBox(find.w_directionRadioBoxFrame,
		"directionRadioBoxFrame", args, i);

    /* Create "Search Forward" toggle */
    find.w_searchForwardToggle = XtVaCreateManagedWidget(
        "searchForwardToggle",
        xmToggleButtonGadgetClass,
        find.w_directionRadioBox,
		XmNindicatorType,	XmONE_OF_MANY,
        XmNmarginHeight,    0,
        NULL
    );

    /* Create "Search Backward" toggle */
    find.w_searchBackwardToggle = XtVaCreateManagedWidget(
        "searchBackwardToggle",
        xmToggleButtonGadgetClass,
        find.w_directionRadioBox,
		XmNindicatorType,	XmONE_OF_MANY,
        XmNmarginHeight,    0,
        NULL
    );

    XtManageChild(find.w_directionRadioBox);

    XtManageChild(find.w_form);
}


/*
 * cb_find_ok - This callback initializes the search.
 */
void
cb_find_ok(widget, client_data, call_data)
Widget widget;
XtPointer client_data;
XtPointer call_data;
{
	struct view_dialog *view = (struct view_dialog *)client_data;
	Widget w;

	/* "Find Again" menu item no longer needs to be greyed out */
	XtSetSensitive(view->w_findAgainItem, True);

	/* Get pattern to search for */
	XtFree(view->find.pattern);
	view->find.pattern =  XmTextFieldGetString(find.w_pattern);
	if (!strlen(view->find.pattern)) {
		warn("No search pattern given.", XtParent(find.w_dialog));
		w = XmSelectionBoxGetChild(find.w_dialog, XmDIALOG_OK_BUTTON);
		XtUnmanageChild(w);
		XtManageChild(w);
		traverse_to_widget(find.w_pattern);
		return;
	}

	/* Match case? */
	view->find.match_case =
		XmToggleButtonGadgetGetState(find.w_matchCaseYesToggle);

	/* Get search direction */
	if (XmToggleButtonGadgetGetState(find.w_searchForwardToggle))
		view->find.direction = FORWARD;
	else
		view->find.direction = BACKWARD;

	/* This might take some time */
	use_busy_cursor();

	/* Get rid of Find dialog */
	save_find_dialog_values();
	XtUnmanageChild(find.w_dialog);
	XtDestroyWidget(XtParent(find.w_dialog));

	/* Perform search */
	find_pattern(view);

	/* All done */
	restore_prev_cursor();
}


void
cb_internal_viewer_find_again(widget, client_data, call_data)
Widget widget;
XtPointer client_data;
XtPointer call_data;
{
	struct view_dialog *view = (struct view_dialog *)client_data;

	/* Clear error flag */
	raise_okflag();

	/* This might take some time */
	use_busy_cursor();

	/* Perform search */
	find_pattern(view);

	/* All done */
	restore_prev_cursor();
}


/*
 * cb_find_help - Callback to display help info for Find dialog.
 */
void
cb_find_help(widget, client_data, call_data)
Widget widget;
XtPointer client_data;
XtPointer call_data;
{
    help_dialog(widget, True, "Find", find_help);
}


/*
 * cb_find_cancel - Callback to handle Find Dialog "Cancel" button.
 */
void
cb_find_cancel(widget, client_data, call_data)
Widget widget;
XtPointer client_data;
XtPointer call_data;
{
	/* Get rid of Find Dialog */
	save_find_dialog_values();
	XtDestroyWidget(XtParent(find.w_dialog));
}


/*
 * save_find_dialog_values - Remember current find dialog settings
 *                           for next time find dialog is popped up.
 */
void
save_find_dialog_values()
{
	if (saved_prev_dialog_values)
		XtFree(prev_dialog_values.pattern);
	prev_dialog_values.pattern = XmTextFieldGetString(find.w_pattern);
	prev_dialog_values.match_case =
		XmToggleButtonGadgetGetState(find.w_matchCaseYesToggle);
	if (XmToggleButtonGadgetGetState(find.w_searchForwardToggle))
		prev_dialog_values.direction = FORWARD;
	else
		prev_dialog_values.direction = BACKWARD;
	saved_prev_dialog_values = True;
}


/*
 * init_find_dialog_values - Initialize the find dialog with values 
 *                           saved from the last time the dialog was
 *                           popped up.  If this is the first time the
 *                           dialog is popped up, use values from the
 *                           user preferences to initialize the settings.
 */
void
init_find_dialog_values()
{
	XmToggleButtonGadgetSetState(find.w_searchForwardToggle, False, True);
	XmToggleButtonGadgetSetState(find.w_searchBackwardToggle, False, True);
	XmToggleButtonGadgetSetState(find.w_matchCaseYesToggle, False,True);
	XmToggleButtonGadgetSetState(find.w_matchCaseNoToggle, False, True);

	if (saved_prev_dialog_values) {
		XmTextFieldSetString(find.w_pattern, prev_dialog_values.pattern);
		if (prev_dialog_values.match_case)
			XmToggleButtonGadgetSetState(find.w_matchCaseYesToggle, True, True);
		else
			XmToggleButtonGadgetSetState(find.w_matchCaseNoToggle, True, True);
		if (prev_dialog_values.direction == FORWARD)
			XmToggleButtonGadgetSetState(find.w_searchForwardToggle, True,
				True);
		else
			XmToggleButtonGadgetSetState(find.w_searchBackwardToggle, True,
				True);
	} else {
		XmTextFieldSetString(find.w_pattern, "");
		if (initial_match_case)
			XmToggleButtonGadgetSetState(find.w_matchCaseYesToggle, True, True);
		else
			XmToggleButtonGadgetSetState(find.w_matchCaseNoToggle, True, True);
		if (initial_search_direction == FORWARD)
			XmToggleButtonGadgetSetState(find.w_searchForwardToggle, True,
				True);
		else
			XmToggleButtonGadgetSetState(find.w_searchBackwardToggle, True,
				True);
	}
}


/*
 * find_pattern - Find pattern in internal viewer specified by "view".
 *                view->find contains the search parameters.
 */
void
find_pattern(view)
struct view_dialog *view;
{
	long start_pos = (long)XmTextGetInsertionPosition(view->w_text);
	long ntext = (long)XmTextGetLastPosition(view->w_text);
	long limit;
	long i;
	int j;
	int npattern = strlen(view->find.pattern);
	char *p;

	/* Make sure that searchable text is available */
	if (view->text == NULL) {
		view->text = XmTextGetString(view->w_text);
		view->text_converted_to_lc = False;
	}

	/* Adjust the case of the pattern and text */
	if (view->find.match_case) {
		if (view->text_converted_to_lc) {
			XtFree(view->text);
			view->text = XmTextGetString(view->w_text);
			view->text_converted_to_lc = False;
		}
	} else {
		p = view->find.pattern;
		while (*p) {
			*p = tolower(*p);
			p++;
		}
		if (!view->text_converted_to_lc) {
			p = view->text;
			while (*p) {
				*p = tolower(*p);
				p++;
			}
			view->text_converted_to_lc = True;
		}
	}

	/* Search text */
	if (view->find.direction == FORWARD) {
		limit = ntext-npattern;
		for (i=start_pos; i<=limit; i++)
			for (j=0; j<npattern; j++)
				if (view->find.pattern[j] == view->text[i+j]) {
					if (j == npattern-1)
						goto found;
				} else
					break;
		limit = MIN(start_pos-1, ntext-npattern);
		for (i=0; i<=limit; i++)
			for (j=0; j<npattern; j++)
				if (view->find.pattern[j] == view->text[i+j]) {
					if (j == npattern-1)
						goto found;
				} else
					break;
	} else {
		limit = MIN(start_pos-1, ntext-npattern);
		for (i=limit; i>=0; i--)
			for (j=0; j<npattern; j++)
				if (view->find.pattern[j] == view->text[i+j]) {
					if (j == npattern-1)
						goto found;
				} else
					break;
		for (i=ntext-npattern; i>=start_pos; i--)
			for (j=0; j<npattern; j++)
				if (view->find.pattern[j] == view->text[i+j]) {
					if (j == npattern-1)
						goto found;
				} else
					break;
	}
	info_dialog("Match not found.", view->w_shell);
	return;

found:

	XmTextSetSelection(view->w_text, (XmTextPosition)i, 
		(XmTextPosition)(i+npattern), CurrentTime);
	XmTextShowPosition(view->w_text, (XmTextPosition)i);
	if (view->find.direction == BACKWARD)
		XmTextSetInsertionPosition(view->w_text, (XmTextPosition)i);
}



syntax highlighted by Code2HTML, v. 0.9.1