/* TextEditor.c
 *
 * Copyright (C) 1997-2002 Paul Boersma
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 * pb 2001/08/02
 * pb 2002/03/07 GPL
 */

#include "TextEditor.h"
#include "machine.h"
#include "longchar.h"
#include "EditorM.h"

/***** TextEditor methods *****/

static void destroy (I) {
	iam (TextEditor);
	forget (my openDialog);
	forget (my saveDialog);
	forget (my printDialog);
	forget (my findDialog);
	inherited (TextEditor) destroy (me);
}

static void nameChanged (I) {
	iam (TextEditor);
	if (my name == NULL)
		sprintf (Melder_buffer1, "(untitled%s)", my dirty ? ", modified" : "");
	else
		sprintf (Melder_buffer1, "File \\\"l%s\\\"r%s", MelderFile_messageName (& my file), my dirty ? " (modified)" : "");
	Longchar_nativize (Melder_buffer1, Melder_buffer2, TRUE);
	XtVaSetValues (my shell, XmNtitle, Melder_buffer2, NULL);
	if (my name == NULL)
		sprintf (Melder_buffer1, "%s(untitled)", my dirty ? "*" : "");
	else
		sprintf (Melder_buffer1, "%s%s", my dirty ? "*" : "", MelderFile_name (& my file));
	Longchar_nativize (Melder_buffer1, Melder_buffer2, TRUE);
	XtVaSetValues (my shell, XmNiconName, Melder_buffer2, NULL);
}

static int openDocument (TextEditor me, MelderFile file) {
	char *text = MelderFile_readText (file);
	if (! text) return 0;
	XmTextSetString (my textWidget, text);
	Melder_free (text);
	/*
	 * XmTextSetString has invoked the XmNvalueChangedCallback,
	 * which has set 'my dirty' to TRUE. Fix this.
	 */
	my dirty = FALSE;
	MelderFile_copy (file, & my file);
	Thing_setName (me, Melder_fileToPath (file));
	return 1;
}

static void newDocument (TextEditor me) {
	XmTextSetString (my textWidget, "");   /* Implicitly sets my dirty to TRUE. */
	my dirty = FALSE;
	Thing_setName (me, NULL);
}

static int saveDocument (TextEditor me, MelderFile file) {
	char *text = XmTextGetString (my textWidget);
	if (! MelderFile_writeText (file, text)) { XtFree (text); return 0; }
	XtFree (text);
	my dirty = FALSE;
	MelderFile_copy (file, & my file);
	Thing_setName (me, Melder_fileToPath (file));
	return 1;
}

static void closeDocument (TextEditor me) {
	forget (me);
}

static int cb_open_ok (Any sender, I) {
	iam (TextEditor);
	MelderFile file = UiFile_getFile (sender);
	if (! openDocument (me, file)) return 0;
	return 1;
}

static void cb_showOpen (EditorCommand cmd, Any sender) {
	TextEditor me = (TextEditor) cmd -> editor;
	(void) sender;
	if (! my openDialog)
		my openDialog = UiInfile_create (my dialog, "Open", cb_open_ok, me, 0);
	UiInfile_do (my openDialog);
}

static int cb_saveAs_ok (Any sender, I) {
	iam (TextEditor);
	MelderFile file = UiFile_getFile (sender);
	if (! saveDocument (me, file)) return 0;
	return 1;
}

DIRECT (TextEditor, cb_saveAs)
	char defaultName [300];
	if (! my saveDialog)
		my saveDialog = UiOutfile_create (my dialog, "Save", cb_saveAs_ok, me, 0);
	sprintf (defaultName, my name ? MelderFile_name (& my file) : "");
	UiOutfile_do (my saveDialog, defaultName);
END

MOTIF_CALLBACK (cb_saveAndOpen)
	EditorCommand cmd = (EditorCommand) void_me;
	TextEditor me = (TextEditor) cmd -> editor;
	if (my name) {
		if (! saveDocument (me, & my file)) { Melder_flushError (NULL); return; }
		cb_showOpen (cmd, NULL);
	} else {
		cb_saveAs (cmd, NULL);
	}
MOTIF_CALLBACK_END

MOTIF_CALLBACK (cb_discardAndOpen)
	EditorCommand cmd = (EditorCommand) void_me;
	TextEditor me = (TextEditor) cmd -> editor;
	XtUnmanageChild (my dirtyOpenDialog);
	cb_showOpen (cmd, NULL);
MOTIF_CALLBACK_END

DIRECT (TextEditor, cb_open)
	if (my dirty) {
		if (! my dirtyOpenDialog) {
			my dirtyOpenDialog = XmCreateMessageDialog (my shell, "dirtyOpenDialog", NULL, 0);
			XtVaSetValues (my dirtyOpenDialog, XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL,
				XmNautoUnmanage, True, XmNdialogType, XmDIALOG_QUESTION,
				motif_argXmString (XmNdialogTitle, "Text changed"),
				motif_argXmString (XmNmessageString, "Save changes?"),
				motif_argXmString (XmNokLabelString, "Save & Open"),
				motif_argXmString (XmNhelpLabelString, "Discard & Open"),
				NULL);
			XtAddCallback (my dirtyOpenDialog, XmNokCallback, cb_saveAndOpen, cmd);
			XtAddCallback (my dirtyOpenDialog, XmNhelpCallback, cb_discardAndOpen, cmd);
		}
		XtManageChild (my dirtyOpenDialog);
	} else {
		cb_showOpen (cmd, sender);
	}
END

MOTIF_CALLBACK (cb_saveAndNew)
	EditorCommand cmd = (EditorCommand) void_me;
	TextEditor me = (TextEditor) cmd -> editor;
	if (my name) {
		if (! saveDocument (me, & my file)) { Melder_flushError (NULL); return; }
		newDocument (me);
	} else {
		cb_saveAs (cmd, NULL);
	}
MOTIF_CALLBACK_END

MOTIF_CALLBACK (cb_discardAndNew)
	EditorCommand cmd = (EditorCommand) void_me;
	TextEditor me = (TextEditor) cmd -> editor;
	XtUnmanageChild (my dirtyNewDialog);
	newDocument (me);
MOTIF_CALLBACK_END

DIRECT (TextEditor, cb_new)
	if (my dirty) {
		if (! my dirtyNewDialog) {
			my dirtyNewDialog = XmCreateMessageDialog (my shell, "dirtyNewDialog", NULL, 0);
			XtVaSetValues (my dirtyNewDialog, XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL,
				XmNautoUnmanage, True, XmNdialogType, XmDIALOG_QUESTION,
				motif_argXmString (XmNdialogTitle, "Text changed"),
				motif_argXmString (XmNmessageString, "Save changes?"),
				motif_argXmString (XmNokLabelString, "Save & New"),
				motif_argXmString (XmNhelpLabelString, "Discard & New"),
				NULL);
			XtAddCallback (my dirtyNewDialog, XmNokCallback, cb_saveAndNew, cmd);
			XtAddCallback (my dirtyNewDialog, XmNhelpCallback, cb_discardAndNew, cmd);
		}
		XtManageChild (my dirtyNewDialog);
	} else {
		newDocument (me);
	}
END

DIRECT (TextEditor, cb_save)
	if (my name) {
		if (! saveDocument (me, & my file)) return 0;
	} else {
		cb_saveAs (cmd, NULL);
	}
END

MOTIF_CALLBACK (cb_saveAndClose)
	iam (TextEditor);
	if (my name) {
		if (! saveDocument (me, & my file)) { Melder_flushError (NULL); return; }
		closeDocument (me);
	} else {
		cb_saveAs (Editor_getMenuCommand (me, "File", "Save as..."), NULL);
	}
MOTIF_CALLBACK_END

MOTIF_CALLBACK (cb_discardAndClose)
	iam (TextEditor);
	XtUnmanageChild (my dirtyCloseDialog);
	closeDocument (me);
MOTIF_CALLBACK_END

static void goAway (I) {
	iam (TextEditor);
	if (my dirty) {
		if (! my dirtyCloseDialog) {
			my dirtyCloseDialog = XmCreateMessageDialog (my shell, "dirtyCloseDialog", NULL, 0);
			XtVaSetValues (my dirtyCloseDialog, XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL,
				XmNautoUnmanage, True, XmNdialogType, XmDIALOG_QUESTION,
				motif_argXmString (XmNdialogTitle, "Text changed"),
				motif_argXmString (XmNmessageString, "Save changes?"),
				motif_argXmString (XmNokLabelString, "Save & Close"),
				motif_argXmString (XmNhelpLabelString, "Discard & Close"),
				NULL);
			XtAddCallback (my dirtyCloseDialog, XmNokCallback, cb_saveAndClose, me);
			XtAddCallback (my dirtyCloseDialog, XmNhelpCallback, cb_discardAndClose, me);
		}
		XtManageChild (my dirtyCloseDialog);
	} else {
		closeDocument (me);
	}
}

DIRECT (TextEditor, cb_cut)
	#if defined (UNIX)
		XmTextCut (my textWidget, 0/*((XmAnyCallbackStruct *) call) -> event -> xbutton. time*/);
	#else
		XmTextCut (my textWidget, 0);
	#endif
END

DIRECT (TextEditor, cb_copy)
	#if defined (UNIX)
		XmTextCopy (my textWidget, 0/*((XmAnyCallbackStruct *) call) -> event -> xbutton. time*/);
	#else
		XmTextCopy (my textWidget, 0);
	#endif
END

DIRECT (TextEditor, cb_paste)
	XmTextPaste (my textWidget);
END

DIRECT (TextEditor, cb_erase)
	XmTextRemove (my textWidget);
END

static void createMenus (I) {
	iam (TextEditor);
	inherited (TextEditor) createMenus (me);
	Editor_addCommand (me, "File", "New", 'N', cb_new);
	Editor_addCommand (me, "File", "Open...", 'O', cb_open);
	Editor_addCommand (me, "File", "-- save --", 0, NULL);
	Editor_addCommand (me, "File", "Save", 'S', cb_save);
	Editor_addCommand (me, "File", "Save as...", 0, cb_saveAs);
	Editor_addCommand (me, "File", "-- close --", 0, NULL);
	Editor_addCommand (me, "Edit", "-- cut copy paste --", 0, NULL);
	Editor_addCommand (me, "Edit", "Cut", 'X', cb_cut);
	Editor_addCommand (me, "Edit", "Copy", 'C', cb_copy);
	Editor_addCommand (me, "Edit", "Paste", 'V', cb_paste);
	Editor_addCommand (me, "Edit", "Erase", 0, cb_erase);
}

MOTIF_CALLBACK (cb_valueChanged)
	iam (TextEditor);
	if (! my dirty) {
		my dirty = TRUE;
		our nameChanged (me);
	}
MOTIF_CALLBACK_END

static void createChildren (I) {
	iam (TextEditor);
	Arg arg [4];
	arg [0]. name = XmNscrollingPolicy; arg [0]. value = XmAUTOMATIC;
	arg [1]. name = XmNscrollBarDisplayPolicy; arg [1]. value = XmAS_NEEDED;
	arg [2]. name = XmNeditable; arg [2]. value = True;
	arg [3]. name = XmNeditMode; arg [3]. value = XmMULTI_LINE_EDIT;   /* On Linux, this must go before creation. */
	my textWidget = XmCreateScrolledText (my dialog, "text", arg, 4);
	XtAddCallback (my textWidget, XmNvalueChangedCallback, cb_valueChanged, me);
	XtVaSetValues (XtParent (my textWidget),
		XmNleftAttachment, XmATTACH_FORM,
		XmNrightAttachment, XmATTACH_FORM,
		XmNtopAttachment, XmATTACH_FORM, XmNtopOffset, Machine_getMenuBarHeight (),
		XmNbottomAttachment, XmATTACH_FORM,
		0);
	XtVaSetValues (my textWidget, XmNeditable, True, XmNeditMode, XmMULTI_LINE_EDIT,
	#ifndef macintosh
		XmNrows, 33, XmNcolumns, 90,
	#endif
		NULL);
	XtManageChild (my textWidget);
}

class_methods (TextEditor, Editor)
	class_method (destroy)
	class_method (nameChanged)
	class_method (goAway)
	class_method (createChildren)
	class_method (createMenus)
class_methods_end

int TextEditor_init (I, Widget parent, const char *initialText) {
	iam (TextEditor);
	if (! Editor_init (me, parent, 0, 0, 600, 400, NULL, NULL)) return 0;
	if (initialText) {
		XmTextSetString (my textWidget, MOTIF_CONST_CHAR_ARG (initialText));
		my dirty = FALSE;   /* Was set to TRUE in valueChanged callback. */
		Thing_setName (me, NULL);
	}
	return 1;
}

TextEditor TextEditor_create (Widget parent, const char *initialText) {
	TextEditor me = new (TextEditor);
	if (! me || ! TextEditor_init (me, parent, initialText)) { forget (me); return NULL; }
	return me;
}

void TextEditor_showOpen (I) {
	iam (TextEditor);
	cb_showOpen (Editor_getMenuCommand (me, "File", "Open..."), NULL);
}

/* End of file TextEditor.c */
